<template>
  <div>
    <vxe-form
      ref="xForm"
      v-show="searchFormShow"
      :data="formData"
      :items="formItems"
      @reset="handleReset"
      @submit="handleSearch"
    ></vxe-form>
    <vxe-toolbar custom>
      <template #buttons>
        <slot name="leftMenu"></slot>
      </template>
      <template #tools>
        <slot name="rightTools"></slot>
      </template>
    </vxe-toolbar>
    <a-row class="topbar" :gutter="10">
      <a-col span="8">
        <vxe-input
          clearable
          class="fullBox"
          :placeholder="labels.tryFullTableFiltering"
          type="search"
          v-if="hasSearchBar"
          v-model="filterName1"
          @keyup="searchEvent"
        ></vxe-input>
      </a-col>
      <a-col :span="hasSearchBar ? 14 : 24">
        <a-alert v-if="selection" type="info" class="topSearch" show-icon
          ><template slot="message">
            <span
              >{{ labels.allSelected }}: {{ fullSelectionSize }}
              <a-button type="link" size="mini" @click="clearSelection">{{
                labels.clearAll
              }}</a-button>
              {{ labels.selectedInCurrentPage }}: {{ selectRecordsSize }}
              {{ labels.totalInCurrentPage }}: {{ listSize }}
            </span>
          </template>
        </a-alert>
      </a-col>
    </a-row>
    <vxe-table
      border
      column-key
      highlight-current-row
      highlight-hover-row
      ref="xTable"
      resizable
      row-id="rowId"
      v-on="$listeners"
      :checkbox-config="checkboxConfig"
      :data="list"
      :height="height"
      :id="id"
      :loading.sync="loading"
      :keyboard-config="keyboardConfig"
      :menu-config="menuConfig"
      :seq-config="sequenceConfig"
      :show-overflow="showOverflow"
      :size="option.size"
      :sort-config="sortConfig"
      :tooltip-config="tooltipConfig"
      @cell-click="cellClickEvent"
      @cell-dblclick="cellDBLClickEvent"
      @checkbox-all="checkboxAllEvent"
      @checkbox-change="checkboxChangeEvent"
    >
      <vxe-table-column v-if="selection" type="checkbox" width="50" fixed="left"></vxe-table-column>
      <vxe-table-column type="seq" width="50" fixed="left"></vxe-table-column>
      <template v-for="item in tableColumn">
        <vxe-table-column
          v-if="!item.slot && !item.component"
          :align="item.align"
          :edit-render="item.editRender"
          :field="item.field"
          :filters="item.filters"
          :filter-method="filterMethod"
          :formatter="getFormatter(item)"
          :key="item.field"
          :min-width="item.minWidth"
          :show-overflow="showOverflow"
          :sortable="sortable && item.sortable"
          :title="item.title"
          :type="item.type"
          :width="item.width"
        >
        </vxe-table-column>
        <vxe-table-column
          v-else
          :edit-render="item.editRender"
          :field="item.field"
          :filters="item.filters"
          :filter-method="filterMethod"
          :key="item.field"
          :min-width="item.minWidth"
          :show-overflow="showOverflow"
          :sortable="sortable && item.sortable"
          :title="item.title"
          :width="item.width"
        >
          <template #edit="{ row }">
            <slot :name="item.field" :row="row" :cellEdit="true">{{
              getShowText(row[item.field], item)
            }}</slot>
          </template>
          <template #default="{ row }">
            <slot :name="item.field" :row="row" :cellEdit="false"
              >{{ getShowText(row[item.field], item) }}
            </slot>
          </template>
        </vxe-table-column>
      </template>
    </vxe-table>
    <vxe-pager
      v-if="showType === 'page' && showPager"
      :current-page.sync="page.currentPage"
      :layouts="['Total', 'Sizes', 'PrevPage', 'Number', 'NextPage', 'FullJump']"
      :page-size.sync="page.pageSize"
      :page-sizes.sync="page.pageSizes"
      :total="page.total"
      @page-change="handlePageChange"
    ></vxe-pager>
  </div>
