<template>
  <div class="vuc-datatable"
       :class="tableClass">
    <vxe-toolbar ref="vxeDatatableToolbar"
                 id="toolbarId"
                 v-if="showToolbar">
      <template v-slot:buttons>
        <Tabs :style="tabStyle"
              class="data-tabs"
              v-if="tabs && tabs.length>0"
              :value="activeTab"
              @on-click="handleTabClick">
          <TabPane v-for="(tab,idx) in tabs"
                   :key="`${_uid}_${idx}`"
                   :label="tab.label"
                   :name="tab.name">
          </TabPane>
        </Tabs>
        <div class="toolbar"
             :style="toolbarStyles">

          <span class="toolbar-slot"
                v-if="$slots.default">
            <slot></slot>
          </span>
          <div ref="refToolbar"
               style="display:inline-block;float:right;"
               :style="istreeStyle">
            <span class="toolbar-slot fr"
                  v-if="$slots.toolbar">
              <slot name="toolbar"></slot>
            </span>
            <span class="toolbar-default"
                  :style="istreeStyle">
              <div :class="{'search-text':true}"
                   style="position:relative;margin-left:5px;display:inline-block;"
                   :style="istreeStyle"
                   v-if="toolbar.indexOf('search')>=0">
                <Input suffix="ios-search"
                       ref="tableInput"
                       v-model="filterText"
                       :style="istreeStyle"
                       @input="doFilter"
                       @on-enter="doInputEnter"
                       placeholder="请输入搜索内容" />
                <Icon v-if="filterText!==''"
                      style="position:absolute;right:7px;top:50%;margin-top:-10px;background:#fff;z-index:9"
                      type="ios-close-circle"
                      size=18
                      @click="doFilter('')"></Icon>
              </div>
              <span v-if="toolbar.indexOf('refresh')>=0"
                    class="toolbar-split">|</span>
              <Button v-if="toolbar.indexOf('refresh')>=0"
                      type="text"
                      size="small"
                      icon="md-refresh"
                      @click="handleToolBarClick('refresh')"
                      class="toolbar-item">
                刷新
              </Button>
              <span v-if="toolbar.indexOf('print')>=0"
                    class="toolbar-split">|</span>
              <Button v-if="toolbar.indexOf('print')>=0"
                      type="text"
                      size="small"
                      icon="ios-print"
                      @click="handleToolBarClick('print')"
                      class="toolbar-item">
                打印
              </Button>
              <span v-if="toolbar.indexOf('exp')>=0"
                    class="toolbar-split">|</span>
              <Button v-if="toolbar.indexOf('exp')>=0"
                      type="text"
                      size="small"
                      icon="md-trending-down"
                      @click="handleToolBarClick('exp')"
                      class="toolbar-item">
                导出
              </Button>
              <span v-if="toolbar.indexOf('imp')>=0"
                    class="toolbar-split">|</span>
              <Button v-if="toolbar.indexOf('imp')>=0"
                      type="text"
                      size="small"
                      icon="md-trending-up"
                      @click="handleToolBarClick('imp')"
                      class="toolbar-item">
                导入
              </Button>
              <!--               <span class="toolbar-split">|</span>
              <Button type="text"
                      size="small"
                      @click="setColumnAutoWidth()"
                      class="toolbar-item">
                <img style="display:inline-block;margin-bottom:-3px;margin-right:5px;"
                     src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA0ElEQVQ4T9XTMQrCQBAF0D8TCJZ6E29gjiA2li4S9hyeIwSNpRbiEeINvImWEsgfWTEiSCCSRqfefczs/BX0LAn30zRNVHXyjUXylOd5+QC89ysAV5LnLoiqjgEMsyxbvQCSZRC7AM+Okx8FvPfTqqrKoiiu7+M454ZxHIe2j60jiMgMwNzM1qp6U9VtQEguSA5EZAlgZ2YHVf18gwYguYmiKADFE3B1XQfAici+FQhb6DXCn6/RzC4AOkUZwFhERq8kNnvtEuPmTBP9x1/oU3fbIcwRmICBMgAAAABJRU5ErkJggg==" />自适应
              </Button> -->
            </span>
          </div>
        </div>
      </template>
    </vxe-toolbar>

    <vxe-table ref="xTable"
               :key="`${_uid}`"
               v-bind="tableProps"
               :size="size"
               :height="tableHeight"
               :start-index="startIndex"
               :data.sync="tableData"
               :customs.sync="customColumns"
               :context-menu="contextMenu"
               :auto-resize="autoResize"
               :highlight-current-row="true"
               :show-footer="showFooter"
               :span-method="spanMethod"
               :footer-method="footerMethod"
               :footer-span-method="footerSpanMethod"
               :footer-row-class-name="footerRowClassName"
               :footer-cell-class-name="footerCellClassName"
               :row-class-name="rowClassName"
               :cell-class-name="cellClassName"
               :header-row-class-name="headerRowClassName"
               :header-cell-class-name="headerCellClassName"
               :cell-style="cellStyleFn"
               :header-cell-style="headerCellStyle"
               :loading="loading"
               :loading-text="loadingText"
               :fit-column="fitColumn"
               :sort-method="sortMethod||defaultSortMethod"
               :select-config="selectConfig"
               @sort-change="handleSortChange"
               @context-menu-click="handleContextMenuClickEvent"
               @scroll="handleTableScroll"
               @select-change="handleChecked"
               @select-all="handleChecked"
               @cell-click="handleSelected"
               @current-change="handleChange"
               @cell-dblclick="handleDblClick"
               @edit-closed="handleEditClosed"
               @edit-actived="handleEditActived"
               @header-cell-context-menu="handleHeaderContext"
               @cell-mouseenter="handleCellMouseenter"
               @cell-mouseleave="handleCellMouseleave"
               @resizable-change="handleResizableChange"
               @on-cell-selected="handleOnCellSelected"
               @filter-change="handleFilterChange"
               @checkbox-range-end="handleRangeEnd">
               <template v-slot:empty>
                  <img :src="nodataPath">
                  <div style="padding-top: 10px">
                    <span>暂无数据</span>
                  </div>
               </template>
    </vxe-table>

    <vxe-pager v-if="showPager"
               ref="tablePager"
               :current-page="tablePage.currentPage"
               :page-size="tablePage.pageSize"
               :page-sizes="[50,100,200,500,1000,-1]"
               :total="tablePage.totalResult"
               :layouts=" ['PrevPage', 'JumpNumber', 'NextPage', 'FullJump', 'Sizes']"
               @page-change="handlePageChange"></vxe-pager>
  </div>
