<!--Created by 熊超超 on 2020/6/1.-->
<template>
  <div v-loading="loading" >
    <form-create v-model="fApi" ref="form" v-if="form" :rule="form.rule" :option="formOption" class="p-l-10 p-t-10 b-b"
                 @submit-click="onSubmit" @clear-click="onClear" v-on="$listeners">
    </form-create>
    <div class="btn b-b p-v-10" v-if="btn" data-flex="main:right cross:center">
      <div data-flex-box="1" class="m-h-10 f-16 f-b">
        <slot name="title">{{title}}</slot>
      </div>
      <xt-button size="small" v-bind="{type: b.type, icon: b.icon, text: b.text, loading: b.loading}"
                 v-for="(b, index) in btn" :key="index" @click="(cb) => onBtnClick(b, cb)"/>
      <el-popover placement="bottom-end" trigger="click" class="m-l-10" v-model="popoverShow">
        <el-transfer v-model="showFieldKeys" :data="selectorFields" :titles="['已隐藏字段', '已显示字段']" target-order="push"></el-transfer>
        <el-button slot="reference" size="small" type="primary" plain icon="el-icon-setting"></el-button>
      </el-popover>
    </div>
    <el-table ref="table" :data="rows" v-bind="$attrs" :size="size" :class="{selected: !!selected}"
              :max-height="maxHeight"
              :highlight-current-row="!multiple && !!selected"
              @select="onSelect"
              @select-all="onSelectAll"
              @row-click="onRowClick"
              @sort-change="onSortChange"
              @current-change="onCurrentChange"
    >
      <template v-for="(column, index) in filteredColumns">
        <el-table-column v-if="column.render" :key="column.prop || index"
                         v-bind="column"
                         :rowKey="rowKey">
          <template slot-scope="scope" v-if="column.renderCell">
            <component :is="{render: column.renderCell(renderParam(scope))}"></component>
          </template>
        </el-table-column>
        <el-table-column v-else :key="column.prop || index"
                         v-bind="column"
                         :rowKey="rowKey"/>
      </template>
    </el-table>

    <div v-if="page" class="m-t-10" data-flex="main:right">
      <el-pagination v-bind="mPage"
                     :current-page="pageNum"
                     :page-sizes="pageSizes"
                     :page-size="pageSize"
                     @current-change="onPageNumChange"
                     @size-change="onSizeChange"
                     :total="total">
      </el-pagination>
    </div>
  </div>
</template>

