<template>
  <div class="ant-query-table" :style="{ height }">
    <slot name="qt-top-query" :queryParams="queryParams" :tableData="tableData"></slot>
    <div v-if="queryConfig !== false" class="qt-query">
      <a-form-model layout="inline" :model="queryTemp">
        <a-form-model-item v-for="item in queryConfig.items" :key="item.prop" :label="item.label">
          <slot :name="`query-${item.prop}`" :prop="item.prop" :queryParams="queryTemp">
            <component
              :is="item.component || 'a-input'"
              v-model="queryTemp[item.prop]"
              v-bind="item.options"
              v-on="item.listeners"
            />
          </slot>
        </a-form-model-item>
        <a-form-model-item>
          <a-space :size="16">
            <a-button type="primary" @click="handleQueryBtnClick">查询</a-button>
            <a-button @click="handleResetBtnClick">重置</a-button>
            <slot name="querybar-action"></slot>
          </a-space>
        </a-form-model-item>
      </a-form-model>
    </div>
    <slot name="qt-query-toolbar" :queryParams="queryParams" :tableData="tableData"></slot>
    <div v-if="showTool" class="qt-toolbar">
      <slot name="toolbar">
        <a-space :size="16">
          <slot
            name="toolbar-left"
            :tableData="tableData"
            :selectedRows="selectedRows"
            :selectedRowKeys="selectedRowKeys"
            :queryParams="queryParams"
            :queryParamsWithoutPage="queryTemp"
          />
        </a-space>
        <a-space :size="16">
          <slot name="toolbar-right" />
        </a-space>
      </slot>
    </div>
    <div ref="qt-table" class="qt-table" v-resize="handleTableResize">
      <a-table
        ref="table"
        :columns="tableColumns"
        :loading="tableLoading"
        :data-source="tableData"
        :pagination="false"
        v-bind="tableConf"
        :scroll="tableScrollConfig"
        :style="{ height: '100%' }"
        :rowSelection="rowSelection"
      >
        <template v-for="(_, name) in tabletitleSlots" :slot="name">
          <slot :name="name" />
        </template>
        <template v-for="(config, name) in titleRender" :slot="name">
          <div :key="name" :style="{ display: 'flex', alignItems: 'center' }">
            <span>{{ config.title }}</span>
            <a-popover title="注释">
              <template slot="content">
                {{ config.explain }}
              </template>
              <a-icon type="exclamation-circle" style="width: 14px; height: 14px; margin-left: 5px; cursor: pointer" />
            </a-popover>
          </div>
        </template>
        <template v-for="(_, name) in tablecellSlots" :slot="name" slot-scope="text, record, index">
          <slot :name="tablecellSlotPrefix + name" v-bind="{ text, record, index }" :data-index="name" />
        </template>
      </a-table>
      <div v-if="tableScrollY" class="qt-table-border__bottom"></div>
    </div>
    <div v-if="isPageMode" class="qt-page">
      <a-pagination v-bind="pageConf" @change="handlePageChange" @showSizeChange="handlePageChange"></a-pagination>
    </div>
  </div>
</template>

<script>
import moment from 'moment'
import cloneDeep from 'lodash.clonedeep'
import {
  defaultUserColumns,
  tabletitleSlotPrefix,
  tablecellSlotPrefix,
  defaultTableCellWidth,
  defaultTableScrollSize,
  defaultTableConfig,
  actionSlotName,
  defaultPageSize,
  defaultPageConfig,
} from './defaultSettings'

