import XEUtils from 'xe-utils'
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import { JVXETypes } from '@/components/jeecg/JVxeTable/jvxeTypes'
import VxeWebSocketMixins from '../mixins/vxe.web.socket.mixins'
import { initDictOptions } from '@/components/dict/JDictSelectUtil'

import { getRefPromise } from '../utils/vxeUtils'
import { getEnhancedMixins, replaceProps } from '../utils/cellUtils'

import JVxeToolbar from './JVxeToolbar'
import JVxeSubPopover from './JVxeSubPopover'
import JVxeDetailsModal from './JVxeDetailsModal'
import JVxePagination from './JVxePagination'
import { cloneObject, getVmParentByName, pushIfNotExist, randomString, simpleDebounce } from '@/utils/util'
import { UtilTools } from 'vxe-table/packages/tools/src/utils'
import { getNoAuthCols } from '@/utils/authFilter'

export default {
  name: 'JVxeTable',
  provide() {
    return {
      superTrigger: (name, event) => this.trigger(name, event)
    }
  },
  mixins: [VxeWebSocketMixins],
  components: {JVxeToolbar, JVxeSubPopover, JVxeDetailsModal, JVxePagination},
  props: {
    rowKey: PropTypes.string.def('id'),
    // 列信息
    columns: {
      type: Array,
      required: true
    },
    // 数据源
    dataSource: {
      type: Array,
      required: true
    },
    authPre: {
      type: String,
      required: false,
      default: ''
    },
    // 是否显示工具栏
    toolbar: PropTypes.bool.def(false),
    // 工具栏配置
    toolbarConfig: PropTypes.object.def(() => ({
      // prefix 前缀；suffix 后缀；
      slots: ['prefix', 'suffix'],
      // add 新增按钮；remove 删除按钮；clearSelection 清空选择按钮；collapse 展开收起
      btns: ['add', 'remove', 'clearSelection'],
    })),
    // 是否显示行号
    rowNumber: PropTypes.bool.def(false),
    // 是否可选择行
    rowSelection: PropTypes.bool.def(false),
    // 选择行类型
    rowSelectionType: PropTypes.oneOf(['checkbox', 'radio']).def('checkbox'),
    // 是否可展开行
    rowExpand: PropTypes.bool.def(false),
    // 展开行配置
    expandConfig: PropTypes.object.def(() => ({})),
    // 页面是否在加载中
    loading: PropTypes.bool.def(false),
    height: PropTypes.instanceOf([Number, String]).def('auto'),
    // 最大高度
    maxHeight: {
      type: Number,
      default: () => null,
    },
    // 要禁用的行 TODO 未实现
    disabledRows: PropTypes.object.def(() => ({})),
    // 是否禁用全部组件
    disabled: PropTypes.bool.def(false),
    // 是否可拖拽排序 TODO 仅实现上下排序，未实现拖拽排序（可能无法实现或较为困难）
    dragSort: PropTypes.bool.def(false),
    // 排序字段保存的Key
    dragSortKey: PropTypes.string.def('orderNum'),
    // 大小，可选值有：medium（中）、small（小）、mini（微）、tiny（非常小）
    size: PropTypes.oneOf(['medium', 'small', 'mini', 'tiny']).def('medium'),
    // 是否显示边框线
    bordered: PropTypes.bool.def(false),
    // 分页器参数，设置了即可显示分页器
    pagination: PropTypes.object.def(() => ({})),
    // 点击行时是否显示子表单
    clickRowShowSubForm: PropTypes.bool.def(false),
    // 点击行时是否显示主表单
    clickRowShowMainForm: PropTypes.bool.def(false),
    // 是否点击选中行，优先级最低
    clickSelectRow: PropTypes.bool.def(false),
    // 是否开启 reload 数据效果
    reloadEffect: PropTypes.bool.def(false),
    // 校验规则
    editRules: PropTypes.object.def(() => ({})),
    // 是否异步删除行，如果你要实现异步删除，那么需要把这个选项开启，
    // 在remove事件里调用confirmRemove方法才会真正删除（除非删除的全是新增的行）
    asyncRemove: PropTypes.bool.def(false),
    // 是否一直显示组件，如果为false则只有点击的时候才出现组件
    // 注：该参数不能动态修改；如果行、列字段多的情况下，会根据机器性能造成不同程度的卡顿。
    alwaysEdit: PropTypes.bool.def(false),
    // 联动配置，数组，详情配置见文档
    linkageConfig: PropTypes.array.def(() => []),
  },
  data() {
    return {
      isJVXETable: true,
      // caseId，表格唯一标识
      caseId: `_j-vxe-${randomString(8)}_`,
      // 内置columns
      _innerColumns: [],
      // 内置 EditRules
      _innerEditRules: [],
      // 记录滚动条位置
      scroll: {top: 0, left: 0},
      // 当前是否正在滚动
      scrolling: false,
      // vxe 默认配置
      defaultVxeProps: {
        'row-id': this.rowKey,
        // 高亮hover的行
        'highlight-hover-row': true,
        // 溢出隐藏并显示tooltip
        'show-overflow': true,
        // 表头溢出隐藏并显示tooltip
        'show-header-overflow': true,
        'show-footer-overflow': true,
        // 可编辑配置
        'edit-config': {trigger: 'click', mode: 'cell', showStatus: true},
        'expand-config': {
          iconClose: 'ant-table-row-expand-icon ant-table-row-collapsed',
          iconOpen: 'ant-table-row-expand-icon ant-table-row-expanded'
        },
        // 虚拟滚动配置，y轴大于30条数据时启用虚拟滚动
        // 'scroll-y': {
        //   gt: 30
        // },
        // 'scroll-x': {
        //   gt: 15
        // },
        'radio-config': {highlight: true},
        'checkbox-config': {highlight: true},
      },
      // 绑定左侧选择框
      selectedRows: [],
      // 绑定左侧选择框已选择的id
      selectedRowIds: [],
      // 统计列配置
      statistics: {
        has: false,
        sum: [],
        average: [],
      },
      // 允许执行刷新特效的行ID
      reloadEffectRowKeysMap: {},
      //配置了但是没有授权的按钮和列 集合
      excludeCode:[],
      // 联动下拉选项（用于隔离不同的下拉选项）
      // 内部联动配置，map
      _innerLinkageConfig: null,
    }
  },
  computed: {

    // vxe 最终 columns
    vxeColumns() {
      this._innerColumns.forEach(column => {
        let renderOptions = {
          caseId: this.caseId,
          bordered: this.bordered,
          disabled: this.disabled,
          scrolling: this.scrolling,
          reloadEffect: this.reloadEffect,
          reloadEffectRowKeysMap: this.reloadEffectRowKeysMap,
          listeners: this.cellListeners,
        }
        if (column.$type === JVXETypes.rowDragSort) {
          renderOptions.dragSortKey = this.dragSortKey
        }
        // slot 组件特殊处理
        if (column.$type === JVXETypes.slot) {
          if (this.$scopedSlots.hasOwnProperty(column.slotName)) {
            renderOptions.slot = this.$scopedSlots[column.slotName]
            renderOptions.target = this
          }
        }
        // 处理联动列，联动列只能作用于 select 组件
        if (column.$type === JVXETypes.select && this._innerLinkageConfig != null) {
          // 判断当前列是否是联动列
          if (this._innerLinkageConfig.has(column.key)) {
            renderOptions.linkage = {
              config: this._innerLinkageConfig.get(column.key),
              getLinkageOptionsSibling: this.getLinkageOptionsSibling,
              getLinkageOptionsAsync: this.getLinkageOptionsAsync,
              linkageSelectChange: this.linkageSelectChange,
            }
          }
        }
        if (column.editRender) {
          Object.assign(column.editRender, renderOptions)
        }
        if (column.cellRender) {
          Object.assign(column.cellRender, renderOptions)
        }
        // update--begin--autor:lvdandan-----date:20201019------for:LOWCOD-882 【新行编辑】列表上带按钮的遮挡问题
        if (column.$type === JVXETypes.file || column.$type === JVXETypes.image) {
          if (column.width && column.width.endsWith('px')) {
            column.width = Number.parseInt(column.width.substr(0,column.width.length-2))+Number.parseInt(1)+'px';
          }
        }
        // update--begin--autor:lvdandan-----date:20201019------for:LOWCOD-882 【新行编辑】列表上带按钮的遮挡问题

        // update--begin--autor:lvdandan-----date:20201211------for:JT-118 【online】 日期、时间控件长度较小
        if (column.$type === JVXETypes.datetime || column.$type === JVXETypes.userSelect || column.$type === JVXETypes.departSelect) {
          let width = column.width && column.width.endsWith('px')?Number.parseInt(column.width.substr(0,column.width.length-2)):0;
          if(width <= 190){
            column.width = '190px'
          }
        }
        if (column.$type === JVXETypes.date) {
          let width = column.width && column.width.endsWith('px')?Number.parseInt(column.width.substr(0,column.width.length-2)):0;
          if(width <= 135){
            column.width = '135px'
          }
        }
        // update--end--autor:lvdandan-----date:20201211------for:JT-118 【online】 日期、时间控件长度较小
      })
      return this._innerColumns
    },
    // vxe 最终 editRules
    vxeEditRules() {
      return Object.assign({}, this.editRules, this._innerEditRules)
    },
    // vxe 最终 props
    vxeProps() {
      let expandConfig = Object.assign({}, this.defaultVxeProps['expand-config'], this.expandConfig)

      return Object.assign({}, this.defaultVxeProps, {
        showFooter: this.statistics.has,
      }, this.$attrs, {
        loading: this.loading,
        columns: this.vxeColumns,
        editRules: this.vxeEditRules,
        // data: this.dataSource,
        height: this.height === 'auto' ? null : this.height,
        maxHeight: this.maxHeight,
        border: this.bordered,
        expandConfig: expandConfig,
        footerMethod: this.handleFooterMethod,
        // footerSpanMethod: this.handleFooterSpanMethod,
      })
    },
    // vxe 最终 events
    vxeEvents() {
      // 内置事件
      let events = {
        'scroll': this.handleVxeScroll,
        'cell-click': this.handleCellClick,
        'edit-closed': this.handleEditClosed,
        'edit-actived': this.handleEditActived,
        'radio-change': this.handleVxeRadioChange,
        'checkbox-all': this.handleVxeCheckboxAll,
        'checkbox-change': this.handleVxeCheckboxChange,
      }
      // 用户传递的事件，进行合并操作
      Object.keys(this.$listeners).forEach(key => {
        let listen = this.$listeners[key]
        if (events.hasOwnProperty(key)) {
          if (Array.isArray(listen)) {
            listen.push(events[key])
          } else {
            listen = [events[key], listen]
          }
        }
        events[key] = listen
      })
      return events
    },
    // 组件监听事件
    cellListeners() {
      return {
        trigger: (name, event) => this.trigger(name, event),
        valueChange: event => this.trigger('valueChange', event),
        /** 当前行向上移一位 */
        rowMoveUp: rowIndex => this.rowResort(rowIndex, rowIndex - 1),
        /** 当前行向下移一位 */
        rowMoveDown: rowIndex => this.rowResort(rowIndex, rowIndex + 1),
        /** 在当前行下面插入一行 */
        rowInsertDown: rowIndex => this.insertRows({}, rowIndex + 1),
      }
    },
  },
  watch: {
    dataSource: {
      //   deep: true,
      immediate: true,
      async handler() {
        let vxe = await getRefPromise(this, 'vxe')

        this.dataSource.forEach((data, idx) => {
          // 开启了排序就自动计算排序值
          if (this.dragSort) {
            this.$set(data, this.dragSortKey, idx + 1)
          }
          // 处理联动回显数据
          if (this._innerLinkageConfig != null) {
            for (let configItem of this._innerLinkageConfig.values()) {
              this.autoSetLinkageOptionsByData(data, '', configItem, 0)
            }
          }
        })

        // 阻断vue监听大数据，提高性能
        vxe.loadData(this.dataSource)

        // TODO 解析disabledRows
        // let disabled = false
        //
        // let disabledRowIds = (this.disabledRowIds || [])
        // // 解析disabledRows
        // Object.keys(this.disabledRows).forEach(disabledColKey => {
        //   // 判断是否有该属性
        //   if (data.hasOwnProperty(disabledColKey)) {
        //     if (disabled !== true) {
        //       let temp = this.disabledRows[disabledColKey]
        //       // 禁用规则可以是一个数组
        //       if (Array.isArray(temp)) {
        //         disabled = temp.includes(data[disabledColKey])
        //       } else {
        //         disabled = (temp === data[disabledColKey])
        //       }
        //       if (disabled) {
        //         disabledRowIds.push(row.id)
        //       }
        //     }
        //   }
        // })
      },
    },
    columns: {
      immediate: true,
      handler(columns) {
        //获取不需要显示列
        this.loadExcludeCode()
        let _innerColumns = []
        let _innerEditRules = {}
        let {rowNumber, rowSelection, rowExpand, dragSort} = this
        let expandColumn, seqColumn, checkboxColumn, radioColumn, dragSortColumn
        if (Array.isArray(columns)) {
          this.statistics.has = false
          this.statistics.sum = []
          this.statistics.average = []

          // 处理成vxe可识别的columns
          columns.forEach(column => {
            if(this.excludeCode.indexOf(column.key)>=0){
              return false
            }
            let col = {...column}
            let {type} = col
            const enhanced = getEnhancedMixins(type)
            if (type === JVXETypes.rowNumber) {
              seqColumn = col
            } else if (type === JVXETypes.rowCheckbox) {
              checkboxColumn = col
            } else if (type === JVXETypes.rowRadio) {
              radioColumn = col
            } else if (type === JVXETypes.rowExpand) {
              expandColumn = col
            } else if (type === JVXETypes.rowDragSort) {
              dragSortColumn = col
            } else {
              col.field = col.key
              // 防止和vxeTable自带的type起冲突
              col.$type = col.type
              delete col.type
              let renderName = 'cellRender', renderOptions = {name: JVXETypes._prefix + type}
              if (type) {
                // hidden 是特殊的组件
                if (type === JVXETypes.hidden) {
                  col.visible = false
                } else if (enhanced.switches.editRender) {
                  renderName = 'editRender'
                  renderOptions.type = (enhanced.switches.visible || this.alwaysEdit) ? 'visible' : 'default'
                }
              } else {
                renderOptions.name = JVXETypes._prefix + JVXETypes.normal
              }
              col[renderName] = renderOptions
              // 处理字典
              if (col.dictCode) {
                this._loadDictConcatToOptions(col)
              }
              // 处理校验
              if (col.validateRules) {
                let rules = []
                if (Array.isArray(col.validateRules)) {
                  for (let rule of col.validateRules) {
                    let replace = {
                      message: replaceProps(col, rule.message)
                    }
                    if (rule.unique || rule.pattern === 'only') {
                      // 唯一校验器
                      rule.validator = uniqueValidator.bind(this)
                    } else if (rule.pattern) {
                      // 非空
                      if (rule.pattern === fooPatterns[0].value) {
                        rule.required = true
                        delete rule.pattern
                      } else {
                        // 兼容Online表单的特殊规则
                        for (let foo of fooPatterns) {
                          if (foo.value === rule.pattern) {
                            rule.pattern = foo.pattern
                            break
                          }
                        }
                      }
                    } else if (typeof rule.handler === 'function') {
                      // 自定义函数校验
                      rule.validator = handlerConvertToValidator.bind(this)
                    }
                    rules.push(Object.assign({}, rule, replace))
                  }
                }
                _innerEditRules[col.key] = rules
              }
              // 处理统计列
              // sum = 求和、average = 平均值
              if (Array.isArray(col.statistics)) {
                this.statistics.has = true
                col.statistics.forEach(item => {
                  let arr = this.statistics[item.toLowerCase()]
                  if (Array.isArray(arr)) {
                    pushIfNotExist(arr, col.key)
                  }
                })
              }
              _innerColumns.push(col)
            }
          })
        }
        // 判断是否开启了序号
        if (rowNumber) {
          let col = {type: 'seq', title: '#', width: 60, fixed: 'left', align: 'center'}
          if (seqColumn) {
            col = Object.assign(col, seqColumn, {type: 'seq'})
          }
          _innerColumns.unshift(col)
        }
        // 判断是否开启了可选择行
        if (rowSelection) {
          let width = 40
          if (this.statistics.has && !rowExpand && !dragSort) {
            width = 60
          }
          let col = {type: this.rowSelectionType, width, fixed: 'left', align: 'center'}
          // radio
          if (this.rowSelectionType === 'radio' && radioColumn) {
            col = Object.assign(col, radioColumn, {type: 'radio'})
          }
          // checkbox
          if (this.rowSelectionType === 'checkbox' && checkboxColumn) {
            col = Object.assign(col, checkboxColumn, {type: 'checkbox'})
          }
          _innerColumns.unshift(col)
        }
        // 是否可展开行
        if (rowExpand) {
          let width = 40
          if (this.statistics.has && !dragSort) {
            width = 60
          }
          let col = {type: 'expand', title: '', width, fixed: 'left', align: 'center', slots: {content: 'expandContent'}}
          if (expandColumn) {
            col = Object.assign(col, expandColumn, {type: 'expand'})
          }
          _innerColumns.unshift(col)
        }
        // 是否可拖动排序
        if (dragSort) {
          let width = 40
          if (this.statistics.has) {
            width = 60
          }
          let col = {type: JVXETypes.rowDragSort, title: '', width, fixed: 'left', align: 'center', cellRender: {name: JVXETypes._prefix + JVXETypes.rowDragSort}}
          if (dragSortColumn) {
            col = Object.assign(col, dragSortColumn, {type: JVXETypes.rowDragSort})
          }
          _innerColumns.unshift(col)
        }

        this._innerColumns = _innerColumns
        this._innerEditRules = _innerEditRules
      }
    },
    // watch linkageConfig
    // 整理多级联动配置
    linkageConfig: {
      immediate: true,
      handler() {
        if (Array.isArray(this.linkageConfig) && this.linkageConfig.length > 0) {
          // 获取联动的key顺序
          let getLcKeys = (key, arr) => {
            let col = this._innerColumns.find(col => col.key === key)
            if (col) {
              arr.push(col.key)
              if (col.linkageKey) {
                return getLcKeys(col.linkageKey, arr)
              }
            }
            return arr
          }
          let configMap = new Map()
          this.linkageConfig.forEach(lc => {
            let keys = getLcKeys(lc.key, [])
            // 多个key共享一个，引用地址
            let configItem = {
              ...lc, keys,
              optionsMap: new Map()
            }
            keys.forEach(k => configMap.set(k, configItem))
          })
          this._innerLinkageConfig = configMap
        } else {
          this._innerLinkageConfig = null
        }
      }
    },
  },
  created() {
  },
  mounted() {
    this.handleTabsChange()
  },
  methods: {

    /**
     * 自动判断父级是否是 <a-tabs/> 组件，然后添加事件监听，自动重置表格
     */
    handleTabsChange() {
      // 获取父级
      const tabs = getVmParentByName(this, 'ATabs')
      const tabPane = getVmParentByName(this, 'ATabPane')
      if (tabs && tabPane) {
        // 用户自定义的 key
        const currentKey = tabPane.$vnode.key
        // 添加 activeKey 监听
        const unwatch = tabs.$children[0].$watch('$data._activeKey', async (key) => {
          // 切换到自己时重新计算
          if (currentKey === key) {
            await this.$nextTick()
            await this.refreshScroll()
            await this.recalculate()
          }
        })
        // 当前实例销毁时取消监听
        this.$on('beforeDestroy', () => unwatch())
      }
    },

    handleVxeScroll(event) {
      let {$refs, scroll} = this

      // 记录滚动条的位置
      scroll.top = event.scrollTop
      scroll.left = event.scrollLeft

      $refs.subPopover ? $refs.subPopover.close() : null
      this.scrolling = true
      this.closeScrolling()
    },
    // 当手动勾选单选时触发的事件
    handleVxeRadioChange(event) {
      let row = event.$table.getRadioRecord()
      this.selectedRows = row ? [row] : []
      this.handleSelectChange('radio', this.selectedRows, event)
    },
    // 当手动勾选全选时触发的事件
    handleVxeCheckboxAll(event) {
      this.selectedRows = event.$table.getCheckboxRecords()
      this.handleSelectChange('checkbox-all', this.selectedRows, event)
    },
    // 当手动勾选并且值发生改变时触发的事件
    handleVxeCheckboxChange(event) {
      this.selectedRows = event.$table.getCheckboxRecords()
      this.handleSelectChange('checkbox', this.selectedRows, event)
    },
    // 行选择change事件
    handleSelectChange(type, selectedRows, $event) {
      let action
      if (type === 'radio') {
        action = 'selected'
      } else if (type === 'checkbox') {
        action = selectedRows.includes($event.row) ? 'selected' : 'unselected'
      } else {
        action = 'selected-all'
      }

      this.selectedRowIds = selectedRows.map(row => row.id)
      this.trigger('selectRowChange', {
        type: type,
        action: action,
        $event: $event,
        row: $event.row,
        selectedRows: this.selectedRows,
        selectedRowIds: this.selectedRowIds
      })
    },

    // 点击单元格时触发的事件
    handleCellClick(event) {
      let {row, column, $event, $table} = event
      let {$refs} = this

      // 点击了可编辑的
      if (column.editRender) {
        $refs.subPopover ? $refs.subPopover.close() : null
        return
      }

      // 显示详细信息
      if (column.own.showDetails) {
        // 两个如果同时存在的话会出现死循环
        $refs.subPopover ? $refs.subPopover.close() : null
        $refs.detailsModal ? $refs.detailsModal.open(event) : null
      } else if ($refs.subPopover) {
        $refs.subPopover.toggle(event)
      } else if (this.clickSelectRow) {
        let className = $event.target.className || ''
        className = typeof className === 'string' ? className : className.toString()
        // 点击的是expand，不做处理
        if (className.includes('vxe-table--expand-btn')) {
          return
        }
        // 点击的是checkbox，不做处理
        if (className.includes('vxe-checkbox--icon') || className.includes('vxe-cell--checkbox')) {
          return
        }
        // 点击的是radio，不做处理
        if (className.includes('vxe-radio--icon') || className.includes('vxe-cell--radio')) {
          return
        }
        if (this.rowSelectionType === 'radio') {
          $table.setRadioRow(row)
          this.handleVxeRadioChange(event)
        } else {
          $table.toggleCheckboxRow(row)
          this.handleVxeCheckboxChange(event)
        }
      }
    },

    // 单元格编辑状态下被关闭时会触发该事件
    handleEditClosed({column}) {
      // 执行增强
      getEnhancedMixins(column.own.$type, 'aopEvents').editClosed.apply(this, arguments)
    },

    // 单元格被激活编辑时会触发该事件
    handleEditActived({column}) {
      // 执行增强
      getEnhancedMixins(column.own.$type, 'aopEvents').editActived.apply(this, arguments)
    },

    /** 表尾数据处理方法，用于显示统计信息 */
    handleFooterMethod({columns, data}) {
      const {statistics} = this
      let footers = []
      if (statistics.has) {
        if (statistics.sum.length > 0) {
          footers.push(this.getFooterStatisticsMap({
            columns: columns,
            title: '合计',
            checks: statistics.sum,
            method: (column) => XEUtils.sum(data, column.property)
          }))
        }
        if (statistics.average.length > 0) {
          footers.push(this.getFooterStatisticsMap({
            columns: columns,
            title: '平均',
            checks: statistics.average,
            method: (column) => XEUtils.mean(data, column.property)
          }))
        }
      }
      return footers
    },

    getFooterStatisticsMap({columns, title, checks, method}) {
      return columns.map((column, columnIndex) => {
        if (columnIndex === 0) {
          return title
        }
        if (checks.includes(column.property)) {
          return method(column, columnIndex)
        }
        return null
      })
    },

    /** 表尾单元格合并方法 */
    handleFooterSpanMethod(event) {
      if (event.columnIndex === 0) {
        return {colspan: 2}
      }
    },

    /*--- 外部可调用接口方法 ---*/

    /**
     * 重置滚动条Top位置
     * @param top 新top位置，留空则滚动到上次记录的位置，用于解决切换tab选项卡时导致白屏以及自动将滚动条滚动到顶部的问题
     */
    resetScrollTop(top) {
      this.scrollTo(null, (top == null || top === '') ? this.scroll.top : top)
    },

    /**
     * 加载新数据，和 loadData 不同的是，用该方法加载的数据都是相当于点新增按钮新增的数据。
     * 适用于不是数据库里查出来的没有id的临时数据
     * @param dataSource
     */
    async loadNewData(dataSource) {
      if (Array.isArray(dataSource)) {
        let {xTable} = this.$refs.vxe.$refs
        // issues/2784
        // 先清空所有数据
        xTable.loadData([])

        dataSource.forEach((data, idx) => {
          // 开启了排序就自动计算排序值
          if (this.dragSort) {
            this.$set(data, this.dragSortKey, idx + 1)
          }
          // 处理联动回显数据
          if (this._innerLinkageConfig != null) {
            for (let configItem of this._innerLinkageConfig.values()) {
              this.autoSetLinkageOptionsByData(data, '', configItem, 0)
            }
          }
        })
        // 再新增
        return xTable.insertAt(dataSource)
      }
      return []
    },

    // 校验table，失败返回errMap，成功返回null
    async validateTable() {
      const errMap = await this.validate().catch(errMap => errMap)
      return errMap ? errMap : null
    },
    // 完整校验
    async fullValidateTable() {
      const errMap = await this.fullValidate().catch(errMap => errMap)
      return errMap ? errMap : null
    },

    /** 设置某行某列的值 */
    setValues(values) {
      if (!Array.isArray(values)) {
        console.warn(`JVxeTable.setValues：必须传递数组`)
        return
      }
      // 是否更新了数据
      let updated = false
      values.forEach((item, idx) => {
        let {rowKey, values: record} = item
        let {row} = this.getIfRowById(rowKey)
        if (!row) {
          return
        }
        Object.keys(record).forEach(colKey => {
          let column = this.getColumnByKey(colKey)
          if (column) {
            let oldValue = row[colKey]
            let newValue = record[colKey]
            if (newValue !== oldValue) {
              updated = true
              this.$set(row, colKey, newValue)
              // 触发 valueChange 事件
              this.trigger('valueChange', {
                type: column.own.$type,
                value: newValue,
                oldValue: oldValue,
                col: column.own,
                column: column,
                isSetValues: true,
              })
            }
          } else {
            console.warn(`JVxeTable.setValues：没有找到key为"${colKey}"的列`)
          }
        })
      })
      // 【issues/3828】数据更新后，重新计算统计列 
      if (updated && this.statistics.has) {
        this.$nextTick(async () => {
          let {xTable} = this.$refs.vxe.$refs;
          await xTable.updateCache(true);
          await xTable.updateData();
        });
      }
    },

    /** 获取所有的数据，包括values、deleteIds */
    getAll() {
      return {
        tableData: this.getTableData(),
        deleteData: this.getDeleteData()
      }
    },
    /** 获取表格表单里的值 */
    getValues(callback, rowIds) {
      let tableData = this.getTableData({rowIds: rowIds})
      callback('', tableData)
    },
    /** 获取表格数据 */
    getTableData(options = {}) {
      let {rowIds} = options
      let tableData
      // 仅查询指定id的行
      if (Array.isArray(rowIds) && rowIds.length > 0) {
        tableData = []
        rowIds.forEach(rowId => {
          let {row} = this.getIfRowById(rowId)
          if (row) {
            tableData.push(row)
          }
        })
      } else {
        // 查询所有行
        tableData = this.$refs.vxe.getTableData().fullData
      }
      return this.filterNewRows(tableData, false)
    },
    /** 仅获取新增的数据 */
    getNewData() {
      let newData = cloneObject(this.$refs.vxe.getInsertRecords())
      newData.forEach(row => delete row.id)
      return newData
    },
    /** 仅获取新增的数据,带有id */
    getNewDataWithId() {
      let newData = cloneObject(this.$refs.vxe.getInsertRecords())
      return newData
    },
    /** 根据ID获取行，新增的行也能查出来 */
    getIfRowById(id) {
      let row = this.getRowById(id), isNew = false
      if (!row) {
        row = this.getNewRowById(id)
        if (!row) {
          console.warn(`JVxeTable.getIfRowById：没有找到id为"${id}"的行`)
          return {row: null}
        }
        isNew = true
      }
      return {row, isNew}
    },
    /** 通过临时ID获取新增的行 */
    getNewRowById(id) {
      let records = this.getInsertRecords()
      for (let record of records) {
        if (record.id === id) {
          return record
        }
      }
      return null
    },
    /** 仅获取被删除的数据（新增又被删除的数据不会被获取到） */
    getDeleteData() {
      return cloneObject(this.$refs.vxe.getRemoveRecords())
    },
    /**
     * 添加一行或多行
     *
     * @param rows
     * @param isOnlJs 是否是onlineJS增强触发的
     * @return
     */
    async addRows(rows = {}, isOnlJs) {
      return this._addOrInsert(rows, -1, 'added', isOnlJs)
    },

    /**
     * 添加一行或多行
     *
     * @param rows
     * @param index 添加下标，数字，必填
     * @return
     */
    async insertRows(rows, index) {
      if (typeof index !== 'number' || index < 0) {
        console.warn(`【JVXETable】insertRows：index必须传递数字，且大于-1`)
        return
      }
      return this._addOrInsert(rows, index, 'inserted')
    },
    /**
     * 添加一行或多行临时数据，不会填充默认值，传什么就添加进去什么
     * @param rows
     * @param options 选项
     * @param options.setActive 是否激活最后一行的编辑模式
     */
    async pushRows(rows = {}, options = {}) {
      let {xTable} = this.$refs.vxe.$refs
      let {setActive, index} = options
      setActive = setActive == null ? false : !!setActive
      index = index == null ? -1 : index
      index = index === -1 ? index : xTable.tableFullData[index]
      // 插入行
      let result = await xTable.insertAt(rows, index)
      if (setActive) {
        // 激活最后一行的编辑模式
        xTable.setActiveRow(result.rows[result.rows.length - 1])
      }
      await this._recalcSortNumber()
      return result
    },

    /** 清空选择行 */
    clearSelection() {
      let event = {$table: this.$refs.vxe, target: this}
      if (this.rowSelectionType === JVXETypes.rowRadio) {
        this.$refs.vxe.clearRadioRow()
        this.handleVxeRadioChange(event)
      } else {
        this.$refs.vxe.clearCheckboxRow()
        this.handleVxeCheckboxChange(event)
      }
    },

    /** 删除一行或多行数据 */
    async removeRows(rows) {
      const res = await this._remove(rows)
      await this._recalcSortNumber()
      return res
    },

    /** 根据id删除一行或多行 */
    removeRowsById(rowId) {
      let rowIds
      if (Array.isArray(rowId)) {
        rowIds = rowId
      } else {
        rowIds = [rowId]
      }
      let rows = rowIds.map((id) => {
        let {row} = this.getIfRowById(id)
        if (!row) {
          return
        }
        if (row) {
          return row
        } else {
          console.warn(`【JVXETable】removeRowsById：${id}不存在`)
          return null
        }
      }).filter((row) => row != null)
      return this.removeRows(rows)
    },

    getColumnByKey() {
      return this.$refs.vxe.getColumnByField.apply(this.$refs.vxe, arguments)
    },

    /* --- 辅助方法 ---*/

    // 触发事件
    trigger(name, event = {}) {
      event.$target = this
      event.$table = this.$refs.vxe
      //online增强参数兼容
      event.target = this
      this.$emit(name, event)
    },

    /** 【多级联动】获取同级联动下拉选项 */
    getLinkageOptionsSibling(row, col, config, request) {
      // 如果当前列不是顶级列
      let key = ''
      if (col.key !== config.key) {
        // 就找出联动上级列
        let idx = config.keys.findIndex(k => col.key === k)
        let parentKey = config.keys[idx - 1]
        key = row[parentKey]
        // 如果联动上级列没有选择数据，就直接返回空数组
        if (key === '' || key == null) {
          return []
        }
      } else {
        key = 'root'
      }
      let options = config.optionsMap.get(key)
      if (!Array.isArray(options)) {
        if (request) {
          let parent = key === 'root' ? '' : key
          return this.getLinkageOptionsAsync(config, parent)
        } else {
          options = []
        }
      }
      return options
    },
    /** 【多级联动】获取联动下拉选项（异步） */
    getLinkageOptionsAsync(config, parent) {
      return new Promise(resolve => {
        let key = parent ? parent : 'root'
        let options
        if (config.optionsMap.has(key)) {
          options = config.optionsMap.get(key)
          if (options instanceof Promise) {
            options.then(opt => {
              config.optionsMap.set(key, opt)
              resolve(opt)
            })
          } else {
            resolve(options)
          }
        } else if (typeof config.requestData === 'function') {
          // 调用requestData方法，通过传入parent来获取子级
          let promise = config.requestData(parent)
          config.optionsMap.set(key, promise)
          promise.then(opt => {
            config.optionsMap.set(key, opt)
            resolve(opt)
          })
        } else {
          resolve([])
        }
      })
    },
    // 【多级联动】 用于回显数据，自动填充 optionsMap
    autoSetLinkageOptionsByData(data, parent, config, level) {
      if (level === 0) {
        this.getLinkageOptionsAsync(config, '')
      } else {
        this.getLinkageOptionsAsync(config, parent)
      }
      if (config.keys.length - 1 > level) {
        let value = data[config.keys[level]]
        if (value) {
          this.autoSetLinkageOptionsByData(data, value, config, level + 1)
        }
      }
    },
    // 【多级联动】联动组件change时，清空下级组件
    linkageSelectChange(row, col, config, value) {
      if (col.linkageKey) {
        this.getLinkageOptionsAsync(config, value)
        let idx = config.keys.findIndex(k => k === col.key)
        let values = {}
        for (let i = idx; i < config.keys.length; i++) {
          values[config.keys[i]] = ''
        }
        // 清空后几列的数据
        this.setValues([{rowKey: row.id, values}])
      }
    },

    /** 加载数据字典并合并到 options */
    _loadDictConcatToOptions(column) {
      initDictOptions(column.dictCode).then((res) => {
        if (res.success) {
          let newOptions = (column.options || [])// .concat(res.result)
          res.result.forEach(item => {
            // 过滤重复数据
            for (let option of newOptions) if (option.value === item.value) return
            newOptions.push(item)
          })
          this.$set(column, 'options', newOptions)
        } else {
          console.group(`JVxeTable 查询字典(${column.dictCode})发生异常`)
          console.warn(res.message)
          console.groupEnd()
        }
      })
    },
    //options自定义赋值 刷新
    virtualRefresh(){
      this.scrolling = true
      this.closeScrolling()
    },
    // 设置 this.scrolling 防抖模式
    closeScrolling: simpleDebounce(function () {
      this.scrolling = false
    }, 100),

    /**
     * 过滤添加的行
     * @param rows 要筛选的行数据
     * @param remove true = 删除新增，false=只删除id
     * @param handler function
     */
    filterNewRows(rows, remove = true, handler) {
      let insertRecords = this.$refs.vxe.getInsertRecords()
      let records = []
      for (let row of rows) {
        let item = cloneObject(row)
        if (insertRecords.includes(row)) {
          handler ? handler({item, row, insertRecords}) : null

          if (remove) {
            continue
          }
          delete item.id
        }
        records.push(item)
      }
      return records
    },

    // 删除选中的数据
    async removeSelection() {
      let res = await this._remove(this.selectedRows)
      this.clearSelection()
      await this._recalcSortNumber()
      return res
    },

    /**
     * 【删除指定行数据】（重写vxeTable的内部方法，添加了从keepSource中删除）
     * 如果传 row 则删除一行
     * 如果传 rows 则删除多行
     * 如果为空则删除所有
     */
    _remove(rows) {
      const xTable = this.$refs.vxe.$refs.xTable

      const {afterFullData, tableFullData, tableSourceData, editStore, treeConfig, checkboxOpts, selection, isInsertByRow, scrollYLoad} = xTable
      const {actived, removeList, insertList} = editStore
      const {checkField: property} = checkboxOpts
      let rest = []
      const nowData = afterFullData
      if (treeConfig) {
        throw new Error(UtilTools.getLog('vxe.error.noTree', ['remove']))
      }
      if (!rows) {
        rows = tableFullData
      } else if (!XEUtils.isArray(rows)) {
        rows = [rows]
      }
      // 如果是新增，则保存记录
      rows.forEach(row => {
        if (!isInsertByRow(row)) {
          removeList.push(row)
        }
      })
      // 如果绑定了多选属性，则更新状态
      if (!property) {
        XEUtils.remove(selection, row => rows.indexOf(row) > -1)
      }
      // 从数据源中移除
      if (tableFullData === rows) {
        rows = rest = tableFullData.slice(0)
        tableFullData.length = 0
        nowData.length = 0
      } else {
        rest = XEUtils.remove(tableFullData, row => rows.indexOf(row) > -1)
        XEUtils.remove(nowData, row => rows.indexOf(row) > -1)
      }
      // 【从keepSource中删除】
      if (xTable.keepSource) {
        let rowIdSet = new Set(rows.map(row => row.id))
        XEUtils.remove(tableSourceData, row => rowIdSet.has(row.id))
      }

      // 如果当前行被激活编辑，则清除激活状态
      if (actived.row && rows.indexOf(actived.row) > -1) {
        xTable.clearActived()
      }
      // 从新增中移除已删除的数据
      XEUtils.remove(insertList, row => rows.indexOf(row) > -1)
      xTable.handleTableData()
      xTable.updateFooter()
      xTable.updateCache()
      xTable.checkSelectionStatus()
      if (scrollYLoad) {
        xTable.updateScrollYSpace()
      }
      return xTable.$nextTick().then(() => {
        xTable.recalculate()
        return {row: rest.length ? rest[rest.length - 1] : null, rows: rest}
      })
    },

    /** 行重新排序 */
    async rowResort(oldIndex, newIndex) {
      const xTable = this.$refs.vxe.$refs.xTable
      window.xTable = xTable
      const sort = (array) => {
        // 存储旧数据，并删除旧项目
        let row = array.splice(oldIndex, 1)[0]
        // 向新项目里添加旧数据
        array.splice(newIndex, 0, row)
      }
      sort(xTable.tableFullData)
      if (xTable.keepSource) {
        sort(xTable.tableSourceData)
      }
      await this.$nextTick()
      await this._recalcSortNumber()
    },

    /** 重新计算排序字段的数值 */
    async _recalcSortNumber() {
      const xTable = this.$refs.vxe.$refs.xTable
      if (this.dragSort) {
        xTable.tableFullData.forEach((data, idx) => data[this.dragSortKey] = (idx + 1))
      }
      await xTable.updateCache(true)
      return await xTable.updateData()
    },

    async _addOrInsert(rows = {}, index, triggerName, isOnlJs) {
      let {xTable} = this.$refs.vxe.$refs
      let records
      if (Array.isArray(rows)) {
        records = rows
      } else {
        records = [rows]
      }
      // 遍历添加默认值
      records.forEach(record => this._createRow(record))
      let result = await this.pushRows(records, {index: index, setActive: true})
      // 遍历插入的行
      // update--begin--autor:lvdandan-----date:20201117------for:LOWCOD-987 【新行编辑】js增强附表内置方法调用问题 #1819
      // online js增强时以传过来值为准，不再赋默认值
      if (isOnlJs != true) {
        for (let i = 0; i < result.rows.length; i++) {
          let row = result.rows[i]
          this.trigger(triggerName, {
            row: row,
            $table: xTable,
            target: this,
          })
        }
      }
      // update--end--autor:lvdandan-----date:20201117------for:LOWCOD-987 【新行编辑】js增强附表内置方法调用问题 #1819
      return result
    },
    // 创建新行，自动添加默认值
    _createRow(record = {}) {
      let {xTable} = this.$refs.vxe.$refs
      // 添加默认值
      xTable.tableFullColumn.forEach(column => {
        let col = column.own
        if (col.key && (record[col.key] == null || record[col.key] === '')) {
          // 设置默认值
          let createValue = getEnhancedMixins(col.$type || col.type, 'createValue')
          record[col.key] = createValue({row: record, column, $table: xTable})
        }
        // update-begin--author:sunjianlei---date:20210819------for: 处理联动列，联动列只能作用于 select 组件
        if (col.$type === JVXETypes.select && this._innerLinkageConfig != null) {
          // 判断当前列是否是联动列
          if (this._innerLinkageConfig.has(col.key)) {
            let configItem = this._innerLinkageConfig.get(col.key)
            this.getLinkageOptionsAsync(configItem, '')
          }
        }
        // update-end--author:sunjianlei---date:20210819------for: 处理联动列，联动列只能作用于 select 组件
      })
      return record
    },

    /*--- 渲染函数 ---*/

    // 渲染 vxe
    renderVxeGrid(h) {
      return h('vxe-grid', {
        ref: 'vxe',
        class: ['j-vxe-table'],
        props: this.vxeProps,
        on: this.vxeEvents,
        // 作用域插槽的格式为
        scopedSlots: this.$scopedSlots,
      })
    },
    // 渲染工具栏
    renderToolbar(h) {
      if (this.toolbar) {
        return h('j-vxe-toolbar', {
          props: {
            toolbarConfig: this.toolbarConfig,
            excludeCode: this.excludeCode,
            size: this.size,
            disabled: this.disabled,
            disabledRows: this.disabledRows,
            selectedRowIds: this.selectedRowIds,
          },
          on: {
            // 新增事件
            add: () => this.addRows(),
            // 保存事件
            save: () => this.trigger('save', {
              $table: this.$refs.vxe,
              target: this,
            }),
            // 删除事件
            remove: () => {
              let $table = this.$refs.vxe
              let deleteRows = this.filterNewRows(this.selectedRows)
              // 触发删除事件
              if (deleteRows.length > 0) {
                let removeEvent = {deleteRows, $table, target: this}
                if (this.asyncRemove) {
                  // 确认删除，只有调用这个方法才会真删除
                  removeEvent.confirmRemove = () => this.removeSelection()
                } else {
                  this.removeSelection()
                }
                this.trigger('remove', removeEvent)
              } else {
                this.removeSelection()
              }
            },
            // 清除选择事件
            clearSelection: this.clearSelection
          },
          scopedSlots: {
            toolbarPrefix: this.$scopedSlots.toolbarPrefix,
            toolbarSuffix: this.$scopedSlots.toolbarSuffix,
          },
        })
      }
      return null
    },
    // 渲染 toolbarAfter 插槽
    renderToolbarAfterSlot() {
      if (this.$scopedSlots['toolbarAfter']) {
        return this.$scopedSlots['toolbarAfter']()
      }
      return null
    },
    // 渲染点击时弹出的子表
    renderSubPopover(h) {
      if (this.clickRowShowSubForm && this.$scopedSlots.subForm) {
        return h('j-vxe-sub-popover', {
          ref: 'subPopover',
          scopedSlots: {
            subForm: this.$scopedSlots.subForm,
          }
        })
      }
      return null
    },
    // 渲染点击时弹出的详细信息
    renderDetailsModal(h) {
      if (this.clickRowShowMainForm && this.$scopedSlots.mainForm) {
        return h('j-vxe-details-modal', {
          ref: 'detailsModal',
          scopedSlots: {
            subForm: this.clickRowShowSubForm ? this.$scopedSlots.subForm : null,
            mainForm: this.$scopedSlots.mainForm
          }
        })
      }
    },
    // 渲染分页器
    renderPagination(h) {
      if (this.pagination && Object.keys(this.pagination).length > 0) {
        return h('j-vxe-pagination', {
          props: {
            size: this.size,
            disabled: this.disabled,
            pagination: this.pagination
          },
          on: {
            change: (e) => this.trigger('pageChange', e)
          },
        })
      }
      return null
    },
    loadExcludeCode(){
      if(!this.authPre || this.authPre.length==0){
        this.excludeCode = []
      }else{
        let pre = this.authPre
        if(!pre.endsWith(':')){
          pre += ':'
        }
        this.excludeCode = getNoAuthCols(pre)
      }
    }

  },
  render(h) {
    return h('div', {
      class: ['j-vxe-table-box', `size--${this.size}`]
    }, [
      this.renderSubPopover(h),
      this.renderDetailsModal(h),
      this.renderToolbar(h),
      this.renderToolbarAfterSlot(),
      this.renderVxeGrid(h),
      this.renderPagination(h),
    ])
  },
  beforeDestroy() {
    this.$emit('beforeDestroy')
  }
}

