<template>
  <div class="card-container">
    <template v-if="showCard">
      <div v-show="showSearch && qurable" shadow="never" class="custom-card-heard">
        <Form ref="searchForm" class="search-form custom-search-form" v-model="searchParams" :labelWidth="labelWidth">
          <slot name="search" :params="searchParams"></slot>
          <Cell class="search-buttons">
            <el-button type="primary" icon="el-icon-search" pageSize="small" size="small" @click="search">搜索</el-button>
            <el-button icon="el-icon-refresh" pageSize="small" size="small" v-if="showReset" @click="resetList">重置</el-button>
            <el-button icon="el-icon-arrow-down" pageSize="small" size="small" v-if="showTable" @click="tableShow">展开</el-button>
          </Cell>
        </Form>
      </div>

      <div class="card-body" v-if="visibleTable">
        <slot name="present"></slot>
        <el-row class="mb20">
            <slot name="operations" :selections="selections" :params="searchParams"></slot>
        </el-row>
        <el-table
          ref="table"
          :row-class-name=" showRowClassName ? tableRowClassName : null"
          :data="localPage ? tempData : modalData"
          v-loading="loading"
          :row-key="rowKey"
          @select="handleSelect"
          @select-all="handleSelectAll"
          @selection-change="handleSelectionChange"
          @row-click="rowClick"
          @row-dblclick.stop="rowDblclick"
          class="page-table"
          :cell-style="cellStyle"
          :max-height="maxHeight"
          :highlight-selection-row="true"
          :show-summary="showSummary"
          :summary-method="summaryMethod"
          :row-style="rowStyle"
          :stripe="stripe"
          @sort-change="sortChange"
          :border="border">
          <slot name="default" :params="searchParams"  :list="modalData"></slot>
        </el-table>
        <slot name="tableFeet"></slot>
      </div>
      <div style="position: relative" v-if="visibleTable">
        <pagination v-show="qurable && searchParams.total > 0 && !localPage && !showSpecialPagination"
                    :total="searchParams.total" :page.sync="searchParams.pageNum" :limit.sync="searchParams.pageSize"
                    @pagination="getList"/>
        <pagination v-show="qurable && searchParams.total > 0 && localPage && !showSpecialPagination"
                    :total="searchParams.total"
                    :page.sync="searchParams.pageNum" :limit.sync="searchParams.pageSize" @pagination="getList"/>
        <SpecialPagination v-if="qurable && searchParams.total > 0 && !localPage && showSpecialPagination"
                           :total="searchParams.total" :page.sync="searchParams.pageNum"
                           :limit.sync="searchParams.pageSize" @pagination="getList">
          <template #content>
            <slot name="specialContent"/>
          </template>
        </SpecialPagination>
        <div style="position: absolute; top: 30px; font-pageSize: 14px">
          <slot name="feet"></slot>
        </div>
      </div>
      <slot name="dialogs" :params="searchParams"></slot>
    </template>
    <template v-if="!showCard">
      <Form class="search-form" v-show="showSearch && qurable" v-model="searchParams" :labelWidth="labelWidth">
        <slot name="search" :params="searchParams"></slot>
        <Cell class="search-buttons">
          <el-button icon="el-icon-arrow-down" pageSize="small" size="small" v-if="showHide && isOpen == 0"
                     @click="searchHide">展开
          </el-button>
          <el-button icon="el-icon-arrow-up" pageSize="small" size="small" v-if="showHide && isOpen == 1"
                     @click="searchHide">收起
          </el-button>
          <el-button type="primary" icon="el-icon-search" pageSize="small" size="small" @click="search">搜索</el-button>
          <el-button icon="el-icon-refresh" pageSize="small" size="small" v-if="showReset" @click="resetList">重置
          </el-button>
        </Cell>
      </Form>

      <el-row class="mb20">
        <slot name="operations" :selections="selections" :params="searchParams"></slot>
      </el-row>
      <el-table
        ref="table"
        :row-class-name=" showRowClassName ? tableRowClassName : null"
        :data="localPage ? tempData : modalData"
        v-loading="loading"
        :row-key="rowKey"
        element-loading-text="loading..."
        @select="handleSelect"
        @select-all="handleSelectAll"
        @selection-change="handleSelectionChange"
        @row-click="rowClick"
        @row-dblclick="rowDblclick"
        class="page-table"
        :cell-style="cellStyle"
        :row-style="{height:rowHeight}"
        :max-height="maxHeight"
        stripe
        @sortChange="sortChange"
      >
        <slot name="default" :params="searchParams" :list="modalData"></slot>
      </el-table>
      <div style="position: relative">
        <pagination v-show="qurable && searchParams.total > 0 && !localPage && !showSpecialPagination"
                    :total="searchParams.total" :page.sync="searchParams.pageNum" :limit.sync="searchParams.pageSize"
                    @pagination="getList"/>
        <pagination v-show="qurable && searchParams.total > 0 && localPage && !showSpecialPagination"
                    :total="searchParams.total"
                    :page.sync="searchParams.pageNum" :limit.sync="searchParams.pageSize" @pagination="getList"/>
        <SpecialPagination v-if="qurable && searchParams.total > 0 && !localPage && showSpecialPagination"
                           :showPagination="qurable && searchParams.total > 0 && !localPage && showSpecialPagination"
                           :total="searchParams.total" :page.sync="searchParams.pageNum"
                           :limit.sync="searchParams.pageSize" @pagination="getList">
          <template #content>
            <div style="display: inline-flex;justify-content: center;width: 100%">
              <slot name="specialContent"/>
            </div>
          </template>
        </SpecialPagination>
        <div style="position: absolute; top: 30px; left: 5px; font-pageSize: 14px">
          <slot name="feet"></slot>
        </div>
      </div>

      <slot name="dialogs" :params="searchParams"></slot>
    </template>
  </div>