export default {
  props: {
    height: { type: [String, Boolean], default: 'auto' },
    queryConfig: { type: [Object, Boolean], default: () => ({}) },
    tableConfig: { type: Object, default: () => ({}) },
    pageConfig: { type: [Object, Boolean], default: () => ({}) },
    showTool: { type: Boolean, default: true },
  },
  data() {
    return {
      queryTemp: {},
      tableLoading: false,
      tableData: [],
      selectedRows: [],
      selectedRowKeys: [],
      widthMap: {
        date: 168,
      },
      titleRender: {},
      tableScrollX: false,
      tableScrollY: false,
      tabletitleSlotPrefix,
      tablecellSlotPrefix,
      actionSlotName,
      total: 0,
      pageParams: { pageNum: 1, pageSize: defaultPageSize },
    }
  },
  computed: {
    isPageMode() {
      return this.pageConfig !== false
    },
    queryParams() {
      if (this.isPageMode) {
        return { ...this.queryTemp, ...this.pageParams }
      }
      return this.queryTemp
    },
    tableColumns() {
      const { columns, userColumns = false } = this.tableConfig
      const originColumns = cloneDeep(columns)
      // 暂存序号列
      let serialCol = null
      const serialColIndex = originColumns.findIndex((col) => col.dataIndex === 'serial')
      if (serialColIndex > -1) {
        serialCol = originColumns[serialColIndex]
        originColumns.splice(serialColIndex, 1)
      }
      // 暂存操作列
      let actionCol = null
      const actionColIndex = originColumns.findIndex((col) => col.dataIndex === actionSlotName)
      if (actionColIndex > -1) {
        actionCol = originColumns[actionColIndex]
        originColumns.splice(actionColIndex, 1)
      }
      // 先处理其他列
      if (userColumns) {
        originColumns.push(...defaultUserColumns)
      }
      this.handleColumns(originColumns)
      // 补充序号列到第一列
      if (serialCol) {
        originColumns.unshift({
          title: '序号',
          align: 'center',
          width: 80,
          ...serialCol,
          customRender: this.renderSerial.bind(this, serialCol.followPage === true),
          fixed: 'left',
        })
      }
      // 补充操作列到最后一列
      if (actionCol) {
        originColumns.push({
          title: '操作',
          width: 150,
          align: 'center',
          ...actionCol,
          slots: { title: 'action-title' },
          scopedSlots: { customRender: 'action' },
          fixed: 'right',
        })
      }
      return originColumns
    },
    tableMaxWidth() {
      return this.calculateWidthByColumns(this.tableColumns)
    },
    tableScrollConfig() {
      return {
        ...(this.tableScrollX === false ? {} : { x: '100%' }),
        ...(this.tableScrollY === false ? {} : { y: this.tableScrollY }),
      }
    },
    tableConf() {
      return {
        ...defaultTableConfig,
        ...this.tableConfig.table,
      }
    },
    defaultRowSelection() {
      return {
        selectedRowKeys: this.selectedRowKeys,
        onChange: this.handleSelectChange.bind(this),
        fixed: true,
        getCheckboxProps: (record) => ({
          props: { disabled: record.selectable === false },
          style: { display: record.selectable === false ? 'none' : '' },
        }),
      }
    },
    rowSelection() {
      if (!this.tableConfig.select) return null
      if (this.tableConfig.select instanceof Object) {
        return {
          ...this.tableConfig.select,
          ...this.defaultRowSelection,
        }
      }
      return this.defaultRowSelection
    },
    tabletitleSlots() {
      const tabletitleSlots = {}
      for (const slotName in this.$scopedSlots) {
        if (!slotName.startsWith(this.tabletitleSlotPrefix)) continue
        const slotFunc = this.$scopedSlots[slotName]
        tabletitleSlots[slotName] = slotFunc
      }
      return tabletitleSlots
    },
    tablecellSlots() {
      const tablecellSlots = {}
      for (const slotName in this.$scopedSlots) {
        if (!slotName.startsWith(this.tablecellSlotPrefix)) continue
        const translotName = slotName.replace(this.tablecellSlotPrefix, '')
        const slotFunc = this.$scopedSlots[slotName]
        tablecellSlots[translotName] = slotFunc
      }
      return tablecellSlots
    },
    pageConf() {
      if (!this.isPageMode) return false
      return {
        ...defaultPageConfig,
        current: this.pageParams.pageNum,
        pageSize: this.pageParams.pageSize,
        total: this.total,
        onChange: this.handlePageChange.bind(this),
        onShowSizeChange: this.handlePageChange.bind(this),
      }
    },
  },
  mounted() {
    if (this.queryConfig && this.queryConfig.initQueryParams) {
      this.initQueryTemp()
    }
    const immediate = this.tableConfig.immediate
    if (immediate !== false) this.queryTable()
  },
  methods: {
    initQueryTemp() {
      const { initQueryParams = {} } = this.queryConfig
      this.queryTemp = cloneDeep(initQueryParams)
    },
    handleTableResize(target) {
      if (!target) return
      this.tableScrollX = target.offsetWidth < this.tableMaxWidth
      const tableBody = target.querySelector('.ant-table-scroll>.ant-table-body>table>.ant-table-tbody') || {
        offsetHeight: 0,
      }
      const innerHeight =
        this.getTableHeadDom(target, this.tableScrollY).offsetHeight +
        tableBody.offsetHeight +
        (this.tableScrollX ? defaultTableScrollSize : 0)
      if (target.offsetHeight > innerHeight) {
        this.tableScrollY = false
      } else {
        this.tableScrollY = true
        this.$nextTick(() => {
          this.tableScrollY = target.offsetHeight - this.getTableHeadDom(target, true).offsetHeight
        })
      }
    },
    getTableHeadDom(target, tableScrollY) {
      let tableHead = null
      if (tableScrollY === false) {
        tableHead = target.querySelector('.ant-table-scroll>.ant-table-body>table>.ant-table-thead')
      } else {
        tableHead = target.querySelector('.ant-table-scroll>.ant-table-header>table')
      }
      return tableHead || { offsetHeight: 0 }
    },
    handleColumns(originColumns) {
      for (let i = 0; i < originColumns.length; i++) {
        const {
          title,
          titleConfig,
          dataIndex,
          style,
          width,
          minWidth,
          align,
          type,
          format,
          dictName,
          digits,
          unit = '',
        } = originColumns[i]
        if (!title) {
          originColumns[i].slots = { title: tabletitleSlotPrefix + dataIndex }
        }
        if (!title && titleConfig) {
          this.$set(this.titleRender, tabletitleSlotPrefix + dataIndex, titleConfig)
          originColumns[i].align = align || 'right'
        }
        originColumns[i].scopedSlots = { customRender: dataIndex }
        if (!width) {
          originColumns[i].width = this.widthMap[type] || defaultTableCellWidth
        }
        if (width === 'auto' && this.tableScrollX !== false) {
          originColumns[i].width = minWidth || defaultTableCellWidth
        }
        if (type === 'number') {
          originColumns[i].align = align || 'right'
          originColumns[i].customRender = (text) => {
            if (isNaN(text)) {
              return text || ''
            } else {
              let num = text
              if (typeof digits === 'number') {
                num = Number(text).toLocaleString('en', { minimumFractionDigits: digits })
              }
              return unit ? `${num} ${unit}` : num
            }
          }
        }
        if (type === 'date') {
          originColumns[i].align = align || 'center'
          originColumns[i].customRender = (text) => (text && format ? moment(text).format(format) : text)
        }
        if (originColumns[i].children) {
          this.handleColumns(originColumns[i].children)
          continue
        }
      }
    },
    calculateWidthByColumns(columns) {
      let baseWidth = 0
      if (this.rowSelection) {
        baseWidth = this.rowSelection.columnWidth || 60
      }
      return columns.reduce((cur, col) => {
        if (col.children && col.children.length > 0) {
          return cur + this.calculateWidthByColumns(col.children)
        }
        return cur + (col.width === 'auto' ? col.minWidth || defaultTableCellWidth : col.width)
      }, baseWidth)
    },
    async queryTable() {
      this.tableLoading = true
      try {
        let res = []
        if (this.tableConfig.query && typeof this.tableConfig.query === 'function') {
          res = await this.tableConfig.query(this.queryParams)
        }
        // console.log('----res----', res)
        if (!this.isPageMode) {
          // ---- 不分页 ----
          this.tableData = res?.data || []
          this.tableLoading = false
          this.$nextTick(() => {
            this.handleTableResize(this.$refs['qt-table'])
          })
          return
        }
        // ---- 分页模式 ----
        this.tableData = res.data || []
        this.total = res.totalCount || 0
        if (this.tableData.length === 0 && this.total > 0) {
          this.pageParams.pageNum = Math.ceil(this.total / this.pageParams.pageSize)
          this.queryTable()
        } else {
          this.tableLoading = false
          this.$nextTick(() => {
            this.handleTableResize(this.$refs['qt-table'])
          })
        }
      } catch {
        this.tableLoading = false
        this.tableData = []
        this.total = 0
        this.$nextTick(() => {
          this.handleTableResize(this.$refs['qt-table'])
        })
      }
      this.handleSelectChange(
        this.selectedRowKeys.filter((key) => this.tableData.some((d) => d[this.tableConf.rowKey] === key)),
        this.selectedRows.filter((r) =>
          this.tableData.some((d) => d[this.tableConf.rowKey] === r[this.tableConf.rowKey])
        )
      )
    },
    handleQueryBtnClick() {
      this.pageParams.pageNum = 1
      this.commitAction('query')
    },
    handleResetBtnClick() {
      this.initQueryTemp()
      this.pageParams.pageNum = 1
      this.commitAction('query')
    },
    handleSelectChange(selectedRowKeys, selectedRows) {
      this.selectedRowKeys = selectedRowKeys
      this.selectedRows = selectedRows
    },
    renderSerial(followPage, text, record, index) {
      if (text) return text
      const { pageNum, pageSize } = this.pageParams
      const base = followPage ? pageSize * (pageNum - 1) + 1 : 1
      return base + index
    },
    handlePageChange(page, size) {
      if (this.pageParams.pageSize === size) {
        this.pageParams.pageNum = page
      } else {
        this.pageParams.pageNum = 1
        this.pageParams.pageSize = size
      }
      this.queryTable()
    },
    commitAction(action) {
      switch (action) {
        case 'query':
          this.queryTable()
          break
        case 'reload':
          this.queryTemp = {}
          this.pageParams.pageNum = 1
          this.pageParams.pageSize = defaultPageSize
          this.queryTable()
          break

        case 'gofirst':
          this.pageParams.pageNum = 1
          this.queryTable()
      }
    },
    getQueryParams(withPage = false) {
      if (withPage) {
        return cloneDeep(this.queryParams)
      } else {
        return cloneDeep(this.queryTemp)
      }
    },
    setQueryParamsValue(key, value) {
      this.$set(this.queryTemp, key, value)
    },
  },
}
</script>

<style lang="less">
.ant-query-table {
  display: flex;
  flex-direction: column;
}
.qt-query {
  .ant-form-item {
    margin-bottom: 16px;
  }
  .ant-input-affix-wrapper .ant-input {
    width: 190px;
  }
  .ant-select {
    width: 190px;
  }
  .ant-select-dropdown {
    max-height: 246px !important;
    top: 28px !important;
  }
  .ant-select-dropdown-menu {
    max-height: 246px;
  }
}

.qt-toolbar {
  margin-bottom: 16px;
  display: flex;
  justify-content: space-between;
}

.qt-table {
  min-height: 122px;
  flex: 1;
  overflow-y: auto;
  .ant-table-row-indent {
    padding-left: 0 !important;
  }
  .ant-table-thead .ant-table-row-cell-break-word {
    text-align: center !important;
    font-weight: bold;
  }
  .qt-table-border__bottom {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 1px;
    background-color: #e8e8e8;
  }
}
.qt-table::-webkit-scrollbar {
  display: none;
}

.qt-page {
  margin-top: 16px;
  display: flex;
  justify-content: flex-end;
}
</style>