</template>
<script>
import XEUtils from 'xe-utils';
import cloneDeep from 'lodash/cloneDeep';
import { ft } from '../../../lib/utils';

const LABELS = {
  allSelected: ft('i18n_common_all_selected', '所有已选中'),
  clearAll: ft('i18n_common_cleared_all', '全部清空'),
  date: ft('i18n_common_date', '日期'),
  operation: ft('i18n_common_operation', '操作'),
  partialSaveSuccessfully: ft('i18n_common_Partial_save_successfully', '局部保存成功'),
  pleaseSelect: ft('i18n_common_please_select', '请选择'),
  reset: ft('i18n_common_reset', '重置'),
  saveSuccessfully: ft('i18n_common_saved_successfully', '保存成功'),
  search: ft('i18n_common_search', '搜索'),
  selectedInCurrentPage: ft('i18n_common_all_selected_in_current_page', '当前分页已选中'),
  time: ft('Time', '时间'),
  totalInCurrentPage: ft('i18n_common_total_in_current_page', '当前分页共计'),
  tryFullTableFiltering: ft(
    'i18n_common_try_full_table_filtering_the_selected_state_of_the_current_page_will_be_reset',
    '试试全表过滤, 当前分页选中状态将重置'
  ),
  validationErrorOnLine: ft('i18n_common_validation_error_on_line', '第?行校验错误')
};

LABELS.pleaseSelectDate = `${LABELS.pleaseSelect}${LABELS.date}`;
LABELS.pleaseSelectDateTime = `${LABELS.pleaseSelect}${LABELS.date}${LABELS.time}`;

const EMBEDDED_DATATYPE = {
  date: '$input',
  datetime: '$input',
  input: 'input',
  number: '$input',
  money: '$input',
  popup: '$input',
  bizDic: '$select',
  select: '$select'
};

const NUMBER_DATATYPE = {
  number: 'number',
  money: 'money'
};

const RENDER_TYPE = {
  seq: 'seq',
  checkbox: 'checkbox',
  radio: 'radio',
  expand: 'expand',
  html: 'html'
};