</template>
<script>
import * as XEUtils from 'xe-utils'
import api from './vuc-datatable-api'
import editor from './vuc-datatable-edit'
import IO from './vuc-datatable-io'
import VucDatatableHeaderConfig from './header-config/vuc-datatable-header-config.vue'
import VucTextarea from '../vuc-buttonedit/vuc-textarea.vue'
import VucCalculator from '../vuc-calculator/vuc-calculator.vue'
import clonedeep from 'clonedeep'
export default {
  name: 'VucDatatable',
  props: {
    tabs: {
      type: Array,
      default: () => []
    },
    activeTab: {
      type: String,
      default: ''
    },
    height: {
      type: [Number, String],
      default: 'auto'
    },
    showToolbar: {
      type: Boolean,
      default: false
    },
    toolbar: {
      type: Array,
      default: () => ['search', 'refresh', 'imp', 'exp', 'print']
    },
    showPager: {
      type: Boolean,
      default: false
    },
    isArrow: {
      type: Boolean,
      default: true
    },
    showInfo: {
      type: Boolean,
      default: true
    },
    // 编辑时状态显示
    showStatus: {
      type: Boolean,
      default: false
    },
    size: {
      type: String,
      default: 'mini'
    },
    searchColumns: {
      type: Array,
      default: () => []
    },
    sumField: {
      type: String,
      default: ''
    },
    treeField: {
      type: String,
      default: ''
    },
    rowKey: {
      type: String,
      default: ''
    },

    showHeader: {
      type: Boolean,
      default: true
    },
    stripe: {
      type: Boolean,
      default: true
    },
    showCheckbox: {
      type: Boolean,
      default: true
    },
    // 表格id,主要用于保存显示列信息
    tableId: {
      type: String
    },
    /// 自定义列
    customs: {
      type: Array,
      default: () => []
    },
    editable: {
      type: Boolean,
      default: false
    },
    // 排序方法
    sortMethod: Function,
    // 单元格焦点时调用
    activeMethod: Function,
    // 表内合并行列函数
    spanMethod: Function,
    // 表尾添加数据函数
    footerMethod: Function,
    // 表尾合并行列函数
    footerSpanMethod: Function,
    // 表尾样式
    footerCellClassName: [String, Function],
    footerRowClassName: [String, Function],
    // 添加行样式
    rowClassName: [String, Function],
    // 添加单元格样式
    cellClassName: [String, Function],
    // 表头添加单元格样式
    headerCellClassName: [String, Function],
    headerRowClassName: [String, Function],
    enter2right: {
      type: Boolean,
      default: true
    },
    bodyContextMenus: {
      type: Array,
      default: () => []
    },
    ContextVisibleMethod: Function,
    // 展开全部
    expand: {
      type: [Boolean, Number, String],
      default: false
    },
    // 树选择时联动,否则点哪个显示哪个
    linkage: {
      type: Boolean,
      default: true
    },
    // 全键盘事件
    keydownMethod: Function,
    // 序号开始
    startIndex: {
      type: Number,
      default: 0
    },
    showLoading: {
      type: Boolean,
      default: false
    },
    loadingText: {
      type: String,
      default: '数据加载中...'
    },
    autoResize: {
      type: Boolean,
      default: false
    },
    // 隐藏表头右键
    hideHeaderContext: {
      type: Boolean,
      default: false
    },
    // 折行
    cellBreakWord: {
      type: Boolean,
      default: false
    },
    // 搜索时左侧匹配
    startFilter: {
      type: Boolean,
      default: false
    },
    // 打印配置
    printHeader: {
      type: Array,
      default: () => [[{ type: 'pageNum', align: 'right' }]]
    },
    // 打印尾部,二维数组
    printFooter: {
      type: Array,
      default: () => [[]]
    },
    printTitle: {
      type: String,
      default: '表格打印'
    },
    printPageFooter: {
      type: Boolean,
      default: true
    },
    // 记忆表头
    storeHeader: {
      type: Boolean,
      default: true
    },
    tabsMarginRight: {
      type: [Number, String],
      default: 0
    },
    printColsCanConfig: {
      type: Boolean,
      default: true
    },
    printIndex: {
      type: Boolean,
      default: true
    },
    // 给单元格附加 样式
    cellStyle: [String, Function],
    // 给表头的单元格附加 样式
    headerCellStyle: [String, Function],
    // 自定义行高
    rowHeight: {
      type: [Number, String],
      default: ''
    },
    treeConfig: {
      type: Object,
      default: () => { return { icon: 'ivu-icon-md-arrow-dropright', expandIcon: 'ivu-icon-md-arrow-dropdown', iconSize: 22 } }
    },
    // 根据内容自适应
    fitColumn: {
      type: Boolean,
      default: false
    },
    needFilter: {
      type: Boolean,
      default: false
    },
    inputFocus: {
      type: Boolean,
      default: false
    },
    enterSearch: {
      type: Boolean,
      default: false
    },
    printRowIndex: { // 是否打印序号
      type: Boolean,
      default: false
    },
    selectConfig: { // 配置 checkField属性 大量数据时不卡
      type: Object,
      default: () => {}
    },
    useCalculator: { // 是否启用计算器
      type: Boolean,
      default: false
    },
    lazyLoadY: { // 是否启用懒加载
      type: Boolean,
      default: true
    },
    showJunior: { // 输入汉字显示下级
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      fieldList: [],
      searchField: [],
      filterText: '',
      filterData: [],
      checkedData: [], // 勾选的数据
      loading: false,
      oldScrollTop: 0,
      scrollTimeId: null,
      dataOftableId: '',
      tableProps: {
        class: 'vxe-table-iview',
        // 'row-key': this.rowKey ? this.rowKey : null,
        size: 'mini',
        border: true,
        'highlight-current-row': true,
        'highlight-hover-row': true,
        stripe: this.stripe,
        'show-header': this.showHeader,
        resizable: true,
        /* showOverflow: 'tooltip', */
        'edit-config': { trigger: 'click', mode: 'cell', showStatus: this.showStatus, showIcon: false, activeMethod: this.activeCellMethod },

        /* showHeaderOverflow: 'tooltip', */
        // height: this.getTableHeight(),
        optimization: { animat: false, cloak: true, scrollY: this.lazyLoadY ? { gt: 30 } : null, scrollX: { rSize: 200 } },
        /* columns: this.columns,
        data: this.tableData, */
        /*         'row-class-name': function ({ seq, row, rowIndex, $rowIndex }) {
                  return row._selected ? 'row--current' : ''
                }, */
        'cell-class-name': function ({ row, column }) {
          if (column.field && this.treeField && column.field === this.treeField) {
            return 'tree-node'
          }
        }

      },
      customColumns: [],
      columns: [],
      tableData: [],
      tablePage: {
        currentPage: 1,
        pageSize: 50,
        totalResult: 0,
        autoTableMsg: true// 自动显示底部信息
      },
      scrollTop: 0,
      scrollLeft: 0,
      curentRow: {},
      tableHeight: 300,
      multHeader: false,
      // 根据数据是否有children判断数据是否树结构
      isTree: false,
      // 如果是翻页，则不用滚动事件
      turnToPage: false,
      toolbarWidth: 0,
      // 记忆滚动条位置
      scrollYStore: null,
      // 添加行高后，这里定义一个单元格样式的方法
      cellStyleFn: function (params) { return '' },
      useOutScrollY: false,
      nodataPath: '/img/nodata.png'
    }
  },
  computed: {
    toolbarId () {
      return 'dt' + this.fieldList.join('-')
    },
    showFooter () {
      return !!this.footerMethod
    },
    contextMenu () {
      return {
        header: {
          options: [[]]
        },
        body: {
          options: this.bodyContextMenus
        },
        visibleMethod: this.ContextVisibleMethod
      }
    },
    tableClass () {
      return [this.multHeader ? 'vuc-datatable-mult-header' : '']
    },
    toolbarStyles () {
      return this.tabs && this.tabs.length > 0 ? 'top:0px' : 'left:0px'
    },
    searchStyles () {
      return `position:relative;margin-left:5px;display:inline-block;`
    },
    istreeStyle () {
      if (this.treeField && this.tabs.length === 0 && this.toolbar.length === 1 && this.toolbar[0] === 'search' && !this.$slots.default) {
        return `width:100%;`
      }
      return ``
    },
    tabStyle () {
      return `width:calc(100% - ${this.toolbarWidth}px);`
    }
  },
  watch: {
    editable () {
      this.setEditConfig(this.editable)
    },
    height () {
      this.setTableHeight(this.height)
      this.setTableOptimization()
      this.afterUpdateTableData()
    },
    tableId () {
      if (this.tableId) {
        this.dataOfTableId = this.tableId
      }
    }
  },
  mixins: [api, editor, IO],
  methods: {
    /// ///////
    setTableHeight (height) {
      let tableHeight = height || (window.innerHeight - this.$el.offsetTop)
      if (this.showToolbar) {
        tableHeight = tableHeight - 52
      }

      if (this.showPager) {
        tableHeight = tableHeight - 52
      }
      // 搜索框聚焦
      if (this.inputFocus) {
        setTimeout(() => {
          this.$refs.tableInput.focus({
            cursor: 'all'
          })
        }, 500)
      }
      this.tableHeight = tableHeight < 0 ? 300 : tableHeight
    },
    setTableOptimization () {
      if (this.height !== 'auto') {
        const rowHeight = this.getRowHeight()
        const ySize = Math.ceil(this.height / rowHeight) + 10
        // this.tableProps.optimization = { scrollY: { gt: ySize, oSize: ySize, rSize: ySize } }
        this.tableProps.optimization = { animat: false, cloak: true, scrollY: this.lazyLoadY ? { gt: ySize, rHeight: rowHeight } : null, scrollX: { rSize: 2000 } }
        if (this.treeField || (!!window.ActiveXObject || 'ActiveXObject' in window)) {
          this.tableProps.showOverflow = 'tooltip'
        }
        /* 搜索后显示html
        if (!this.cellBreakWord) {
          this.tableProps.showOverflow = 'tooltip'
        }
        */
      }
      // 通过cellStyle设置行高
      this.cellStyleFn = (params) => {
        const { cellStyle, rowHeight } = this
        let cstyle = cellStyle ? (XEUtils.isFunction(cellStyle) ? cellStyle(params) : cellStyle) : ''

        if (rowHeight && rowHeight > 23 && this.size === 'mini') {
          const cellSpan = (rowHeight - 24) / 2
          return `padding:${cellSpan}px 0 !important;${cstyle}`
        }
        return cstyle
      }
    },
    getRowHeight () {
      if (this.rowHeight && this.size === 'mini') {
        return this.rowHeight
      }
      const rowHeight = { medium: 44, small: 40, mini: 36 }[this.size]
      return rowHeight
    },
    // 排序规则
    defaultSortMethod ({ data, column, property, order, $table }) {
      data.sort(function (a, b) {
        let aVal = a[property] || ''
        let bVal = b[property] || ''
        if (column.type === 'Number') {
          aVal = Number(aVal)
          bVal = Number(bVal)
        }
        if (order === 'asc') {
          return aVal > bVal ? 1 : -1
        } else {
          return aVal < bVal ? 1 : -1
        }
      })
    },
    setEditConfig () {
      if (this.editable) {
        // this.tableProps['edit-config'] = { trigger: 'click', mode: 'cell', showStatus: this.showStatus, showIcon: false, activeMethod: this.activeCellMethod }
        this.tableProps['mouse-config'] = { selected: true }
        this.tableProps['keyboard-config'] = { isArrow: this.isArrow, isDel: true, isTab: true, isEdit: true, enter2right: this.enter2right, keydownMethod: this.keydownMethodBase }
      } else {
        // delete this.tableProps['edit-config']
        // delete this.tableProps['keyboard-config']
        this.tableProps['keyboard-config'] = { isArrow: this.isArrow, keydownMethod: this.keydownMethodBase }
        delete this.tableProps['mouse-config']
      }
      /// ///////////
      this.setTableOptimization()
      //
      if (!this.showCheckbox) {
        this.tableProps['row-class-name'] = function ({ seq, row, rowIndex, $rowIndex }) {
          return row._selected ? 'row--current' : ''
        }
      }
    },
    activeCellMethod ({ row, rowIndex, column, columnIndex }) {
      if (!this.editable) {
        return false
      }
      if (this.activeMethod) {
        return this.activeMethod({ row, rowIndex, column, columnIndex })
      }
      return true
    },
    /// /////
    updatePagerInfo (msg) {
      this.tablePage.autoTableMsg = !msg
      if (!this.showPager) return false
      let info = ''
      /*       if (!this.showInfo) {
              msg = ''
            } */
      setTimeout(() => {
        if (!msg && this.showInfo) {
          const checkedData = this.getCheckedData()
          let sumChecked = 0.0
          let sum = 0.0
          let tableData = this.getTableData().visibleData || []

          for (let i = 0, len = tableData.length; i < len; i++) {
            sum += Number(tableData[i][this.sumField])
          }
          for (let i = 0, len = checkedData.length; i < len; i++) {
            sumChecked += Number(checkedData[i][this.sumField])
          }
          info =
            '<span>共' +
            tableData.length +
            '条数据' +
            (this.sumField
              ? ' 合计金额：' + XEUtils.commafy(sum, { fixed: 2 })
              : '') +
            '</span>'
          if (checkedData.length > 0) {
            info =
              info +
              '<span style="margin-left:20px;">已选择' +
              checkedData.length +
              '条数据' +
              (this.sumField
                ? ' 合计金额：' + XEUtils.commafy(sumChecked, { fixed: 2 })
                : '') +
              '</span>'
          }
        } else if (this.showInfo) {
          info = msg
        } else {
          info = ''
        }
        if (this.$refs.tablePager) {
          this.$refs.tablePager.setPagerInfo(info)
        }
      }, 30)
    },

    /// ///
    // 树型取真实数据
    getTreeRealData (row) {
      const text = this.$utils.text
      const rowKey = this.rowKey
      let findNode = function (rw) {
        if (rw[rowKey] && text(row[rowKey]) === text(rw[rowKey])) {
          return clonedeep(rw)
        }
        let tmpRow
        if (rw.children && rw.children.length > 0) {
          for (let j = 0, len = rw.children.length; j < len; j++) {
            let r = rw.children[j]
            if (r[rowKey] && text(row[rowKey]) === text(r[rowKey])) {
              tmpRow = clonedeep(r)
              break
            }
            if (r.children && r.children.length > 0) {
              tmpRow = findNode(r)
              if (tmpRow) break
            }
          }
        }
        return tmpRow
      }
      let realRow
      for (let i = 0, len = this.tableData.length; i < len; i++) {
        if (this.tableData[i].level === 0) {
          realRow = findNode(this.tableData[i])
          if (realRow) {
            break
          }
        }
      }
      return realRow
    },
    // 取真实数据，过滤后有样式
    getRealData (row) {
      if (!row) {
        return false
      }
      const text = this.$utils.text
      if (this.filterText !== '') {
        if (this.treeField && this.treeField !== '') {
          return this.getTreeRealData(row)
        }
        const ipos = this.tableData.findIndex(item => text(item[this.rowKey]) === text(row[this.rowKey]))

        if (ipos >= 0) {
          return clonedeep(this.tableData[ipos])
        }
      }
      return row
    },
    // 取真实数据，过滤后有样式;需要更多信息
    getBaseRow (row) {
      let { baseRow = row, rowIndex } = [null, -1]
      if (this.filterText !== '') {
        rowIndex = this.tableData.findIndex(item => item[this.rowKey] === row[this.rowKey])
        if (rowIndex >= 0) {
          baseRow = this.tableData[rowIndex]
        }
      }
      return { row: baseRow, rowIndex }
    },
    handleEditClosed ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }) {
      let baseRowInfo = this.getBaseRow(row)
      if (baseRowInfo.rowIndex > -1 && column.property && column.property !== '') {
        baseRowInfo.row[column.property] = row[column.property]
      }
      this.$emit('on-edit-closed', { row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell })
    },
    handleEditActived ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }) {
      this.$emit('on-edit-actived', { row, rowIndex, column, columnIndex, cell })
    },
    handleSelected ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event) {
      this.hideTooltip()
      if (column.type === 'selection') {
        return false
      }
      this.curentRow = {
        row: this.getRealData(row),
        rowIndex,
        $rowIndex,
        column,
        columnIndex,
        $columnIndex,
        cell
      }
      if (row._selected) {
        this.$emit('on-select', this.curentRow)
        return false
      }
      if (this.curentRow && this.curentRow.row) {
        this.curentRow.row._selected = false
      }
      row._selected = true
      this.$emit('on-select', this.curentRow || {})
      this.$emit('on-select-change', this.curentRow || {})
    },
    handleOnCellSelected ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event) {
      this.curentRow = {
        row: this.getRealData(row),
        rowIndex,
        $rowIndex,
        column,
        columnIndex,
        $columnIndex,
        cell
      }
      this.setActiveCell(row, column.property)
      this.$emit('on-cell-selected', this.curentRow, event)
    },
    handleChange ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event) {
      this.curentRow = {
        row: this.getRealData(row),
        rowIndex,
        $rowIndex,
        column,
        columnIndex,
        $columnIndex,
        cell
      }
      if (event && event.type === 'keydown' && event.key === 'Enter') {
        this.tableData = this.$refs.xTable.getTableData().fullData
        if (rowIndex === this.tableData.length - 1) {
          // 防止回车新增的行无法上下移动光标
          this.$refs.xTable.loadData(this.tableData)
        }
      }
      this.$emit('current-change', this.curentRow, event)
    },
    handleDblClick ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event) {
      // 如果是buttonEdit,且使用默认多文本编辑时弹出查看2021-12-05
      if (column.editRender && column.editRender.name === 'buttonedit' && !column.editRender.props.options.customComponent) {
        this.showCellTextareaModal({ ...column.editRender.props.options, value: row[column.property] })
        return false
      }
      if (
        event.target.matches('input[type="checkbox"]') ||
        event.target.matches('.checkbox--icon')
      ) {
        return false
      }
      this.hideTooltip()
      row._selected = true
      this.curentRow = {
        row: this.getRealData(row),
        rowIndex,
        $rowIndex,
        column,
        columnIndex,
        $columnIndex,
        cell
      }
      this.$emit('on-dblclick', this.curentRow)
    },
    handleChecked ({ selection, checked, row }, event) {
      this.curentRow = this.getRealData(row)
      this.$emit('on-checked', { selection, checked, row: this.curentRow })
      this.$emit('on-selectAll', { selection, checked, row: this.curentRow }) // table-select多选时传入数组
      this.updatePagerInfo()
      // 更新选中行
      if (this.rowKey) {
        const rowKey = this.rowKey
        let selections = row ? [row] : selection
        if (!row && selection.length === 0) {
          selections = this.getTableData().fullData
        }
        // if (selections)
        setTimeout(() => {
          // 取消
          if (checked === false) {
            // 如果有 row 则表示单选取消
            if (row) {
              const idx = this.checkedData.findIndex(item => item[rowKey] === row[rowKey])
              if (idx !== -1) {
                this.checkedData.splice(idx, 1)
              }
            } else {
              this.checkedData = []
            }
          } else if (row) { // 单条选择
            if (row[rowKey] !== undefined && row[rowKey] !== null && !this.checkedData.find(item => item[rowKey] === row[rowKey])) {
              this.checkedData.push(row)
            }
          } else { // 全选
            const map = new Map()
            //
            selections = this.getTableData().visibleData
            //
            this.checkedData.forEach(item => {
              map.set(item[rowKey], item)
            })
            selections.forEach((item) => {
              map.set(item[rowKey], item)
            })
            this.checkedData = [...map.values()]
          }
          // for (let i = 0, len = selections.length; i < len; i++) {
          //   const select = this.getRealData(selections[i])// this.filterText === '' ? row : JSON.parse(text(JSON.stringify(row)))
          //   if (select[rowKey] === undefined) {
          //     return false
          //   }

          //   const idx = this.checkedData.findIndex(item => text(item[rowKey]) === text(select[rowKey]))
          //   if (checked && idx === -1) {
          //     this.checkedData.push(select)
          //   } else if (!checked && idx >= 0) {
          //     this.checkedData.splice(idx, 1)
          //   }
          // }
        }, 0)
      }
    },
    handleHeaderContext ({ type, column, columnIndex, cell }, event) {
      // 提供禁用表头右键功能
      if (this.hideHeaderContext) {
        return false
      }
      const _this = this
      const columns = this.columns.filter(item => !['index', 'selection', 'tools'].includes(item.type) && item.title && item.title.trim() !== '')
      const size = columns.length
      let iCols = 2
      if (size > 24) iCols = 4
      else if (size > 16) iCols = 3
      const iRows = Math.ceil(size / iCols)
      const width = 200 * iCols + 16
      const height = 31 * iRows + 16
      const winWidth = window.innerWidth
      const winHeight = window.innerHeight
      const positionX = event.x + width > winWidth - 10 ? winWidth - 10 - width : event.x
      const positionY = event.y + height > winHeight - 10 ? winHeight - 10 - height : event.y
      // console.log(this.$refs.xTable.autoColumnWidth)
      const tipInstance = _this.$vuctip.tip({
        target: event.target, // tip 的参考元素
        width: width,
        duration: 100,
        positionX: positionX - 5,
        positionY: positionY - 5,
        // container: document.querySelector('body'),
        arrowsSize: 0,
        placements: ['bottom'],
        customProps: {
          customs: _this.columns,
          autoColumnWidth: _this.$refs.xTable.autoColumnWidth
        },
        show: (headConfig) => {
          if (headConfig) {
            setTimeout(() => {
              headConfig.updateVisibleCols()
            }, 10)
          }
        },
        hide: () => {
          tipInstance.destroy()
        },
        // 用于监听自定义组件的 emit 事件
        customListeners: {
          handleVisibleColumn ({ column, columns }) {
            // _this.columns = [..._this.columns, ...columns]
            setTimeout(() => {
              _this.handleVisibleColumn(column)
            })
          },
          handleLockedColumn ({ column, val, columns }) {
            // _this.columns = [..._this.columns, ...columns]
            setTimeout(() => {
              _this.handleLockedColumn(column, val)
            })
          },
          handleOutsideClick () {
            // tipInstance.hiddenTip()
            tipInstance.destroy()
          },
          refreshColumns (columns) {
            _this.columns = columns
            setTimeout(async () => {
              const customs = await _this.updateCustom(columns)
              _this.handleReloadColumn(customs)
            })
          },
          setColumnAutoWidth (autoColumnWidth) {
            setTimeout(() => {
              _this.setColumnAutoWidth(autoColumnWidth)
            }, 300)
            // tipInstance.hiddenTip()
            tipInstance.destroy()
          }
        },
        customComponent: VucDatatableHeaderConfig
      })
      event.stopPropagation()
      return tipInstance
    },
    handleCellMouseenter ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event) {
      // console.log(row, cell.getBoundingClientRect(), event, event.target.getBoundingClientRect())
      this.$emit('cell-mouseenter', { row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event)
    },
    handleCellMouseleave ({ row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event) {
      this.$emit('cell-mouseleave', { row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, cell }, event)
    },
    handleResizableChange ({ $rowIndex, column, columnIndex, $columnIndex, fixed, isHidden }) {
      const idx = this.customs.findIndex(col => col.field === column.property)
      if (idx >= 0) {
        this.customs[idx].width = column.renderWidth
        // 将宽度也调整，打印时可以固定
        column.width = column.renderWidth
        if (this.dataOfTableId) {
          localStorage.setItem(this.dataOfTableId, JSON.stringify(this.customs))
        }
        this.$emit('on-columns-change', { tableId: this.dataOfTableId, customs: this.customs })
      }
    },
    /// ///
    handleTabClick (name) {
      this.$emit('on-tab-click', name)
    },
    handleToolBarClick (name) {
      if (name === 'print') {
        this.doPrint()
      } else if (name === 'imp') {
        this.importXLSX()
      } else if (name === 'exp') {
        this.exportXLSX()
      } else {
        this.$emit('on-toolbar-click', name)
      }
    },
    /// /
    handlePageChange ({ currentPage, pageSize }) {
      this.turnToPage = true
      this.tablePage.currentPage = currentPage
      this.tablePage.pageSize = pageSize === -1 ? 100000 : pageSize
      this.scrollToRow(
        (this.tablePage.currentPage - 1) * this.tablePage.pageSize
      )
      const timeId = setTimeout(() => {
        clearTimeout(timeId)
        this.turnToPage = false
      }, 600)
    },
    handleTableScroll ({ type, fixed, scrollTop, scrollLeft, isX, isY, $table }, event) {
      // console.log({ type, fixed, scrollTop, scrollLeft, isX, isY }, event)
      if (this.scrollTimeId) {
        clearTimeout(this.scrollTimeId)
        this.scrollTimeId = null
      }

      if (this.turnToPage) return false
      this.scrollYStore = { ...this.$refs.xTable.scrollYStore }
      this.scrollTop = this.useOutScrollY ? this.scrollTop : scrollTop
      this.scrollLeft = scrollLeft
      if (this.treeField && this.treeField !== '') {
        // 树不需要分页
        return false
      }

      const rowHeight = this.getRowHeight()
      const startIndex = Math.ceil(scrollTop / rowHeight)
      const currentPage = Math.floor(startIndex / this.tablePage.pageSize) + 1
      const table = this.getTableData()
      let { fullData } = table
      if (this.treeField && this.treeField !== '') {
        fullData = table
      }
      if (!fullData || fullData.length === 0) {
        this.tablePage.currentPage = 1
      }
      const total = fullData.length
      if (startIndex + this.tablePage.pageSize > total) {
        const lastNum = Math.floor(total / this.tablePage.pageSize) + 1
        if (lastNum > this.tablePage.currentPage) {
          this.tablePage.currentPage = lastNum
        }
      } else {
        this.tablePage.currentPage = currentPage
      }
      this.scrollTimeId = setTimeout(() => {
        if (this.oldScrollTop - scrollTop > 10) {
          this.$refs.xTable.scrollTo(scrollLeft, scrollTop + 1)
          this.$refs.xTable.scrollTo(scrollLeft, scrollTop - 1)
        } else if (scrollTop - this.oldScrollTop > 10) {
          this.$refs.xTable.scrollTo(scrollLeft, scrollTop - 1)
          this.$refs.xTable.scrollTo(scrollLeft, scrollTop + 1)
        }
        this.oldScrollTop = scrollTop
      }, 1000)
    },
    handleContextMenuClickEvent ({ menu, row, column }) {
      this.$emit('context-menu-click', { menu, row, column })
    },
    handleSortChange (params) {
      this.$emit('sort-change', params)
    },
    handleFilterChange (params) {
      if (this.tablePage.autoTableMsg) {
        this.updatePagerInfo()
      }
      const tableData = this.getTableData().visibleData
      this.tablePage.currentPage = 1
      this.tablePage.totalResult = tableData.length
      // 设置显示数据的勾选状态
      if (this.checkedData.length && tableData.length) {
        const set = new Set([...this.checkedData])
        const selectList = [] // 选择的列表
        const deSelectList = [] // 取消选择的列表
        tableData.forEach(item => {
          if (set.has(item)) {
            selectList.push(item)
          } else {
            deSelectList.push(item)
          }
        })
        this.$refs.xTable.setSelection(selectList, true)
        this.$refs.xTable.setSelection(deSelectList, false)
      }
      this.$emit('filter-change', params)
      // 刷新一下
      const $table = this.$refs.xTable
      const scrollTop = this.scrollTop
      const scrollLeft = this.scrollLeft
      const timeId = setTimeout(() => {
        $table.scrollTo(scrollLeft - 1, scrollTop - 1)
        $table.refreshColumn()
        $table.updateScrollYSpace()
        $table.recalculate()
        clearTimeout(timeId)
      }, 100)
    },
    handleRangeEnd () {
      this.checkedData = this.$refs.xTable.getSelectRecords()
    },
    /// //////////
    hideTooltip () {
      const tooltipEls = document.getElementsByClassName('vxe-table--tooltip-wrapper')
      for (let i = 0, len = tooltipEls.length; i < len; i++) {
        const el = tooltipEls[i]
        el.classList.remove('is--visible')
      }
    },

    scrollToRow (irow) {
      this.$refs.xTable.scrollToRow(this.$refs.xTable.getData(irow))
    },
    // 多行文本弹出，需要编辑框为buttonedit
    showCellTextareaModal (options) {
      options.customComponent = VucTextarea
      options.showFooter = false
      let opt = {
        message: '',
        title: options.title || '请选择',
        resize: true,
        showFooter: false,
        escClosable: true,
        slots: {
          default: ({ $modal, $custom }, h) => {
            return [
              h(options.customComponent, {
                props: { ...options.customProps, value: options.value || '', editable: false },
                on: Object.assign(options.customEvents || {}, {
                  close () {
                    $modal.close()
                  }
                })
              })
            ]
          }
        }
      }

      if (options.width) {
        opt.width = options.width
      }
      if (options.height) {
        opt.height = options.height
      }
      if (options.minWidth) {
        opt.minWidth = options.minWidth
      }
      if (options.minHeight) {
        opt.minHeight = options.minHeight
      }
      if (options.hasOwnProperty('resize')) {
        opt.resize = options.resize
      }
      if (options.hasOwnProperty('showFooter')) {
        opt.showFooter = options.showFooter
      }
      if (options.hasOwnProperty('escClosable')) {
        opt.escClosable = options.escClosable
      }
      if (options.hasOwnProperty('title')) {
        opt.title = options.title
      }
      if (options.id) {
        opt.id = options.id
      }
      this.$modal(opt)
    },
    /// //////////////
    async setFieldList (columns) {
      for (let idx = 0, len = columns.length; idx < len; idx++) {
        let col = columns[idx]
        /// /使用slots后不筛选
        if (
          col.field &&
          col.field !== ' ' &&
          col.field !== '' &&
          (!col.slots || !col.hasOwnProperty('canFilter') || col.canFilter)
        ) {
          if (this.fieldList.indexOf(col.field) === -1) {
            this.fieldList.push(col.field)
          }
        }
        if (col.children) {
          this.setFieldList(col.children)
        }
      }
      return this.fieldList
    },
    // 匹配两个值
    compareText (str, subStr) {
      return this.startFilter ? XEUtils.startsWith(str, subStr) : str.indexOf(subStr) > -1
    },
    getTreeFilterData (filterText) {
      let _this = this
      let filterData = []
      let filter = function (row) {
        let hasNode = false
        if (_this.showJunior) {
          if (_this.searchField.some(key => {
            const val = XEUtils.toString(row[key]).toLowerCase()
            return _this.compareText(val, filterText)
          })) {
            hasNode = true
            row._expand = true
            if (row.children && row.children.length > 0) {
              for (let k = row.children.length - 1; k >= 0; k--) {
                filterData.unshift(row.children[k])
                row.children[k]._expand = true
              }
            }
            return hasNode
          }
        }
        if (row.children && row.children.length > 0) {
          for (let j = row.children.length - 1; j >= 0; j--) {
            let r = row.children[j]
            if (r.children && r.children.length > 0) {
              if (filter(r)) {
                hasNode = true
                r._expand = true
                filterData.unshift(r)
                continue
              }
            }
            if (_this.searchField.some(key => {
              const val = XEUtils.toString(r[key]).toLowerCase()
              // return val.indexOf(filterText) > -1
              return _this.compareText(val, filterText)
            })) {
              hasNode = true
              r._expand = true
              filterData.unshift(r)
            } else {
              row.children.splice(j, 1)
            }
          }
        } else if (_this.searchField.some(key => {
          const val = XEUtils.toString(row[key]).toLowerCase()
          // return val.indexOf(filterText) > -1
          return _this.compareText(val, filterText)
        })) {
          hasNode = true
          // row._expand = true
        }
        return hasNode
      }
      let tableData = clonedeep(this.tableData)
      for (let i = tableData.length - 1; i >= 0; i--) {
        let row = tableData[i]
        if (row.level === 0) {
          if (filter(row)) {
            row._expand = true
            filterData.unshift(row)
          }
        }
      }
      console.log(filterData, 567567)
      return filterData
    },
    transSpecialReg (text) {
      const specialList = ['\\', '(', ')', '*', '+', '.', '[', ']', '?', '^', '{', '}', '|', '-']
      let regex = text
      for (let i = 0, len = specialList.length; i < len; i++) {
        const s = specialList[i]
        const trans = '\\' + s
        regex = regex.replace(s, trans)
      }
      return regex
    },
    async getFilterData () {
      let filterText = XEUtils.toString(this.filterText).trim().toLowerCase()

      if (filterText) {
        // 过滤匹配字段
        if (this.searchField.length === 0) {
          this.searchField =
            this.searchColumns && this.searchColumns.length > 0
              ? this.searchColumns
              : await this.setFieldList(this.columns)
        }
        let numberField = []
        this.columns.forEach(item => {
          if (item.type === 'Number' || item.type === 'InputMoney' || item.type === 'DOUBLE') {
            numberField.push(item.field)
          }
          if (item.children) {
            item.children.forEach(c => {
              if (c.type === 'Number' || c.type === 'InputMoney' || c.type === 'DOUBLE') {
                numberField.push(c.field)
              }
            })
          }
        })
        let rest
        if (this.treeField && this.treeField !== '') {
          rest = this.getTreeFilterData(filterText)
        } else {
          rest = this.tableData.filter(item =>
            this.searchField.some(
              key => {
                const val = XEUtils.toString(item[key]).toLowerCase()
                // return val.indexOf(filterText) > -1
                if ((Number(item[key]) || Number(item[key]) === 0) && numberField.includes(key)) {
                  return this.compareText(val, filterText) || this.compareText(Number(item[key]).toFixed(2), filterText)
                } else {
                  return this.compareText(val, filterText)
                }
              }
            )
          )
        }
        // 标亮
        filterText = this.transSpecialReg(filterText)
        let filterRE = this.startFilter ? new RegExp(filterText) : new RegExp(filterText, 'gi')

        rest.map(row => {
          let item = Object.assign({}, row)
          this.searchField.forEach(key => {
            item[key] = XEUtils.toString(item[key]).replace(
              filterRE,
              match => `<span class="keyword-lighten">${match}</span>`
            )
          })
          return item
        })
        return rest
      }
      return this.tableData
    },
    async doFilter (val, isFree) {
      if (this.enterSearch) {
        // 回车才进行搜索
        if (val === '') {
          this.filterText = val
        }
        return false
      }
      if (this.filterText !== val) {
        this.filterText = val
      }
      let filterData = await this.getFilterData()
      if (this.showCheckbox) {
        await this.setNodePath(filterData)
      }
      this.filterData = filterData
      setTimeout(() => {
        // 阻断 vue 对大数组的双向绑定，大数据性能翻倍提升
        if (this.$refs.xTable) {
          // this.$refs.xTable.loadData(filterData)
          this.$refs.xTable.loadTableData(filterData).then(() => {
            if (filterData.length === 0 || this.filterText === '') {
              this.$refs.xTable.clearCurrentRow()
            }
            this.afterUpdateTableData()
            // 自由录入查询时不需要选中第一个
            if (!isFree) {
              this.selectFirstRow(filterData)
            }

            if (this.showCheckbox) {
              this.updateTreeCheckedStatus(filterData)
            }
            // 更新左下角信息2020-02-17
            this.updatePagerInfo()
            this.tablePage.totalResult = filterData.length

            if (this.checkedData.length > 0) {
              const rowKey = this.rowKey
              const set = new Set()
              this.checkedData.forEach((item) => {
                set.add(item[rowKey])
              })
              setTimeout(() => {
                this.setRowsChecked((item) => {
                  const keyVal = item[rowKey]
                  return set.has(keyVal)
                }, true)
              }, 300)
            }
            this.$emit('on-input-filter', this.filterText)
          })
        }
      }, 30)
      return filterData
    },
    async doInputEnter () {
      this.$emit('on-input-keydown-enter')
      if (!this.enterSearch) {
        // 回车才进行搜索
        return false
      }
      let filterData = await this.getFilterData()
      if (this.showCheckbox) {
        await this.setNodePath(filterData)
      }
      this.filterData = filterData
      setTimeout(() => {
        // 阻断 vue 对大数组的双向绑定，大数据性能翻倍提升
        if (this.$refs.xTable) {
          // this.$refs.xTable.loadData(filterData)
          this.$refs.xTable.loadTableData(filterData).then(() => {
            if (filterData.length === 0 || this.filterText === '') {
              this.$refs.xTable.clearCurrentRow()
            }
            this.afterUpdateTableData()
            if (this.showCheckbox) {
              this.updateTreeCheckedStatus(filterData)
            }
            this.updatePagerInfo()
            this.tablePage.totalResult = filterData.length

            if (this.checkedData.length > 0) {
              const rowKey = this.rowKey
              const set = new Set()
              this.checkedData.forEach((item) => {
                set.add(item[rowKey])
              })
              setTimeout(() => {
                this.setRowsChecked((item) => {
                  const keyVal = item[rowKey]
                  return set.has(keyVal)
                }, true)
              }, 300)
            }
            this.$emit('on-input-filter', this.filterText)
          })
        }
      }, 30)
      return filterData
    },
    /// /////////
    afterUpdateTableData (isInit) {
      const _this = this
      const $table = this.$refs.xTable
      if (!$table || !$table.$el) {
        return false
      }
      // const { startIndex, rowHeight } = this.scrollYStore || $table.scrollYStore
      $table.updateData()
      this.turnToPage = true
      const timeId = setTimeout(() => {
        // $table.scrollYStore.startIndex = 0
        $table.scrollTo(0, _this.scrollTop - 1)
        $table.refreshColumn()
        $table.updateScrollYSpace()
        $table.recalculate()
        this.turnToPage = false
        clearTimeout(timeId)
      }, 100)
      if (!isInit) {
        // console.log($table.scrollYStore,_this.scrollTop,startIndex * rowHeight)
        const timeId1 = setTimeout(() => {
          // $table.scrollTo(_this.scrollLeft, startIndex * rowHeight+1)
          $table.scrollTo(_this.scrollLeft, _this.scrollTop + 1)
          clearTimeout(timeId1)
        }, 150)
      }
    },
    // 选择末级行,可用于过滤后选中
    async selectFirstRow (tableData) {
      const size = tableData.length
      if (size === 0 || this.filterText === '') {
        this.$refs.xTable.clearCurrentRow()
        this.$emit('on-select-change')
        return
      }
      const filterText = this.filterText
      let findRow = false
      let includeRow
      for (let i = 0; i < size; i++) {
        const row = tableData[i]
        findRow = this.searchField.some(key => {
          const val = XEUtils.toString(this.$utils.text(row[key])).toLowerCase()
          return XEUtils.startsWith(val, filterText) || XEUtils.startsWith(val, filterText.toLowerCase())
        })
        if (findRow) {
          this.setCurrentRow(row)
          break
        }
        const findIncludeRow = this.searchField.some(key => {
          const val = XEUtils.toString(row[key]).toLowerCase()
          return val.indexOf(filterText) >= 0
        })
        if (findIncludeRow) {
          includeRow = row
        }
        // 选中第一个符合条件的记录,从头匹配

        /*
        if (!row.children || row.children.length === 0) {
          this.setCurrentRow(row)
          break
        }
        */
      }
      if (!findRow && includeRow) {
        this.setCurrentRow(includeRow)
      }
    },
    toggleTreeExpand ({ row, expand }) {
      if (expand !== undefined) {
        if (row._expand === expand) return false
        row._expand = !expand
      } else {
        // 说明点击了小三角来展开下一级
        if (!row._expand) {
          // 此时row的_expand还没变成true，所以需要用false来判断
          this.$emit('on-dropdown-click', row)
        }
      }
      if (!row.children || row.children.length === 0) {
        return false
      }
      let tableData = this.filterText === '' ? this.tableData : this.filterData
      const rowKey = this.rowKey
      let rowIndex = -1
      if (this.filterText === '') {
        rowIndex = tableData.indexOf(row)
      } else {
        rowIndex = tableData.findIndex(item => this.$utils.text(item[rowKey]) === this.$utils.text(row[rowKey]))
      }
      if (rowIndex === -1) {
        return false
      }
      row = tableData[rowIndex]
      let newIndex = rowIndex + 1
      // this.tableData[newIndex - 1]._expand = row._expand
      // tableData[newIndex - 1]._expand = row._expand
      if (!row._expand) {
        for (let i = 0; i < row.children.length; i++) {
          let child = row.children[i]
          child._expand = false
          if (!this.filterText) {
            tableData.splice(newIndex + i, 0, child)
          } else {
            const idx = tableData.findIndex(node => this.$utils.text(node[this.rowKey]) === this.$utils.text(child[this.rowKey]))
            if (idx === -1) {
              tableData.splice(newIndex + i, 0, child)
            }
          }
        }
        // this.$refs.xTable.scrollTo(this.scrollLeft, this.scrollTop === 0 ? this.scrollTop + 1 : this.scrollTop - 1)
        tableData[rowIndex]._expand = true
      } else {
        let removeLen = 0
        let getLen = function (rw) {
          if (!rw.children || !rw._expand) return false
          removeLen += rw.children.length
          for (let i = 0, len = rw.children.length; i < len; i++) {
            let r = rw.children[i]
            if (r.children && r._expand) {
              getLen(r)
            }
          }
        }
        getLen(row)
        // console.log(removeLen, tableData)
        for (let i = 0; i < removeLen; i++) {
          tableData.splice(newIndex, 1)
        }
        tableData[rowIndex]._expand = false

        // this.$refs.xTable.loadData(tableData)
        // this.afterUpdateTableData()
      }
      this.scrollYStore = { ...this.$refs.xTable.scrollYStore }
      this.$refs.xTable.loadTableData(tableData).then(() => {
        this.afterUpdateTableData()
      })
      /*       row = tableData[rowIndex]
            row._expand = row.hasOwnProperty('_expand') ? !row._expand : true */
    },
    /// ////////////
    setTreeChecked ({ value, row, rowIndex, column, columnIndex }) {
      const { updateFullDataNodeCheced } = this
      row._checked = value
      row._indeterminate = false
      let setChecked = function (rw) {
        if (!rw.children) return false
        for (let i = 0, len = rw.children.length; i < len; i++) {
          let r = rw.children[i]
          r._checked = value
          r._indeterminate = false
          updateFullDataNodeCheced(r)
          if (r.children && r.children.length > 0) {
            setChecked(r)
          }
        }
      }
      if (this.linkage) {
        setChecked(row)
      }
      if (!row._path && row.level > 0) {
        this.setNodePath()
      }
      /// ///////
      if (this.linkage) {
        this.updateParentNodeChecked(row._path)
      }
      this.$refs.xTable.updateScrollYSpace()
      this.$refs.xTable.updateData()
      // 有过滤条件时需要去除标签
      if (this.filterText !== '') {
        row = JSON.parse(this.$utils.text(JSON.stringify(row)))
        this.updateFullDataNodeCheced(row)
      }
      this.$emit('on-checked', { row, checked: value })
      if (!this.showCheckbox) {
        if (value) {
          this.$refs.xTable.setCurrentRow(row)
          this.$emit('on-set-current-row', { row })
        } else {
          this.$refs.xTable.setCurrentRow()
        }
      }
    },
    getRootRowByIndex (idx, isUpdateFullData) {
      let tableData = (this.filterText === '' || isUpdateFullData) ? this.tableData : this.filterData
      let node
      let iroot = 0
      for (let i = 0, len = tableData.length; i < len; i++) {
        let tempNode = tableData[i]
        if (tempNode.level === 0) {
          if (iroot === parseInt(idx)) {
            node = tempNode
            break
          }
          iroot = iroot + 1
        }
      }
      return node
    },
    updateTreeCheckedStatus (treeData) {
      const size = treeData.length
      for (let i = size - 1; i >= 0; i--) {
        const row = treeData[i]
        if (row._checked) {
          this.updateParentNodeChecked(row._path)
        }
      }
    },
    // 同步原数据状态
    updateFullDataNodeCheced (row) {
      const { rowKey, $modal } = this
      if (!rowKey || rowKey === '' || !row.hasOwnProperty(rowKey)) {
        const msg = `树型展示要求正确的row-key:${rowKey}`
        $modal.message({ message: msg, status: 'warning' })
        return false
      }
      let findNode = function (rw) {
        if (row[rowKey] === rw[rowKey]) {
          return rw
        }
        let tmpNode
        if (rw.children && rw.children.length > 0) {
          for (let j = 0, len = rw.children.length; j < len; j++) {
            let r = rw.children[j]
            tmpNode = findNode(r)
            if (tmpNode) {
              break
            }
          }
        }
        return tmpNode
      }
      let fullNode
      for (let i = 0, len = this.tableData.length; i < len; i++) {
        let node = this.tableData[i]
        if (!node.hasOwnProperty('level') || node.level === 0) {
          fullNode = findNode(this.tableData[i])
          if (fullNode) {
            break
          }
        }
      }

      if (fullNode) {
        fullNode._checked = row._checked
        if (this.linkage) {
          this.updateParentNodeChecked(fullNode._path, true)
        }
      }
    },
    // 同步上级节点的状态
    updateParentNodeChecked (path, isUpdateFullData) {
      if (!path) return false
      let pNodes = []
      let pathList = path.toString().split('-')
      let pathLen = pathList.length - 1
      for (let i = 0; i < pathLen; i++) {
        const idx = pathList[i]
        if (i === 0) {
          pNodes.push(this.getRootRowByIndex(idx, isUpdateFullData))
        } else {
          pNodes.push(pNodes[i - 1].children[idx])
        }
      }
      for (let i = pathLen - 1; i >= 0; i--) {
        const node = pNodes[i]
        if (!node || !node.children || node.children.length === 0) continue
        let hasUnChecked = node.children.findIndex(row => {
          return !row._checked
        })
        let hasChecked = node.children.findIndex(row => {
          return row._checked || row._indeterminate
        })
        node._checked = hasUnChecked === -1
        node._indeterminate = hasUnChecked >= 0 && hasChecked >= 0
      }
    },
    setNodePath (tableData) {
      const { rowKey, $modal } = this
      if (!rowKey || rowKey === '') {
        return false
      }
      tableData = tableData || this.tableData
      let setPath = function (rw, path) {
        for (let i = 0, len = rw.children.length; i < len; i++) {
          let r = rw.children[i]
          if (!r.hasOwnProperty(rowKey)) {
            const msg = `树型展示要求正确的row-key:${rowKey}`
            $modal.message({ message: msg, status: 'warning' })
            continue
          }

          const rowIndex = tableData.findIndex(item => item[rowKey] === r[rowKey])
          if (rowIndex >= 0) {
            rw.children[i] = tableData[rowIndex]
          }

          r = rw.children[i]
          r._path = path + '-' + i

          if (r.children) {
            setPath(r, r._path)
          }
        }
      }
      let root = 0
      for (let i = 0, len = tableData.length; i < len; i++) {
        let node = tableData[i]
        if (node.children && node.children.length > 0 && node.level === 0) {
          node._path = ''
          setPath(node, root)
        }
        if (node.level === 0) {
          root = root + 1
        }
      }
    },
    // 展开所有行
    setAllRowExpansion (checked) {
      let fullData = []
      let expandRow = function (row) {
        if (row.children && row.children.length > 0) {
          for (let j = row.children.length - 1; j >= 0; j--) {
            let r = row.children[j]
            if (r.children && r.children.length > 0) {
              expandRow(r)
            }
            r._expand = true
            fullData.unshift(r)
          }
        }
      }
      let tableData = clonedeep(this.tableData)
      for (let i = tableData.length - 1; i >= 0; i--) {
        let row = tableData[i]
        if (checked) {
          if (row.level === 0) {
            expandRow(row)
            row._expand = true
            fullData.unshift(row)
          }
        } else {
          if (row.level !== 0) {
            tableData.splice(i, 1)
          }
        }
      }
      // this.afterUpdateTableData()
      this.tableData = checked ? fullData : tableData
      // this.$refs.xTable.loadData(this.tableData)
      this.$refs.xTable.loadTableData(this.tableData).then(() => {
        this.afterUpdateTableData()
      })
    },
    // 展开到指定级次
    expandLevel (level) {
      let fullData = []
      let expandRow = function (row) {
        if (row.level > level) {
          row._expand = false
          return false
        }
        if (row.children && row.children.length > 0) {
          for (let j = row.children.length - 1; j >= 0; j--) {
            let r = row.children[j]
            if (r.level > level) {
              r._expand = false
              fullData.unshift(r)
              continue
            }
            if (r.children && r.children.length > 0) {
              expandRow(r)
            }
            r._expand = true
            fullData.unshift(r)
          }
        }
      }
      let tableData = clonedeep(this.tableData)
      for (let i = tableData.length - 1; i >= 0; i--) {
        let row = tableData[i]
        if (row.level === 0) {
          expandRow(row)
          row._expand = true
          fullData.unshift(row)
        }
      }
      this.tableData = fullData
      this.$refs.xTable.loadTableData(this.tableData).then(() => {
        this.afterUpdateTableData()
      })
    },
    setRowExpansion (method, checked) {
      let curRow
      let findRow = function (row) {
        if (method(row)) {
          return row
        }
        if (row.children && row.children.length > 0) {
          let rs
          for (let j = 0, len = row.children.length; j < len; j++) {
            let r = row.children[j]
            r._path = row._path + '-' + j
            // if (r.children && r.children.length > 0) {
            rs = findRow(r)
            if (rs) {
              break
            }
            // }
          }
          return rs
        }
      }
      let tableData = this.tableData
      let rootIndex = 0
      for (let i = 0, len = tableData.length; i < len; i++) {
        let row = tableData[i]
        if (row.level === 0) {
          row._path = rootIndex
          curRow = findRow(row)
          if (curRow) {
            break
          }
          rootIndex = rootIndex + 1
        }
      }
      if (curRow && !checked) {
        this.toggleTreeExpand({ row: curRow, expand: true })
        this.afterUpdateTableData()
        return false
      }

      const newIndex = this.tableData.indexOf(curRow)
      if (newIndex >= 0) {
        const root = this.tableData[newIndex]
        if (!root._expand) {
          this.toggleTreeExpand({ row: root, expand: true })
        }
      } else if (curRow) {
        this.expandByPath(curRow._path, checked)
      }
      this.$refs.xTable.scrollTo(this.scrollLeft, this.scrollTop - 1)
      if (curRow) {
        this.$refs.xTable.scrollToRow(curRow)
      }
    },
    expandByPath (path, expanded) {
      if (!path) return false
      let pNodes = []
      let pathList = path.split('-')
      let pathLen = pathList.length
      for (let i = 0; i < pathLen; i++) {
        const idx = pathList[i]
        if (i === 0) {
          pNodes.push(this.getRootRowByIndex(idx))
        } else {
          if (pNodes[i - 1]) {
            pNodes.push(pNodes[i - 1].children[idx])
          }
        }
      }
      for (let i = 0; i < pathLen; i++) {
        const idx = pathList[i]
        let node
        if (i === 0) {
          node = this.getRootRowByIndex(idx)
        } else {
          if (pNodes[i - 1]) {
            node = pNodes[i - 1].children[idx]
          }
        }

        if (node && !node._expand) {
          this.toggleTreeExpand({ row: node, expand: true })
        }
      }
    },
    async keydownMethodBase ({ $table, row, rowIndex, column, columnIndex, event }) {
      // 上下左右箭头移动单元格
      if ((!row || !column) && !(['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key) && !event.shiftKey)) {
        // 排除一下特殊情况，例如表格里下拉树只有row没有column和index
        return false
      }
      // 粘贴带逗号千分位到inputnumber单元格时处理一下剪切板
      if (column && column.editRender && column.editRender.name === 'InputNumber' && event.key === 'v' && (event.metaKey || event.ctrlKey)) {
        // 处理粘贴板板数据，带逗号的千分位数字粘贴到InputNumberer 单元格时处理一下剪切板
        this.formatClipboard(event)
      }
      let columns = this.$refs.xTable.getColumns()
      let tableData = this.$refs.xTable.getTableData().fullData
      if (event.shiftKey && event.key === 'ArrowLeft' && columnIndex !== undefined && columns[columnIndex - 1]) {
        this.$refs.xTable.setActiveCell(row, columns[columnIndex - 1].property)
        return true
      } else if (event.shiftKey && event.key === 'ArrowRight' && columnIndex !== undefined && columns[columnIndex + 1]) {
        this.$refs.xTable.setActiveCell(row, columns[columnIndex + 1].property)
        return true
      } else if (event.shiftKey && event.key === 'ArrowUp' && rowIndex !== undefined && tableData[rowIndex - 1]) {
        this.$refs.xTable.setActiveCell(tableData[rowIndex - 1], column.property)
        this.setCurrentRow(tableData[rowIndex - 1])
        return true
      } else if (event.shiftKey && event.key === 'ArrowDown' && rowIndex !== undefined && tableData[rowIndex + 1]) {
        this.$refs.xTable.setActiveCell(tableData[rowIndex + 1], column.property)
        this.setCurrentRow(tableData[rowIndex + 1])
        return true
      } else if (this.useCalculator) {
        this.keydownMethodCalculator()
      } else if (this.keydownMethod) {
        return this.keydownMethod({ $table, row, rowIndex, column, columnIndex, event })
      }
      return true
    },
    keydownMethodCalculator ({ $table, row, rowIndex, column, columnIndex, event }) {
      // let { row, column } = event
      const _this = this
      if (column && column.editRender && column.editRender.name === 'InputNumber' && event.key === '+') {
        this.$modal({
          message: '',
          title: '计算器',
          resize: false,
          mask: false,
          width: 475,
          height: 315,
          showFooter: false,
          escClosable: false,
          zIndex: 9999,
          events: {
            show ({ $modal, $custom }, event) {
              // $custom.prepearExport()
            }
          },
          slots: {
            default: ({ $modal, $custom }, h) => {
              return [
                h(VucCalculator, {
                  props: {
                  },
                  on: {
                    ok (content) {
                      row[column.property] = content
                      $modal.close()
                      _this.setActiveCell(row, column.property)
                      return true
                    },
                    close () {
                      $modal.close()
                      return true
                    }
                  }
                })
              ]
            }
          }
        })
      } else {
        if (this.keydownMethod) {
          this.keydownMethod({ $table, row, rowIndex, column, columnIndex, event })
        }
        return true
      }
    },
    async formatClipboard (event) {
      // 处理粘贴板板数据，带逗号的千分位数字粘贴到InputNumberer 单元格时处理一下剪切板
      let text = ''
      if (window.clipboardData && window.clipboardData.getData) {
        // 如果有粘贴事件数据---老浏览器事件
        text = window.clipboardData.getData('text')
      } else {
        // 尝试使用现代新版浏览器 Clipboard API
        try {
          if (navigator.clipboard && navigator.clipboard.readText) {
            text = await navigator.clipboard.readText()
          }
        } catch (err) {
          console.warn('Clipboard API 访问被拒绝:', err)
        }
      }
      const cleanedText = text.replace(/,/g, '')
      document.execCommand('insertText', false, cleanedText)
    }
  },
  created () {
    if (this.tableId) {
      this.dataOfTableId = this.tableId
    }
    if (this.hasOwnProperty('rowKey')) {
      this.tableProps['row-key'] = this.rowKey
      // this.tableProps['row-id'] = this.rowKey
    }
    /* if (this.editable) {
      this.tableProps['edit-config'] = { trigger: 'click', mode: 'cell', showIcon: false }
    } */
    this.tableProps['edit-config'] = { trigger: 'click', mode: 'cell', showStatus: this.showStatus, showIcon: false, activeMethod: this.activeCellMethod }
    this.setEditConfig()
  },
  activated () {
    this.setTableHeight(this.height)
    this.setTableOptimization()
    this.afterUpdateTableData()
  },
  mounted () {
    this.$nextTick(() => {
      this.customColumns = this.customs
      this.setTableHeight(this.height)
      this.updatePagerInfo()
      if (!this.tabsMarginRight || this.tabsMarginRight === 0) {
        if (this.$refs.refToolbar && (this.toolbar.length > 0 || this.$slots.toolbar)) {
          const rect = this.$refs.refToolbar.getBoundingClientRect()
          this.toolbarWidth = rect.width + 20
        }
      } else {
        this.toolbarWidth = this.tabsMarginRight
      }
    })
  }
}

</script>
<style lang="less">
@import './vuc-datatable.less';
</style>
