<template>
  <div
    ref="tablePro"
    :key="key"
    v-loading="hasLoading && loading"
    class="y-table-pro"
  >
    <!-- Element风格搜索框 -->
    <div
      v-if="uiStyle === 'element'"
      ref="tableFilter"
      :class="
        formConfig.length > 3 ? 'y-form-wrapper' : 'y-form-inline-wrapper'
      "
    >
      <y-form
        :key="key"
        ref="form"
        v-model="queryParams"
        v-bind="$attrs"
        :config="config"
        :inline="true"
        :label-position="$attrs['label-position'] || 'left'"
        :label-width="$attrs['label-width']"
        v-on="$listeners"
        @keyup.enter.native="handleQuery"
      >
        <el-form-item v-if="hasSearch && formConfig.length <= 3">
          <el-button type="primary" @click="handleQuery">{{ searchButtonText }}</el-button>
          <slot name="botton"></slot>
        </el-form-item>
      </y-form>
      <div v-if="hasSearch && formConfig.length > 3">
        <el-button type="primary" @click="handleQuery">{{ searchButtonText }}</el-button>
        <slot name="botton"></slot>
      </div>
    </div>
    <!-- antd风格搜索框 -->
    <div
      v-else-if="uiStyle === 'antd'"
      ref="tableFilter"
      class="y-form-inline-wrapper antd-form-wrapper"
    >
      <y-form
        :key="key"
        ref="form"
        v-model="queryParams"
        v-bind="$attrs"
        :config="config"
        :inline="true"
        :label-position="$attrs['label-position'] || 'left'"
        :label-suffix="$attrs['label-suffix'] || $attrs['labelSuffix'] || '：'"
        :label-width="$attrs['label-width']"
        v-on="$listeners"
        @keyup.enter.native="handleQuery"
      >
      </y-form>
      <div v-if="hasReset || hasSearch" class="btn-wrapper">
        <el-button
          v-if="hasSearch"
          @click="handleQuery"
        >查 询</el-button>
        <el-button v-if="hasReset" type="text" @click="handleReset">
          <y-icon icon="icon-zhongzhi" color="#787979"></y-icon>
          <span class="text-gray y-m-l-6">重置</span>
        </el-button>
        <el-button
          v-if="canShowExpandBtn"
          type="text"
          @click="handleToggle"
        >{{ isExpand ? '收起' : '展开'
        }}<i
          :class="[isExpand ? 'el-icon-arrow-up' : 'el-icon-arrow-down']"
        ></i>
        </el-button>
      </div>
    </div>
    <!-- 分割线 -->
    <div
      v-if="uiStyle === 'antd'"
      class="divider"
      :style="{
        background: uiStyle === 'antd' ? bgColor : '',
        overflow: 'auto',
        height: '16px',
        margin: '0 16px'
      }"
    ></div>
    <!-- 表格 -->
    <div class="table-wrapper">
      <y-table
        :key="key"
        ref="table"
        :data="tableData"
        :columns.sync="currentColumns"
        :pagination="$attrs.pagination === undefined ? true : $attrs.pagination"
        :total="total"
        :page-size="queryParams.size"
        :reload="reloadData"
        v-bind="$attrs"
        :max-height="height"
        :offset-height="offsetHeight"
        :row-key="rowKey"
        :is-vxe="isVxe"
        :is-checked-all="isCheckedAll"
        @update="handleUpdateColumns"
        @selection-change="handleSelectChange"
        v-on="$listeners"
      >
        <template>
          <!-- table左侧 -->
          <slot name="table"></slot>
          <el-button v-if="showExportButton" :loading="exportLoading" @click="handleExportTable">{{ exportButtonText }}</el-button>
        </template>
        <template slot="table-top-right">
          <!-- table右侧 -->
          <slot name="table-top-right"></slot>
        </template>
        <template slot="list-top">
          <!-- 列表顶部 -->
          <slot name="list-top"></slot>
        </template>
      </y-table>
    </div>
    <!-- 批量操作区域 -->
    <div
      v-if="hasBatchAction"
      class="y-table-batch-action-area"
      :style="`left: ${offset}px`"
    >
      <div class="action-left">
        <div v-if="isCheckedAll">已选择全部<span v-if="!filterFn"><span :style="{color: primaryColor}"> {{ total }}</span> 项</span></div>
        <div v-else>已选择当前页<span :style="{color: primaryColor}"> {{ selection.length }}</span> 项</div>
        <el-button v-if="showSelectAll && !isCheckedAll" type="text" @click="handleSelectAll">选择全部 {{ `${showSelectAllCount?total+'项':''}` }}</el-button>
      </div>
      <div class="action-right">
        <el-button @click="handleCancelSelection">取消选择</el-button>
        <slot name="action-right" :selection="selection"></slot>
      </div>
    </div>
  </div>