</template>

<script>
// import XLSX from 'xlsx'
import {isBlank} from "../../../utils";

/**
 * 列表组件
 */
export default {
  name: 'Table',
  props: {
    showSpecialPagination: {
      type: Boolean,
      default: false
    },
    showRowClassName: {
      type: Boolean,
      default: false
    },
    list: {
      required: false,
    },
    formatter: {
      type: Function,
      default: (obj) => obj,
    },
    cellStyle: {
      type: Object
    },
    labelWidth: {
      type: String,
      default: '200px',
    },
    selectValue: {
      type: String,
    },
    showSearch: {
      type: Boolean,
      default: true,
    },
    showTable: {
      type: Boolean,
      default: false
    },

    init: {
      type: Boolean,
      default: true,
    },
    defaultParams: {
      type: Object,
      default: () => {
      },
      desc: '为查询表单设置默认值',
    },
    query: {
      type: Object,
      default: () => {
      }
    },
    //搜索时不需要的参数
    delProp: {
      type: Array,
      default: () => []
    },
    showReset: {
      type: Boolean,
      default: true
    },
    showCard: {
      type: Boolean,
      default: true
    },
    localPage: {
      type: Boolean,
      default: false
    },
    needExclude: {
      type: Boolean,
      default: false
    },
    pageSize: {
      type: Number,
      default: 10
    },
    fieldName: {
      type: String,
      default: 'id'
    },
    rowHeight: {
      type: String,
      default: '8px'
    },
    clickSelect: {
      type: Boolean,
      default: false
    },
    maxHeight: {
      type: String,
      default: '520px'
    },
    rowKey: {
      type: String,
      default: 'id'
    },
    selectableData: {
      type: Array,
      default: () => []
    },
    showSummary: {
      type: Boolean,
      default: false
    },
    summaryMethod: {
      type: Function,
      default: () => [],
    },
    firstSearch: {
      type: Boolean,
      default: true
    },
    defaultTableData: {
      type: Array,
      default: () => [],
      desc: 'table的默认数据，配合firstSearch = false使用'
    },
    rowStyle: {},
    stripe: {
      type: Boolean,
      default: true,
    },
    showHide: {
      type: Boolean,
      default: false
    },
    sortChange: {
      type: Function,
      default: () => {
      }
    },
    border: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    qurable() {
      return typeof this.list === 'function' || this.localPage
    },
  },
  data() {
    return {
      localParams: this.defaultParams, // 初始化本地属性
      searchParams: {
        total: 0,
        pageNum: 1,
        pageSize: 10,
        ...this.localParams
      },
      modalData: [],
      tempData: [],
      loading: false,
      selections: [],
      excludeData: [],
      delRows: [],
      visibleTable: true,
      highlightRow: '',
      searchParamsCache: null,
      scrollPosition: 0,// 存储滚动位置,
      //0收起 1展开
      isOpen: 0,
      response: null,
      visible: false,
    }
  },
  created() {
  },
  // watch: {
  //   defaultParams: {
  //     handler: function(newVal, oldVal) {
  //       this.defaultParams = newVal
  //       this.search()
  //     },
  //     deep: true,
  //   },
  // },
  mounted() {
    if (this.init) {
      if (this.pageSize != 10) {
        this.$set(this.searchParams, 'pageSize', this.pageSize)
      }
      this.modalData = this.defaultTableData
      if (this.firstSearch) {
        this.getList()
      }
    }
    // 绑定enter事件
    // this.enterKeyup();
    if (typeof this.list == 'object' && this.list instanceof Array) {
      this.modalData = this.list
    }

  },
  destroyed() {
    // 销毁enter事件
    // this.enterKeyupDestroyed();
  },
  methods: {
    changeLoading(item) {
      this.loading = item
    },
    searchHide() {
      this.isOpen = 1 - this.isOpen
      this.$emit("searchHide")
    },
    handlePageChange(newPage) {
      if (this.$refs.table) {
        // 在分页切换前保存滚动位置
        this.scrollPosition = this.$refs.table.$el.querySelector('.el-table__body-wrapper').scrollTop;
      }

    },
    resetScrollTop() {
      if (this.$refs.table) {
        // 还原滚动位置
        this.$nextTick(() => {
          this.$refs.table.$el.querySelector('.el-table__body-wrapper').scrollTop = this.scrollPosition;
        });
      }
    },
    tableRowClassName({row, rowIndex}) {
      if (row && row.paymentType != null && row.paymentType == '未付' && row.arriveDate && new Date(row.arriveDate) < new Date()) {
        return 'warning-row'
      }
      if (row && row.paymentType != null && row.paymentType != '已付' && row.arriveDate && new Date(row.arriveDate).setMonth(new Date(row.arriveDate).getMonth() - 1) < new Date()) {
        return 'success-row'
      }
      if (row && (this.isWithinTenDaysOfToday(row.arriveDate) && row.status == '0') || row.status == '1') {
        return 'warning-row';
      }

      let index = this.selections.findIndex(selectedRow => selectedRow[this.fieldName] === row[this.fieldName])
      if (index == -1) {
        return rowIndex % 2 === 0 ? 'stripe' : ''
      } else {
        return 'highlight-row'
      }

      return '';
    },
    isWithinTenDaysOfToday(dateStr) {

      if (dateStr == undefined) {
        return false
      }
      // 解析给定的日期字符串
      const targetDate = new Date(dateStr);
      targetDate.setHours(0, 0, 0, 0)
      // 获取今天的日期并清除时、分、秒、毫秒
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      // 计算两个日期之间的差异（毫秒）
      const diff = targetDate - today;
      // 将毫秒差异转换为天数
      const diffDays = diff / (1000 * 60 * 60 * 24);

      // 检查日期差异是否在10天之内且给定的日期小于今天
      return diffDays >= 0 && diffDays <= 10;
    },
    tableShow() {
      this.visibleTable = !this.visibleTable;
    },
    //切换选中状态
    toggleSelection(rows, selected = true) {
      if (rows) {
        this.$refs.table.clearSelection();
        rows.forEach(row => {
          this.$refs.table.toggleRowSelection(row, selected);
        });
      } else {
        this.$refs.table.clearSelection();
      }
    },
    enterKey(event) {
      const componentName = this.$options.name;
      if (componentName == "Table") {
        const code = event.keyCode
          ? event.keyCode
          : event.which
            ? event.which
            : event.charCode;
        if (code == 13) {
          this.search();
        }
      }

    },
    doLayout() {
      if (this.$refs.table) {
        this.$nextTick(() => {
          this.$refs.table.doLayout();
        })
      }
    },
    enterKeyupDestroyed() {
      document.removeEventListener("keyup", this.enterKey);
    },
    enterKeyup() {
      document.addEventListener("keyup", this.enterKey);
    },

    search() {
      this.searchParams.pageNum = 1
      this.searchParams = {...this.searchParams, ...this.query}
      this.getList()
    },
    async getList(query) {
      await this.$emit('beforeChange')
      this.handlePageChange()
      if (this.list) {
        if (typeof this.list == 'function') {
          try {
            this.loading = true
            let searchQuery = null
            if (this.needExclude) {
              searchQuery = JSON.parse(JSON.stringify({...this.searchParams, ...this.query}))
              this.$set(searchQuery, 'excludeStr', this.excludeData.join(","))
            } else {
              searchQuery = JSON.parse(JSON.stringify({...this.searchParams, ...this.query}))
            }
            searchQuery = JSON.parse(JSON.stringify({...searchQuery, ...query}))

            if (this.delProp && this.delProp.length > 0) {
              this.delProp.forEach(prop => {
                delete searchQuery[prop]
              })
            }
            //备份搜索条件
            this.searchParamsCache = searchQuery
            const response = await this.list(searchQuery)
            const {rows, total, data} = response
            this.response = response
            this.modalData = this.formatter(rows || data, this.searchParams, this.$refs.table)
            this.$nextTick(() => {
              if (this.selectValue) {
                rows.forEach((row) => {
                  this.$refs.table.toggleRowSelection(row, row[this.selectValue])
                })
              }
              //刷新表单
              this.doLayout();
            })
            this.searchParams.total = Number(total)
            this.$emit('search', this.searchParams)
          } finally {
            this.loading = false
          }
        } else if (typeof this.list == 'object' && this.list instanceof Array) {
          if (this.localPage) {
            /**前端分页**/
            let pageNum = this.searchParams.pageNum;
            let pageSize = this.searchParams.pageSize;
            this.tempData = this.modalData.slice((pageNum - 1) * this.pageSize, (pageNum - 1) * pageSize + pageSize)
          }
        }
        this.resetScrollTop()
        this.$nextTick(() => {
          //选中
          if (this.selectableData.length > 0) {
            for (let row of this.selectableData) {
              let index = this.findIndex(row, this.fieldName)
              if (index != undefined) {
                this.$refs.table.toggleRowSelection(this.modalData[index], true)
              }
            }
          }
        })
      }
      await this.$emit('afterChange')
    },
    clearData() {
      this.modalData = []
    },
    localAddRows(rows, fieldName, type) {
      if (!isBlank(rows) && type == 'add') {
        //新增
        rows.forEach(row => row.operationFlag = '1')
      }
      if (this.localPage) {
        this.modalData = [...this.modalData, ...rows]
        this.searchParams.total = this.modalData.length || 0
        this.searchParams.pageNum = Math.ceil(this.searchParams.total / this.searchParams.pageSize)
        this.tempData = this.modalData.slice((this.searchParams.pageNum - 1) * this.searchParams.pageSize, (this.searchParams.pageNum - 1) * this.searchParams.pageSize + this.searchParams.pageSize)
      } else if (this.needExclude) {
        this.excludeData = this.excludeData.filter(item => !rows.some(row => row[fieldName] == item))
        this.getList()
      }
    },
    localRemoveSelections(rows, fieldName, dataSource) {
      let arr = rows.map(row => row[fieldName])
      rows.forEach(row => {
        if (!isBlank(row[dataSource])) {
          row.operationFlag = 3
          this.delRows.push(row)
        }
      })
      this.excludeData = this.excludeData.concat(arr)
      this.modalData = this.modalData.filter(item => !rows.some(section => section[fieldName] == item[fieldName]))
      this.searchParams.total = this.modalData.length || 0
      this.tempData = this.modalData.slice((this.searchParams.pageNum - 1) * this.searchParams.pageSize, (this.searchParams.pageNum - 1) * this.searchParams.pageSize + this.searchParams.pageSize)
    },
    updateRows(arr, condition) {
      if (!Array.isArray(arr)) {
        return;
      }
      this.modalData.map(item => {
        for (let value of arr) {
          if (condition(item)) {
            item[value.label] = value.value
          }
        }
      })
      this.searchParams.total = this.modalData.length || 0
      this.searchParams.pageNum = Math.ceil(this.searchParams.total / this.searchParams.pageSize)
      this.tempData = this.modalData.slice((this.searchParams.pageNum - 1) * this.searchParams.pageSize, (this.searchParams.pageNum - 1) * this.searchParams.pageSize + this.searchParams.pageSize)
    },
    resetList() {
      this.searchParams = {
        total: 0,
        pageNum: 1,
        pageSize: 10,
        ...this.localParams
      }
      this.clearSort()
      this.$emit('resetList')
      this.getList()
    },
    async exportList(name = '数据') {
      if (this.loading) {
        return
      }
      this.loading = true
      if (this.list) {
        try {
          // 根据插槽分发的节点属性获取标题及数据
          // 一级标题，目前只支持2级标题的导出，通过第一层级的prop控制是否导出
          const nodes = this.$scopedSlots.default().filter(
            ({
               componentOptions: {
                 children,
                 propsData: {prop},
               },
             }) => prop
          )
          const hasSubTitle = nodes.filter(({componentOptions: {children}}) => children && children.length > 0).length > 0
          const exportTitle = hasSubTitle ? [[], []] : [[]]
          const merges = []

          const columns = []
          let col = 0
          nodes.forEach(
            ({
               componentOptions: {
                 children,
                 propsData: {label, prop},
               },
             }) => {
              if (children && children.length > 0) {
                // 标题
                exportTitle[0].push(label, ...Array(children.length - 1).fill(''))
                if (hasSubTitle) {
                  merges.push({
                    s: {r: 0, c: col},
                    e: {r: 0, c: col + children.length - 1},
                  })
                }
                // 属性
                children.forEach((child) => {
                  columns.push(child.componentOptions.propsData.prop)
                  exportTitle[1].push(child.componentOptions.propsData.label)
                  col++
                })
              } else {
                // 标题
                exportTitle[0].push(label)
                if (hasSubTitle) {
                  merges.push({s: {r: 0, c: col}, e: {r: 1, c: col}})
                  exportTitle[1].push('')
                }
                // 属性
                columns.push(prop)
                col++
              }
            }
          )
          const {records} = await this.list({
            ...this.searchParams,
            pageNum: 1,
            pageSize: this.searchParams.total,
          })
          const exportData = this.formatter(records, this.searchParams).map((row) => [...columns.map((prop) => row[prop])])
          const sheet = XLSX.utils.aoa_to_sheet([...exportTitle, ...exportData])
          sheet['!merges'] = merges
          const wb = XLSX.utils.book_new()
          XLSX.utils.book_append_sheet(wb, sheet, `data`)
          XLSX.writeFile(wb, `${name}.xlsx`)
        } finally {
          this.loading = false
        }
      }

      this.$nextTick(() => {
      })
    },
    handleSelect(selection, row) {
      for (let i = 0; i < selection.length; i++) {
        const {id} = selection[i]
        if (id === row.id) {
          this.$emit('select', [row.id])
          return
        }
      }
      this.$emit('cancel', [row.id])
    },
    handleSelectAll(selection) {
      const isSelect = selection.length > 0
      this.$emit(
        isSelect ? 'select' : 'cancel',
        this.modalData.map((row) => row.id)
      )
    },
    handleSelectionChange(selection) {
      this.selections = selection
    },
    async remove(action, msg, sucessMsg) {
      await this.$confirm(msg || '是否确认删除选中的数据项？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
      const {code} = await action()
      if (code === 200 || code === 0) {
        this.$message.success(sucessMsg || '删除成功')
        await this.getList()
      }
    },
    //单击行数据
    rowClick(row, column, event) {
      if (this.clickSelect) {
        let index = this.selections.findIndex(selectedRow => selectedRow[this.fieldName] === row[this.fieldName])
        if (index == -1) {
          this.$refs.table.toggleRowSelection(row, true);
        } else {
          this.$refs.table.toggleRowSelection(row, false);
        }
      }
      this.$emit('rowClick', {row: row, column: column, event: event})
    },
    //双击行数据
    rowDblclick(row, column, event) {
      this.$emit('rowDblclick', {row: row, column: column, event: event})
    },
    getTotal() {
      return this.searchParams.total
    },
    getFieldTotal(fieldName) {

    },
    findIndex(row, fieldName) {
      let i = -1;
      for (let obj of this.modalData) {
        i++

        if (obj[fieldName] === row) {
          return i
        }
      }
      return undefined
    },
    clearSort() {
      this.$refs.table.clearSort()
    }
  },
}
</script>

<style scoped>
.page-table .el-link {
  margin-right: 10px;
}

.search-form.el-form--inline {
  /*padding-bottom: 25px !important;*/
  /*padding-top: 22px !important;*/

}

.custom-search-form {
  display: flex;
  align-items: center;
  align-content: center;

  flex-wrap: wrap;
}


.custom-card-heard {
  padding: 15px !important;
}

.highlight-row {
  background-color: #FFF3E0 !important;
}

.stripe {
  background-color: #FAFAFA !important;
}

::v-deep .card-body {
  border-bottom: none;
  padding: 10px 15px 15px;
}

::v-deep .pagination-container {
  margin-bottom: 0px;
  padding-bottom: 0px;
}

::v-deep .el-form-item {
  margin: 10px 0px !important;
}

.search-buttons {
  margin-left: 20px !important;
}


</style>