export default {
  name: 'popVxeRender',
  props: {
    checkKey: {
      type: String,
      default: 'uuid'
    },

    data: {
      type: Array,
      default: () => {
        return [];
      }
    },

    // 是否展示头部的全表搜索
    hasSearchBar: {
      type: Boolean,
      default: true
    },

    height: {
      type: String,
      default: '500'
    },

    id: {
      type: String,
      default: '001'
    },

    loadingState: {
      type: Boolean,
      default: false
    },

    mergeKey: {
      type: Array,
      default: () => {
        return [];
      }
    },

    option: {
      type: Object,
      default: () => {
        return {};
      }
    },

    page: {
      type: Object,
      default: () => {
        return {
          pageSize: 50,
          currentPage: 0
        };
      }
    },

    readOnly: {
      type: Boolean,
      default: false
    },

    rowId: {
      type: String,
      default: 'uuid'
    },

    selection: {
      type: Boolean,
      default: true
    },

    selectionData: {
      type: Array,
      default: () => {
        return [];
      }
    },

    searchFormShow: {
      type: Boolean,
      default: false
    },

    // showOverflow: boolean | string, ellipsis(只显示省略号), title(并且显示为原生 title), tooltip(并且显示为 tooltip 提示)
    showOverflow: {
      type: String,
      default: 'title'
    },

    showPager: {
      // 是否展示分页
      type: Boolean,
      default: true
    },

    showType: {
      type: String,
      default: 'page'
    },

    sortable: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      labels: LABELS,
      formData: {},
      formItems: [
        {
          span: 24,
          children: []
        },
        {
          align: 'right',
          span: 24,
          itemRender: {
            name: '$buttons',
            children: [
              { props: { type: 'submit', content: LABELS.search, status: 'primary' } },
              { props: { type: 'reset', content: LABELS.reset } }
            ]
          }
        }
      ],

      list: this.data,
      loading: this.loadingState || false,
      isAllChecked: false,
      isReload: false,
      fullSelection: [],
      selectRecords: [],
      selectRecordsSize: 0,
      filterName1: '',
      validRules: {},

      checkboxConfig: {
        checkField: 'checked',
        checkRowKeys: this.defaultSelectedRows,
        reserve: true,
        highlight: true
      },

      keyboardConfig: { isArrow: true, isDel: true, isEnter: true, isTab: true, isEdit: true },
      menuConfig: { enabled: false },
      sortConfig: { trigger: 'cell' },
      tooltipConfig: { showAll: false },

      formatType: {
        date: this.formatDate,
        datetime: this.formatTime
      }
    };
  },
  watch: {
    data: {
      handler: function (newVal, oldVar) {
        this.list = newVal || [];
        if (this.list.length > 0) {
          this.loading = false;
        }
        this.getPageSelected();
      },
      deep: true,
      immediate: true
    },

    fullSelectionSize: function (newVal, oldVal) {
      this.calcSelectRecordsSize();
    },

    loadingState(newVal) {
      this.loading = newVal;
    },

    option: {
      handler: function (newVal, oldVar) {
        this.isReload = true;
      },
      deep: true,
      immediate: true
    },

    selectionData: {
      handler: function (newVal, oldVar) {
        this.fullSelection = newVal;
        this.getPageSelected();
      },
      deep: true,
      immediate: true
    }
  },
  computed: {
    defaultSelectedRows() {
      const list = this.fullSelection.map((item) => {
        const id = this.checkKey;
        item.checked = true;
        return item[id];
      });
      return list;
    },

    fullSelectionSize() {
      return this.fullSelection.length;
    },

    listSize() {
      return this.list.length;
    },

    selectionObj() {
      const obj = {};
      this.defaultSelectedRows.forEach((item) => {
        obj[item] = true;
      });
      return obj;
    },

    sequenceConfig() {
      return {
        seqMethod: this.generateSequence
      };
    },

    tableColumn() {
      if (this.isReload) {
        this.$nextTick(() => {
          this.isReload = false;
        });
      }

      return this.handlerColumn(this.option.column || []);
    }
  },

  methods: {
    activeRowMethod({ row, rowIndex }) {
      if (!this.readOnly) {
        return false;
      }
      this.$emit('row-click', row);

      return true;
    },

    add2selection(selection, fullItems = this.fullSelection) {
      const key = this.checkKey;

      const selectionKeyValue = selection[key];
      const fullSelection = this.fullSelection;

      const itemIndex = fullSelection.findIndex((item) => item[key] === selectionKeyValue);

      if (itemIndex < 0) {
        fullSelection.push(selection);
      }
    },

    add2selections(items = [], fullItems = this.fullSelection) {
      const key = this.checkKey;
      const existItems = {};

      for (const fullItem of fullItems) {
        existItems[fullItem[key]] = fullItem;
      }

      for (const item of items) {
        if (!existItems[item[key]]) {
          this.fullSelection.push(item);
        }
      }
    },

    calcSelectRecordsSize() {
      const xTable = this.$refs.xTable;

      if (xTable && xTable.getCheckboxRecords) {
        // const isAllSelected = xTable.isAllSelected;
        // const isIndeterminate = xTable.isIndeterminate;

        const checkboxRecords = xTable.getCheckboxRecords();

        this.selectRecordsSize = checkboxRecords.length;
      } else {
        this.selectRecordsSize = this.selectRecords.length;
      }
    },

    cellClickEvent({ row }) {
      if (!this.selection) {
        this.$emit('selection-change', [{ ...row }]);
      }
      // 多选不触发选中传递
      // console.log(`单元格点击${column.title}`);
    },

    cellContextMenuEvent({ row }) {
      // console.log(`右键行 ${row.name}`);
    },

    cellDBLClickEvent({ row }) {
      if (!this.selection) {
        this.$emit('selection-change', [{ ...row }]);
        this.$emit('close-dialog');
      }

      // console.log(`单元格双击${column.title}`);
    },

    cellMouseenterEvent({ column }) {
      this.$emit('cell-enter', column);
      // console.log(`鼠标进入单元格${column.title}`);
    },

    cellMouseleaveEvent({ column }) {
      this.$emit('cell-leave', column);
      // console.log(`鼠标离开单元格${column.title}`);
    },

    changeFilterEvent(event, option, $panel) {
      $panel.changeOption(event, !!option.data, option);
    },

    checkboxAllEvent({ records, reserves, indeterminates, checked, $event }) {
      this.checkboxChange({ records, reserves, indeterminates, checked, $event });
    },

    checkboxChange({
      records,
      reserves,
      indeterminates,
      checked,
      row,
      rowIndex,
      $rowIndex,
      column,
      columnIndex,
      $columnIndex,
      $event
    }) {
      this.isAllChecked = this.$refs.xTable.isAllCheckboxChecked();
      this.isIndeterminate = this.$refs.xTable.isAllCheckboxIndeterminate();

      if (checked && records.length > 0) {
        typeof rowIndex === 'undefined' ? this.add2selections(records) : this.add2selection(row);
      } else if (typeof rowIndex === 'undefined') {
        this.remove2selections(records);
      } else {
        this.remove2selection(row);
      }

      this.selectRecords = records;
      this.$emit('selection-change', this.fullSelection);
    },

    checkboxChangeEvent({
      records,
      reserves,
      indeterminates,
      checked,
      row,
      rowIndex,
      $rowIndex,
      column,
      columnIndex,
      $columnIndex,
      $event
    }) {
      this.checkboxChange({
        records,
        reserves,
        indeterminates,
        checked,
        row,
        rowIndex,
        $rowIndex,
        column,
        columnIndex,
        $columnIndex,
        $event
      });
    },

    clearSelection() {
      this.$refs.xTable.clearCheckboxRow();

      this.fullSelection = [];
      this.selectRecords = [];
    },

    countAllAmount(data) {
      return XEUtils.sum(data.map((row) => this.countAmount(row)));
    },

    countAmount(row) {
      return XEUtils.multiply(row.amount, row.number);
    },

    filterMethod({ value, row, column }) {
      const valueString = XEUtils.toValueString(row[column.property]);
      const isRight = valueString.toLowerCase().indexOf(value.toLowerCase()) > -1;
      return isRight;
    },

    formatter({ cellValue, column, columnIndex, row, rowIndex }) {
      const cellType = column?.type;

      return cellType && this.formatType[cellType]
        ? this.formatType[cellType]({ cellValue, column, columnIndex, row, rowIndex })
        : cellValue;
    },

    formatDate({ cellValue, column, columnIndex, row, rowIndex }) {
      return XEUtils.toDateString(cellValue, 'yyyy-MM-dd');
    },

    formatTime({ cellValue, column, columnIndex, row, rowIndex }) {
      return XEUtils.toDateString(cellValue, 'yyyy-MM-dd HH:mm:ss');
    },

    async fullValidEvent() {
      const $table = this.$refs.xTable;
      const errMap = await $table.fullValidate().catch((errMap) => errMap);

      if (errMap) {
        const msgList = [];
        Object.values(errMap).forEach((errList) => {
          errList.forEach((params) => {
            const { rowIndex, column, rules } = params;
            rules.forEach((rule) => {
              msgList.push(
                `${LABELS.validationErrorOnLine.replace('?', (rowIndex + 1).toString())} - ${
                  column.title
                }: ${rule.message}`
              );
            });
          });
        });
        this.$message.error({
          status: 'error',
          message: () => {
            return [
              <div class="red" style="max-height: 400px;overflow: auto;">
                {msgList.map((msg) => (
                  <div>{msg}</div>
                ))}
              </div>
            ];
          }
        });
        return false;
      } else {
        return true;
      }
    },

    /***
     * 生成表格的序号, 不分页时不计算当前页数
     * @see https://xuliangzhan_admin.gitee.io/vxe-table/?hmsr=aladdin1e5#/table/api
     */
    generateSequence({ seq }) {
      // { $rowIndex:. rowIndex, columnIndex, rowIndex, seq }
      const currentPage = this.page?.currentPage || 0;
      const pageSize = this.page?.pageSize;

      return currentPage > 0 && pageSize ? (currentPage - 1) * pageSize + seq : seq;
    },

    getAllProp() {
      const list = this.tableColumn.map((item) => {
        return item.field;
      });
      return list;
    },

    getFormatter(item = {}) {
      const cellType = item?.type;

      return cellType && this.formatType[cellType] ? this.formatType[cellType] : null;
    },

    getPageSelected() {
      this.list.forEach((item) => {
        if (this.selectionObj[item[this.checkKey]]) {
          this.selectRecords.push(item);
          this.$refs.xTable.setCheckboxRow([item], true);
        }
      });
    },

    getRender(item) {
      const ctype = item.datatype;
      let embeddedDatatype = EMBEDDED_DATATYPE[ctype];
      let prop = {};
      const obj = {};

      // 根据配置生成规则
      if (ctype === 'number') {
        prop = { type: 'number' };
      } else if (ctype === 'date' || item.type === 'date' || item.prop === 'createDate') {
        embeddedDatatype = '$input';
        prop = { type: 'date', placeholder: LABELS.pleaseSelectDate };
      } else if (ctype === 'datetime' || item.type === 'datetime') {
        embeddedDatatype = '$input';
        prop = { type: 'datetime', placeholder: LABELS.pleaseSelectDateTime };
      } else if (ctype === 'money') {
        embeddedDatatype = '$input';
        prop = { type: 'float', digits: 2 };
      } else if (ctype === 'bizDic' || item.bizDic) {
        embeddedDatatype = '$select';
        obj.options = item.dicData;
        obj.props = { clearable: true };
      } else if (item.prop === 'taxCode') {
        embeddedDatatype = '$select';
        obj.options = item.dicData;
        obj.events = { change: this.taxCodeChangeEvent };
      }

      obj.name = embeddedDatatype || 'input';
      obj.props = prop;

      if (obj.name.startsWith('$')) {
        Object.assign(obj.props, { disabled: !!item.disabled });
      } else {
        obj.attrs = { disabled: !!item.disabled };
      }

      if (item.isReadOnly === true || item.isReadOnly === 'Y') {
        obj.props = { disabled: true };
      }

      return obj;
    },

    getShowText(key, column) {
      let showText = key;
      const options = column.editRender.options;
      if (options) {
        const item = options.find((i) => i.value === key);
        if (item) {
          showText = item.label;
        }
      }
      return showText;
    },

    handlerColumn(column) {
      const searchForm = [];
      const list = column.map((item) => {
        const { label, displayName, prop } = item;
        const newItem = {
          align: item.align, // 对齐
          cell: item.cell, // 是否可编辑单元格
          field: prop,
          filters: item.dicData,
          isSearch: item.isSearch || item.search || false, // 是否查询
          popup: item.datatype === 'popup', // 是否默认弹窗
          prop: prop,
          readonly: item.isReadOnly === true || item.isReadOnly === 'Y',
          slot: item.slot, // 是否插槽
          title: displayName || label,
          type: RENDER_TYPE[item.type] || '', // 渲染类型
          width: item.width || false
        };

        newItem.editRender = this.getRender(item);

        this.setValidRule(item);

        this.setAlign(newItem, item);
        this.setSortable(newItem, item);
        this.setSlot(newItem, item);

        if (item.search) {
          const searchItem = cloneDeep(newItem);
          delete searchItem.editRender;
          searchItem.itemRender = this.getRender(item);
          searchItem.span = 8;

          // const formatter = this.getFormatter(item);

          // if (formatter) {
          //   newItem.formatter = formatter;
          // }
          searchForm.push(searchItem);
        }

        return newItem;
      });

      this.formItems[0].children = searchForm;

      return list;
    },

    handlePageChange({ type, currentPage, pageSize, $event }) {
      this.page.currentPage = currentPage;
      this.page.pageSize = pageSize;
      this.loading = true;
      this.selectRecords = [];
      this.$emit('update:page', this.page);
      this.$emit('page-change', this.page);
    },

    // 清空搜索条件
    handleReset(data) {
      this.formData = {};
      this.$emit('handle-reset');
    },

    // 搜索
    handleSearch(data) {
      this.$emit('handle-search', data.data);
    },

    remove2selection(selection) {
      const key = this.checkKey;
      const selectionKeyValue = selection[key];
      const fullSelection = this.fullSelection;

      const itemIndex = fullSelection.findIndex((item) => item[key] === selectionKeyValue);

      if (itemIndex > -1) {
        fullSelection.splice(itemIndex, 1);
      }
    },

    remove2selections(items = [], fullItems = this.fullSelection) {
      const key = this.checkKey;
      const pageItems = {};
      const newItems = [];

      for (const dataItem of this.data) {
        pageItems[dataItem[key]] = dataItem;
      }

      // 保留非当前页面数据
      for (const fullItem of fullItems) {
        if (!pageItems[fullItem[key]]) {
          newItems.push(fullItem);
        }
      }

      // 添加当前页选中数据
      newItems.concat(items);

      this.fullSelection = newItems;
    },

    searchEvent() {
      const filterName = XEUtils.toValueString(this.filterName1).trim().toLowerCase();

      if (filterName) {
        const filterRE = new RegExp(filterName, 'gi');
        const searchProps = this.getAllProp();
        const rest = this.data.filter((item) =>
          searchProps.some(
            (key) => XEUtils.toValueString(item[key]).toLowerCase().indexOf(filterName) > -1
          )
        );
        this.list = rest.map((row) => {
          const item = Object.assign({}, row);
          searchProps.forEach((key) => {
            item[key] = XEUtils.toValueString(item[key]).replace(filterRE, (match) => `${match}`);
          });
          return item;
        });
      } else {
        this.list = this.data;
      }

      this.selectRecords = [];
    },

    setAlign(newItem, item) {
      if (
        NUMBER_DATATYPE[item.datatype] ||
        item.label.includes('数量') ||
        item.label.includes('款') ||
        item.label.includes('价') ||
        item.label.includes('金额') ||
        item.label.includes('总额')
      ) {
        newItem.align = 'right';
      }
    },

    setSlot(newItem, item) {
      if (item.datatype === 'popup') {
        newItem.slot = true;
      }
    },

    setSortable(newItem, item) {
      if (
        NUMBER_DATATYPE[item.datatype] ||
        item.label.includes('量') ||
        item.label.includes('行号') ||
        item.label.includes('编号') ||
        item.label.includes('编码') ||
        item.label.includes('时间') ||
        item.label.includes('款') ||
        item.label.includes('价') ||
        item.label.includes('金额') ||
        item.label.includes('总额') ||
        item.label.includes('日期') ||
        item.sortable
      ) {
        newItem.sortable = true;
      }
    },

    setValidRule(item) {
      if (
        item.prop &&
        item.rules &&
        Array.isArray(item.rules) &&
        item.rules.length > 0 &&
        !this.validRules[item.prop] &&
        (item.isReadOnly === true || item.isReadOnly !== 'Y')
      ) {
        const ruleItems = [...item.rules];

        this.validRules[item.prop] = ruleItems;
      }
    },

    taxCodeChangeEvent(data, { $event, value }) {
      // { row, rowIndex, $rowIndex, column, columnIndex, $columnIndex } = data
      const { row } = data;
      this.$emit('tax-code-change', { $event, value, row });
    }
  }
};
</script>
<style>
.fullBox .vxe-input--inner {
  height: 39px;
  width: 100%;
}
.fullBox.vxe-input {
  width: 100% !important;
  margin-right: 10px;
}
.topbar {
  padding-bottom: 8px;
}
.topSearch .ant-btn {
  height: auto;
}
</style>