<script>
import {mapState} from 'vuex'
const domHeight = document.documentElement.clientHeight
export default {
  name: 'xt-table',
  props: {
    title: String,
    form: Object,
    table: Object,
    page: {type: Object, default: _ => ({})},
    btn: Array,
    rowKey: {type: [String, Function], default: 'id'},
    selected: [Object, Array],
    autoHeight: {type: Boolean, default: true},
    size: String,
    formSize: String,
    otherSearch: Object,
    showFields: Array,
    data: Array,
    action: String,
  },
  data() {
    return {
      fApi: {},
      rows: [],
      total: 0,
      defaultPageSizes: [10, 20, 50, 100, 200, 300, 400],
      pageNum: this.page?.pageNum || 1,
      pageSize: this.page?.pageSize || 10,
      formHeight: 0,
      sort: {},
      showFieldKeys: this.showFields,
      popoverShow: false,
      loading: false,
      mData: this.data ? [...this.data] : null,
    }
  },
  computed: {
    ...mapState({
      resources: state => state.resources
    }),
    pageSmall() {
      return ['small', 'mini'].includes(this.size)
    },
    mPage() {
      return {
        small: this.pageSmall,
        background: !this.pageSmall,
        layout: 'total, sizes, prev, pager, next, jumper',
        ...this.page,
      }
    },
    formOption() {
      const {mounted, ...other} = this.form?.option || {}
      return {
        form: {inline: true},
        submitBtn: false,
        resetBtn: false,
        global: {
          '*': {
            props: {
              size: this.formSize || 'small',
              clearable: true,
            }
          }
        },
        mounted: $f => {
          mounted && mounted($f)
          this.formHeight = this.$refs.form.$el.clientHeight + 10
        },
        ...other,
      }
    },
    pageSizes() {
      const list = [...new Set([...this.defaultPageSizes, this.pageSize])]
      list.sort((a, b) => a - b)
      return list
    },
    pageHeight() {
      return this.page ? (this.pageSmall ? 36 : 42) : 0
    },
    btnHeight() {
      return this.btn ? 49 : 0
    },
    maxHeight() {
      // dom高 - 顶部 - tabs - 内容padding - formHeight - pageHeight - 按钮 - 修正值
      return this.autoHeight ? domHeight - 62 - 40 - 20 - this.formHeight - this.pageHeight - this.btnHeight - 2 : null
    },
    multiple() {
      return this.table.columns.some(c => c.type === 'selection')
    },
    permissionFilteredColumns() {
      return this.table.columns.filter(item => {
        if (!item.permission) return true
        if (typeof item.permission === 'string' && this.resources.includes(item.permission)) {
          return true
        } else if (Array.isArray(item.permission)) {
          return item.permission.some(item => this.resources.includes(item))
        }
      })
    },
    selectorFields() {
      return this.permissionFilteredColumns
        .filter(c => c.prop)
        .map(c => ({key: c.prop, label: c.label}))
    },
    filteredColumns() {
      const list = this.permissionFilteredColumns.filter(c => !c.prop || this.showFieldKeys.includes(c.prop))
      // 排序
      list.sort((a, b) => {
        const index1 = this.showFieldKeys.indexOf(a.prop)
        const index2 = this.showFieldKeys.indexOf(b.prop)
        return index1 - index2
      })
      return list
    },
  },
  watch: {
    selected() {
      this.selectData()
    },
    showFieldKeys(val) {
      this.$emit('update:showFields', val)
    },
    permissionFilteredColumns: {
      handler() {
        this.setShowFieldKeys()
      },
      immediate: true,
    },
  },
  mounted() {
    this.getData()
  },
  methods: {
    async getData() {
      if (this.mData) { // 本地数据
        this.rows = this.mData.slice((this.pageNum - 1) * this.pageSize, this.pageNum * this.pageSize)
        this.total = this.mData.length
        this.selectData() // 选中数据
      } else if (this.action) {
        this.loading = true
        const {data} = await this.$store.dispatch(this.action, {...this.getCbData(), component: null})
        if (data) {
          this.rows = data.rows || data.records || data.list
          this.total = data.total
          this.selectData() // 选中数据
        }
      }
    },
    renderParam(scope) {
      return {
        row: scope.row, column: scope.column, cellValue: scope.row[scope.column.property], index: scope.$index
      }
    },
    onCurrentChange(currentRow) {
      if (!this.multiple && currentRow) {
        this.$emit('update:selected', currentRow)
      }
    },
    onSelect(selection, row) {
      // 判断是选择还是取消选择， 看selection的最后一项是否是row即可
      const last = selection[selection.length - 1]
      const isSelect = last && last[this.rowKey] === row[this.rowKey]
      if (isSelect) {
        this.$emit('update:selected', this.$utils.mergeList(this.selected, selection, this.rowKey))
      } else {
        const index = this.selected.findIndex(item => item[this.rowKey] === row[this.rowKey])
        index > -1 && this.selected.splice(index, 1)
      }
    },
    onSelectAll(selection) {
      if (selection.length === 0) {
        // 从选中列表中删除当前页数据
        this.rows.forEach(item => {
          const index = this.selected.findIndex(a => item[this.rowKey] === a[this.rowKey])
          this.selected.splice(index, 1)
        })
      } else {
        this.$emit('update:selected', this.$utils.mergeList(this.selected, selection, this.rowKey))
      }
    },
    onRowClick(row) {
      if (this.multiple) {
        const index = this.selected.findIndex((item) => item[this.rowKey] === row[this.rowKey])
        if (index >= 0) {
          this.selected.splice(index, 1)
        } else {
          this.selected.push(row)
        }
      }
    },
    onPageNumChange(pageNum) {
      this.pageNum = pageNum
      this.getData()
    },
    onSizeChange(pageSize) {
      this.pageNum = 1
      this.pageSize = pageSize
      this.getData()
    },
    selectData() {
      if (this.multiple) {
        this.$refs.table.clearSelection()
        this.$nextTick(() => {
          this.selected.forEach(s => {
            const row = this.rows.find(item => item[this.rowKey] === s[this.rowKey])
            row && this.$refs.table.toggleRowSelection(row, true)
          })
        })
      } else {
        if (this.selected) {
          const row = this.rows.find(item => item[this.rowKey] === this.selected[this.rowKey])
          row && this.$refs.table.setCurrentRow(row)
        }
      }
    },
    onSubmit() {
      console.log('submit')
      this.pageNum = 1
      this.getData()
    },
    onClear() {
      console.log('clear')
      this.fApi.resetFields()
      // this.getData()
    },
    onBtnClick(b, cb) {
      if (b.cb) {
        b.cb(this.getCbData(), cb)
      }
    },
    getCbData() {
      return {
        formData: this.fApi.formData(),
        rows: this.rows,
        otherSearch: this.otherSearch,
        component: this,
        sort: this.sort,
        pageSize: this.pageSize,
        pageNum: this.pageNum,
      }
    },
    setShowFieldKeys() {
      this.showFieldKeys = this.permissionFilteredColumns.map(c => c.prop)
        .filter(k => k)
    },
    onSortChange({prop, order, column}) {
      // 注意：sortable为true的时候，el-table会自动排序，当只能排当前页数据,
      // 如果要排所有的数据，设置sortable为custom，并定义sortMethod
      // sortMethod返回数字，默认按照正序的计算方式
      if (column.sortable !== 'custom') return
      this.sort = {prop, order}
      if (this.mData) {
        if (order !== null) {
          const reverse = order === 'descending' ? -1 : 1
          this.mData.sort((a, b) => {
            let n = 0
            if (column.sortMethod) {
              n = column.sortMethod(a, b)
            } else {
              if (a.name > b.name) n = 1
              if (a.name < b.name) n = -1
            }
            return n * reverse
          })
        } else {
          this.mData = [...this.data]
        }
      }
      this.getData()
    }
  }
}
</script>

<style lang="scss" scoped>
  .el-form /deep/ {
    .el-form-item{
      margin-bottom: 6px;
    }
  }
  /deep/ .el-transfer__buttons button{
    display: block;
    margin-left: 0;
    margin-right: 0;
    padding: 9px 15px;
    font-size: 12px;
  }
</style>