// 兼容 online 的规则
const fooPatterns = [
  {title: '非空', value: '*', pattern: /^.+$/},
  {title: '6到16位数字', value: 'n6-16', pattern: /^\d{6,16}$/},
  {title: '6到16位任意字符', value: '*6-16', pattern: /^.{6,16}$/},
  {title: '6到18位字母', value: 's6-18', pattern: /^[a-z|A-Z]{6,18}$/},
  {title: '网址', value: 'url', pattern: /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/},
  {title: '电子邮件', value: 'e', pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/},
  {title: '手机号码', value: 'm', pattern: /^1[3456789]\d{9}$/},
  {title: '邮政编码', value: 'p', pattern: /^[0-9]{6}$/},
  {title: '字母', value: 's', pattern: /^[A-Z|a-z]+$/},
  {title: '数字', value: 'n', pattern: /^-?\d+(\.?\d+|\d?)$/},
  {title: '整数', value: 'z', pattern: /^-?\d+$/},
  {title: '金额', value: 'money', pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,5}))$/},
]

/** 旧版handler转为新版Validator */
function handlerConvertToValidator(event) {
  const {column, rule} = event
  return new Promise((resolve, reject) => {
    rule.handler(event, (flag, msg) => {
      let message = rule.message
      if (typeof msg === 'string') {
        message = replaceProps(column.own, msg)
      }
      if (flag == null) {
        resolve(message)
      } else if (!!flag) {
        resolve(message)
      } else {
        reject(new Error(message))
      }
    }, this, event)
  })
}

/** 唯一校验器 */
function uniqueValidator(event) {
  const {cellValue, column, rule} = event
  let tableData = this.getTableData()
  let findCount = 0
  for (let rowData of tableData) {
    if (rowData[column.own.key] === cellValue) {
      if (++findCount >= 2) {
        return Promise.reject(new Error(rule.message))
      }
    }
  }
  return Promise.resolve()
}