</template>

<script>
import color from '../../../styles/element-variables.scss'
import { filter, cloneDeep, isEmpty, get, trim, debounce } from 'lodash'
import { findComponentForward } from '../../utils'
export default {
  name: 'YTablePro',
  props: {
    // 请求接口的api
    loadDataApi: {
      type: Function,
      required: true
    },
    // 表格列
    columns: {
      type: Array,
      default: () => {
        return []
      }
    },
    // 是否显示查询按钮
    hasSearch: {
      type: Boolean,
      default: true
    },
    searchButtonText: {
      type: String,
      default: '查询'
    },
    // 是否显示重置按钮
    hasReset: {
      type: Boolean,
      default: true
    },
    // 传递过来的查询参数
    params: {
      type: Object,
      default: () => { }
    },
    /* UI风格 */
    uiStyle: {
      type: String,
      default: 'element'
    },
    offset: {
      type: [String, Number],
      default: 200
    },
    rowKey: {
      type: [String, Function],
      default: 'id'
    },
    lazyLoad: {
      type: Boolean,
      default: false
    },
    model: {
      type: Object,
      default: () => ({ data: 'data.records', total: 'data.total' })
    },
    format: {
      type: Function,
      default: null
    },
    // 是否显示loading
    hasLoading: {
      type: Boolean,
      default: false
    },
    // 默认值
    defaultParams: {
      type: Object,
      default: () => {}
    },
    bgColor: {
      type: String,
      default: '#EFF3F6'
    },
    hasBatchButton: {
      type: Boolean,
      default: true
    },
    showSelectAll: { // 是否显示 选择更多按钮
      type: Boolean,
      default: false
    },
    // 是否展示全选后的数量
    showSelectAllCount: { // 是否显示 选择更多按钮
      type: Boolean,
      default: true
    },
    beforeCheckAll: {
      type: Function,
      default: null
    },
    expand: {
      type: Boolean,
      default: false
    },
    customQuery: {
      type: Function,
      default: null
    },
    isVxe: {
      type: Boolean,
      default: false
    },
    // 是否展示导出按钮
    showExportButton: {
      type: Boolean,
      default: false
    },
    // 导出按钮文本
    exportButtonText: {
      type: String,
      default: '导出'
    },
    exportConfig: {
      type: Object,
      default: () => {
        return {
          // 必传 本地导出 vxeTable才支持本地导出
          localExport: false,
          // localExport为false时必传
          exportApi: null,
          // 非必传,不传默认使用queryParams
          exportParams: null,
          // 非必传，saas-web项目导出成功是否展示任务中心弹出框(异步导出)
          showTaskPopover: true
        }
      }
    }
  },
  data() {
    return {
      isCheckedAll: false,
      key: Math.random().toString(32).replace('.', ''),
      tableData: [],
      total: 0,
      loading: false,
      queryParams: {
        current: 1,
        size: 10
      },
      primaryColor: color.primaryColor,
      config: {}, // 渲染表单的数据
      canShowExpandBtn: false, // 是否显示展开筛选条件按钮
      isExpand: false,
      overflowHeight: 0,
      selection: [],
      currentColumns: [],
      height: 'auto',
      offsetHeight: 0,
      noResetList: [],
      filterFn: null,
      exportLoading: false
    }
  },
  computed: {
    formConfig() {
      const filterColumns = filter(this.columns, column => column.filter)
      return filterColumns
    },
    hasBatchAction() {
      const hasSelection = this.columns.find(column => column.type === 'selection') && this.uiStyle === 'antd'
      if (this.hasBatchButton && hasSelection && !isEmpty(this.selection)) {
        return true
      }
      return false
    },
    // 找出接口中多余的参数
    excessiveParamKeys() {
      const filterColumns = filter(this.columns, column => (column.filter && column.excessive))
      return filterColumns.map(column => column.prop)
    },
    getExportParams() {
      if (this.exportConfig.exportParams) {
        return this.exportConfig.exportParams
      }

      const params = {
        ...this.queryParams,
        responseType: 'blob'
      }
      delete params.current
      delete params.size
      return params
    }
  },
  watch: {
    columns: {
      handler(val) {
        this.currentColumns = this.columns && this.columns.filter(column => !column.hidden)
        this.initConfig()
        if (this.$refs.table) {
          this.$nextTick(() => {
            this.key = Math.random().toString(32).replace('.', '')
            this.$refs.table.columnsReload()
          })
        }
      },
      deep: true,
      immediate: true
    },
    params: {
      handler(val) {
        // this.queryParams = merge(this.queryParams, val)
        const newParams = {
          ...val,
          size: this.queryParams.size
        }
        this.queryParams = { ...this.queryParams, ...newParams }
      },
      deep: true
    },
    loadDataApi: {
      handler(val) {
        this.loadData()
      },
      deep: true
    },
    uiStyle: {
      handler(val) {
        if (!['element', 'antd'].includes(val)) {
          // 如果不是这两套风格，
          // 去掉tableWrapper的marginTop
          this.$nextTick(() => {
            const tablePro = this.$refs.tablePro
            if (tablePro) {
              const tableWrapper = tablePro.querySelector('.table-wrapper')
              tableWrapper.style.marginTop = 0
            }
          })
        }
      },
      immediate: true,
      deep: true
    },
    expand: {
      handler(val) {
        this.isExpand = !!val
      },
      immediate: true
    }
  },
  mounted() {
    // 生成form, table， pagination相关配置
    this.initConfig()

    // 将table上的一些方法挂载到TablePro上
    this.mergeTableMethods()

    // 是否立即请求数据
    if (!this.lazyLoad) {
      this.loadData()
    }

    // 当窗口尺寸，发生变化，改变Table高度，使其实现自适应高度
    this.resizeTable()

    if (this.uiStyle === 'antd') {
      this.$nextTick(() => {
        this.initTableFilter()
      })
    }
  },
  destroyed() {
    this.removeResizeTable()
  },
  methods: {
    debounce: debounce,
    mergeTableMethods() {
      let table
      if (this.isVxe) {
        table = findComponentForward(this, 'VTable')
      } else {
        table = findComponentForward(this, 'ElTable')
      }
      // 解决y-table-pro组件没有相关方法的问题
      Object.keys(table).forEach(key => {
        if (['clearSelection', 'toggleRowSelection', 'toggleAllSelection', 'toggleRowExpansion', 'setCurrentRow', 'clearSort', 'clearFilter', 'doLayout', 'sort'].includes(key)) {
          if (key === 'clearSelection') {
            this[key] = this.handleCancelSelection
          } else {
            this[key] = table[key].bind(table)
          }
        }
      })
    },
    handleUpdateColumns(columns) {
      this.currentColumns = columns
      this.initConfig()
    },
    /* 取消选择 */
    handleCancelSelection() {
      let refTable
      if (this.isVxe) {
        refTable = findComponentForward(this, 'VTable')
      } else {
        refTable = findComponentForward(this, 'ElTable')
      }
      if (refTable) {
        if (typeof refTable.clearSelection === 'function') {
          refTable.clearSelection()
          this.selection = []
        }
        this.$emit('clear-selection')
        this.$emit('selection-change', [])
        this.isCheckedAll = false
        this.$emit('select-all-page', false)
      }
    },
    /* 选择框 */
    handleSelectChange(selection) {
      if (!this.isVxe) {
        this.selection = selection
      } else {
        setTimeout(() => {
          const refTable = findComponentForward(this, 'VxeTable')
          const records = refTable.getCheckboxRecords()
          const reserveSelection = refTable.getCheckboxReserveRecords()
          this.selection = records.concat(reserveSelection)
        }, 200)
      }
      this.isCheckedAll = false
    },
    /* 展开，收起 */
    handleToggle() {
      this.isExpand = !this.isExpand
      const tableFilter = this.$refs.tableFilter
      const elForm = tableFilter.querySelector('.el-form')

      const btnWrapper = tableFilter.querySelector('.btn-wrapper')
      if (this.isExpand) {
        // 展开
        tableFilter.style.overflow = 'hidden'
        tableFilter.style.height = 'auto'
        elForm.style.marginRight = 0
        btnWrapper.style.position = 'static'
        btnWrapper.style.float = 'right'
        btnWrapper.style.paddingBottom = 16 + 'px'
      } else {
        // 收起
        tableFilter.style.overflow = 'hidden'
        tableFilter.style.height = `${this.overflowHeight}px`
        elForm.style.marginRight = `274px`
        btnWrapper.style.position = 'absolute'
        btnWrapper.style.float = 'none'
        btnWrapper.style.paddingBottom = 0 + 'px'
      }
    },
    initTableFilter() {
      if (this.uiStyle === 'antd' || this.uiStyle === 'jixian') {
        this.$nextTick(() => {
          // 如果是antd风格
          const tableFilter = this.$refs.tableFilter

          const tablePro = this.$refs.tablePro
          const elForm = tableFilter.querySelector('.el-form')
          /* 记录筛选框的高度，默认一行的高度 */
          const height = elForm.offsetHeight

          const formWrapper = tablePro.querySelector('.antd-form-wrapper')

          const elFormItem = tableFilter.querySelector('.el-form-item')

          const btnWrapper = formWrapper.querySelector('.btn-wrapper')

          if (elFormItem) {
            let { height: formItemHeight, marginBottom } = getComputedStyle(elFormItem)
            formItemHeight = parseFloat(formItemHeight.replace('px', ''))
            /* 修复Table筛选条件不对齐的问题 */
            marginBottom = parseFloat(marginBottom.replace('px', ''))
            formWrapper.style.paddingTop = marginBottom + 'px'
            btnWrapper.style.bottom = marginBottom + 'px'
            this.overflowHeight = Math.round(formItemHeight + marginBottom)
            if (height > this.overflowHeight) {
              // 换行了
              this.canShowExpandBtn = true
              if (!this.isExpand) {
                tableFilter.style.overflow = 'hidden'
                tableFilter.style.height = `${this.overflowHeight}px`
              }
            } else {
              this.canShowExpandBtn = false
              this.isExpand = this.expand || false
            }
          }
        })
      }
    },
    /* 当屏幕宽度发生变化时，重新绘制表格 */
    resizeTable() {
      window.addEventListener('resize', this.initTableFilter)
    },
    removeResizeTable() {
      window.removeEventListener('resize', this.initTableFilter)
    },
    // 通过点击enter键来查询数据
    queryDataByEnterKey() {
      this.$nextTick(() => {
        const form = this.$refs?.form?.$el
        form && form.addEventListener('keyup', (e) => {
          const keyCode = e.keyCode || e.which
          if (keyCode === 13) {
            this.handleQuery()
          }
        }, false)
      })
    },
    async loadData() {
      const data = cloneDeep(this.queryParams)
      for (const key in data) {
        if (data[key] === undefined || data[key] === null || data[key] === '' || this.excessiveParamKeys.includes(key)) {
          delete data[key]
        }
      }
      try {
        if (this.loading) return
        this.loading = true
        const res = await this.loadDataApi(data)
        let tableData = get(res, this.model.data, [])
        if (typeof this.format === 'function') {
          tableData = this.format(tableData)
          if (!Array.isArray(tableData)) {
            throw Error('format函数必须返回一个数组')
          }
        }
        this.tableData = tableData
        this.total = parseInt(get(res, this.model.total, 0))
        this.$emit('loaded', res)
        this.$nextTick(() => {
          if (this.isCheckedAll) this.handleSelectAll()
        })
      } catch (error) {
        this.loading = false
        this.tableData = []
        this.total = 0
        this.$emit('error', error)
      } finally {
        this.loading = false
        this.$emit('completed')
      }
    },
    initConfig() {
      // 先清空config
      this.config = {}
      // 生成表格列数据
      const filterColumns = filter(this.columns, column => column.filter)
      this.noResetList = this.columns.filter(i => i.noReset).map(i => i.prop)
      filterColumns.forEach(column => {
        const key = column.prop
        // 生成表单的数据
        this.$set(this.config, key, { ...column, clearable: column.clearable === undefined ? true : column.clearable, hidden: false, width: column.filterWidth || '292px' })

        // 生成查询参数
        if (!column.hidden) {
          // 如果这个字段，不需要显示，则默认不作为查询参数
          this.queryParams[key] = ''
        }
      })

      // this.queryParams = merge(this.queryParams, this.params)
      this.queryParams = { ...this.queryParams, ...this.params }
    },
    /* 重置 */
    handleReset() {
      const { size } = this.queryParams
      let cloneParams
      Object.keys(this.queryParams).forEach(param => {
        switch (param) {
          case 'current':
            // 将当前页重置为1
            this.queryParams[param] = 1
            break
          case 'size':
            // 查询条数
            this.queryParams[param] = size
            break
          default:
            // 其他字段，全部清空
            if (!this.noResetList.some(key => param === key)) {
              this.queryParams[param] = ''
            }
        }

        this.$emit('reset')
      })
      // 处理默认参数
      this.queryParams = { ...this.queryParams, ...this.defaultParams }
      // 处理params
      if (!isEmpty(this.params)) {
        cloneParams = cloneDeep(this.params)
        Object.keys(cloneParams).forEach(key => {
          if (!this.noResetList.some(i => key === i)) {
            cloneParams[key] = ''
          }
        })
        // 处理默认参数
        cloneParams = { ...cloneParams, ...this.defaultParams }
        this.$emit('update:params', cloneParams)
      }
      this.total = 0
      this.loadData()
    },
    /**
    * 【查询】
    */
    async handleQuery() {
      try {
        if (!this.$refs.form || await this.$refs.form.validate()) {
          // 查询时，重置current为1
          // this.queryParams = merge(this.queryParams, { current: 1 })
          this.queryParams = { ...this.queryParams, current: 1 }
          console.log('查询参数', this.queryParams)
          this.handleCancelSelection()
          // 对查询参数进行trim处理
          Object.keys(this.queryParams).forEach(key => {
            if (typeof this.queryParams[key] === 'string') {
              this.queryParams[key] = trim(this.queryParams[key])
            }
          })
          this.total = 0
          if (typeof this.customQuery === 'function') {
            // 自定义查询
            return this.customQuery()
          }
          this.loadData()
        }
      } catch (e) {
        console.error(e)
      }
    },
    /**
     * 分页时，重新加载数据
     */
    reloadData({ pageSize: size, currentPage: currentPage, type }) {
      if (type === 'size-change') {
        // 分页条数变更，需要重置current为1
        // this.queryParams = merge(this.queryParams, { size, current: 1 })
        this.queryParams = { ...this.queryParams, size, current: 1 }
        if (this.$attrs['page-size']) {
          this.$attrs['page-size'] = size
        }
      } else {
        // 页码变更时
        // this.queryParams = merge(this.queryParams, { current: currentPage })
        this.queryParams = { ...this.queryParams, current: currentPage }
      }
      this.$emit('before-reload', this.queryParams)
      this.loadData()
    },
    // 选择全部
    handleSelectAll() {
      const selectAll = async(filterFn) => {
        // const YTable = this.$refs.table
        let YTable
        if (this.isVxe) {
          YTable = findComponentForward(this, 'VTable')
          await YTable.toggleAllCheckbox(true)
        } else {
          YTable = findComponentForward(this, 'ElTable')
          // YTable.toggleAllSelection()
          const tableData = filterFn ? filterFn(this.tableData) : this.tableData
          tableData.forEach(row => {
            YTable.toggleRowSelection(row, true)
          })
        }

        this.isCheckedAll = true
        this.$emit('select-all-page', true)
      }
      if (this.beforeCheckAll && typeof this.beforeCheckAll === 'function') {
        this.beforeCheckAll((valid, filterFn) => {
          // filterFn是用来过滤tableData的， 用来对某些disabled的项，不作勾选操作
          this.filterFn = filterFn
          if (valid) {
            selectAll(filterFn)
          }
        })
      } else {
        // 如果beforeCheckAll不是一个Function,走以前的逻辑
        selectAll()
      }
    },
    async handleExportTable() {
      if (this.exportConfig.localExport && this.isVxe) {
        // 针对vxeTable的本地导出
        const vxeTable = this.$refs.table?.$refs?.vxeTable
        if (vxeTable?.openExport) {
          this.exportLoading = true
          try {
            await vxeTable.openExport({ types: ['csv'] })
            this.$emit('on-export-success')
          } finally {
            this.exportLoading = false
          }
        } else {
          throw Error('未找到vxeTable！')
        }
      } else if (!this.exportConfig.localExport) {
        if (!this.exportConfig.exportApi) {
          throw Error('exportApi不能为空！')
        }
        if (typeof this.exportConfig.exportApi !== 'function') {
          throw Error('exportApi必须为一个函数')
        }
        this.exportLoading = true
        try {
          await this.exportConfig.exportApi(this.getExportParams)
          this.$emit('on-export-success')
          if (this.exportConfig.showTaskPopover && this.$store) {
            this.$store.commit('SET_TASK_VISIBLE', true)
          }
        } finally {
          this.exportLoading = false
        }
      } else {
        throw Error('导出配置exportConfig不正确！')
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.y-table-pro {
  background: #fff;
}
.y-form-inline-wrapper {
  position: relative;
  overflow: hidden;
  border-radius: 2px;
  &.antd-form-wrapper {
    padding: 0 16px 0;
    background-color: $--color-white;
    ::v-deep .el-form {
      margin-right: 274px;
      .el-form-item {
        margin-right: 74px;
      }
      .el-form-item .el-input {
        // width: 100%;
        width: 292px;
      }
      .el-form-item .el-select {
        // width: 100%;
        width: 292px;
      }
    }
  }
  .btn-wrapper {
    position: absolute;
    right: 16px;
    bottom: 22px;
  }
}
.table-wrapper {
  padding: 16px;
  // margin-top: 16px;
  background-color: $--color-white;
  border-radius: 2px;
}
.text-gray {
  color: #787979;
}
/* ::v-deep .table-top{
  margin-bottom: 15px;
} */
// 分页
::v-deep .el-pagination {
  .btn-prev,
  .btn-next,
  .el-pager .number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    background: #ffffff;
    border-radius: 2px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    padding: 0;
    margin-right: 8px;
  }
  span:not([class*='suffix']) {
    height: 32px;
    line-height: 32px;
  }
  .el-input--mini .el-input__inner {
    height: 32px;
    line-height: 32px;
  }
  .el-pagination__editor,
  .el-pagination__editor.el-input .el-input__inner {
    height: 32px;
    line-height: 32px;
  }
}

.y-table-batch-action-area {
  z-index: 100;
  display: flex;
  position: fixed !important;
  height: 64px;
  align-items: center;
  bottom: 0;
  right: 0;
  background-color: $--color-white;
  box-sizing: border-box;
  padding: 0 24px;
  box-shadow: 0px -3px 6px -4px rgba(0, 0, 0, 0.12),
    0px -6px 16px 0px rgba(0, 0, 0, 0.08), 0px -9px 28px 8px rgba(0, 0, 0, 0.05);
  .action-left {
    flex: 1;
    height: 100%;
    display: inline-flex;
    align-items: center;
    font-size: 14px;
    button {
      margin-left: 16px;
    }
  }
  .action-right {
    flex: 1;
    height: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
  }
}
</style>
