<template>
  <div class="row-list">
    <!--  batch addition -->
    <template v-if="batchAdditionComponent.component">
      <component
        :api="listApi[batchAdditionComponent.prop]"
        :is="batchAdditionComponent.component"
        :batchAddition="batchAdditionComponent.batchAddition"
        :multiple="batchAdditionComponent.multiple"
        :listParams.sync="batchAdditionComponent.listParams"
        :option="popupOption"
        :ref="batchAdditionComponent.refName"
        :selected.sync="batchAdditionComponent.prop"
        @cancelBatchSelected="
          doSelect(
            'cancelBatchSelected',
            scope,
            $event,
            batchAdditionComponent.option || batchAdditionComponent.params
          )
        "
        @onOpenBatchAddition="handleOpenBatchAddition"
        @selectDone="
          doSelect(
            batchAdditionComponent.func,
            scope,
            $event,
            batchAdditionComponent.option || batchAdditionComponent.params
          )
        "
      />
    </template>

    <el-button
      v-if="addBtn && !readOnly && !enableBatchAddition && !multiple"
      size="small"
      @click.stop="addRow()"
    >
      {{ labels.addRow }}
    </el-button>
    <el-button v-if="showImportExcelButton" size="small" @click.stop="handleDownloadExcel()">
      {{ labels.downExcelTemplate }}
    </el-button>

    <fast2-upload
      v-if="showImportExcelButton"
      ref="upload"
      :accept="excelXlsx"
      :btnName="labels.importExcel"
      :isElementBtn="true"
      @file-error="onFileError"
      @file-progress="onFileProgress"
      @file-success="onFileSuccess"
    />

    <slot name="innerTopLeftBar" />
    <slot name="innerTopRightBar" />
    <avue-crud
      v-if="reload"
      ref="crud"
      v-model="obj"
      v-on="$listeners"
      :cell-class-name="setCellClassName"
      :cell-style="cellStyle"
      :class="{ 'manage-list': isManageList }"
      :data="data"
      :option.sync="finalOption"
      :page.sync="ipage"
      :row-class-name="rowClassNameFn"
      :row-style="rowStyle"
      :span-method="spanMethod"
      @row-del="rowDelete"
      @row-update="rowUpdate"
      @selection-change="selectionChange"
    >
      <!-- 自定义 slots -->
      <template
        v-for="customSlotItem in customSlotItems"
        :slot="customSlotItem.prop"
        slot-scope="{ row }"
      >
        <slot
          v-if="customSlotItem && customSlotItem.prop"
          :slot-scope="row"
          :name="customSlotItem.slotName"
        />
      </template>

      <template slot="menu" slot-scope="{ row }">
        <slot :slot-scope="row" name="operation" />
      </template>

      <template v-if="ticketNumber && getTicketURLFn" :slot="ticketNumber" slot-scope="{ row }">
        <router-link class="ticket-number" :to="getTicketURLFn(row)">
          {{ row[ticketNumber] }}
        </router-link>
      </template>

      <template v-for="item in itemLinkList" v-slot:[item.prop]="scope">
        <span :key="item.prop">
          <span
            v-if="
              heightLightItemLink === 1 &&
              (readOnly || scope.row.isReadOnly || !scope.row.$cellEdit)
            "
            :key="item.prop"
          >
            {{ scope.row[item.prop] }}
          </span>
          <span v-else-if="readOnly || scope.row.isReadOnly || !scope.row.$cellEdit">{{
            scope.row[item.prop]
          }}</span>
          <component
            v-else
            :is="item.component"
            :multiple="multiple"
            :api="listApi[item.prop]"
            :listParams.sync="listParams[item.prop]"
            :selected.sync="scope.row[item.prop]"
            :option="popupOption"
            @selectDone="doSelect(item.func, scope.row, $event, item.option || item.params)"
          />
        </span>
      </template>
    </avue-crud>
  </div>
</template>
<script>
import cloneDeep from 'lodash/cloneDeep';

import { EXCEL_XLSX } from '../../../lib/const.js';
import { ft, generateRandomString, simulationDownload } from '../../../lib/utils.js';
import { checkDataType, setRequired, setCellStyle } from '../core/utils';
import popList from '../../../lib/popList';
// import materialsFix from '../../../lib/materials-fix';
import { i18nConfig } from '../../../mixins/i18n';
import { DownloadAndImportService } from '../../../lib/api/downloadImport';
import dayjs from 'dayjs';

const LABELS = {
  addRow: ft('i18n_common_add_row', '添加行'),
  cancel: ft('i18n_common_cancel', '取消'),
  continueImport: ft('i18n_common_continue_to_import', '继续导入'),
  downExcelTemplate: ft('i18n_common_download_excel_template', '下载Excel模板'),
  existingDataWillBeClearAndContinue: ft(
    'i18n_common_existing_data_will_be_cleared_whether_to_continue_importing',
    '已有数据将被清除, 是否继续导入?'
  ),
  failedToImportExcel: ft('i18n_common_failed_to_import_excel', '导入Excel数据失败'),
  importExcel: ft('i18n_common_import_excel', '导入Excel数据'),
  materialDetails: ft('i18n_common_material_details', '物料明细'),
  materialNumber: ft('i18n_common_material_number', '物料编码'),
  noData: ft('i18n_common_no_data', '暂无数据'),
  pleaseSelect: ft('i18n_common_please_select', '请选择 '),
  sameMaterialExistInLine: ft(
    'i18n_common_same_material_exists_in_line',
    '第?行, 已存在相同的物料'
  ),
  serialNumber: ft('i18n_common_serial_number', '序号'),
  uploadFailedPleaseTry: ft('i18n_common_upload_failed_please_try_again', '上传失败，请重试'),
  uploading: ft('i18n_common_uploading', '上传中...')
};

export default {
  /**
   * 导入导出说明
   * @param businessTypeProperty { String } Excel 模板下载接口所需参数，询价类型
   * @param templateName 指定下载模板文件的名称
   * @param showImportExcelButton { Boolean } 是否显示 Excel 导入、导出按钮，默认为 false
   * @event importExcel 侦听Excel导入行信息后，获取接口返回的行数据
   */
  name: 'RowPageProvider',
  mixins: [i18nConfig],
  props: {
    /*
     * 导入导出 begin
     */

    // businessTypeProperty: excel 模板, 询价类型
    businessTypeProperty: {
      type: String,
      default: ''
    },

    // 导入接口。
    importUrl: {
      type: String,
      default: ''
    },

    // showImportExcelButton: excel 模板 是否显示 导入行信息按钮
    showImportExcelButton: {
      type: Boolean,
      default: false
    },

    // templateName，指定下载导入模板文件的文件名
    templateName: {
      type: String,
      default: ''
    },

    // toElsAccount: 根据els号来校验导入模板是否正确
    toElsAccount: {
      type: String,
      default: ''
    },

    /*
     * 导入导出 end
     */

    // addBtn: 是否显示添加按钮
    addBtn: {
      type: Boolean,
      default: true
    },

    // batchAdditionProperty: 启用 批量添加, 明细表格对应的字段, 用于匹配弹出窗口组件进行批量选择
    // 使用之前需要确认内置的表格弹出窗口组件是否已做相应的修改, 参考 reconNumber.vue, 关键字 batchAddition
    // fast2render/packages/components/render/dialog/reconNumber.vue
    batchAdditionProperty: {
      type: String,
      default: ''
    },

    // cellStyle: 单元格样式
    cellStyle: {
      type: Function
    },

    // customSlots: 自定义 slots, 只输出 slot name, 具体内容由使用者定义(不在组件内部定义), 最多支持 9 个自定义 slot
    customSlots: {
      type: Array,
      default: function () {
        return [];
      }
    },

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

    /*
     *  批量添加功能
     *  配置属性 启用 批量添加功能标识 enableBatchAddition
     *  配置属性 批量添加 明细表格对应的字段: batchAdditionProperty
     *  配置方法, 批量添加数据时检查已有数据，以避免重复数据: existingItemFn(item, exitsItems)
     *  初始化组件后，再初始化 批量添加组件 batchAdditionComponent.setBatchAdditionComponent
     *  内置弹出窗口组件支持批量选择, 参考 packages/components/render/dialog/order/vue or reconNumber.vue, 关键字 batchAddition
     */

    // enableBatchAddition: 启用 批量添加功能
    enableBatchAddition: {
      type: Boolean,
      default: false
    },

    // enAbleI18n: 是否支持多语言
    enAbleI18n: {
      type: Boolean,
      default: true
    },

    // excludePreSlots: 排除组件内置的预定义的字段(属性)Slot, 例如 :excludePreSlots=['materialNumber']
    excludePreSlots: {
      type: Array,
      default: function () {
        return [];
      }
    },

    // heightLightItemLink: 高亮显示 slot 关联的 key
    heightLightItemLink: {
      type: Number,
      default: 1
    },

    // isManageList: 是否管理列表
    isManageList: {
      type: Boolean,
      default: false
    },

    // itemLink: 是否以只读模式展现 slot 关联的组件
    itemLink: {
      type: Boolean,
      default: false
    },

    // itemLinkList: 组件内置的预定义的字段(属性)Slot
    itemLinkList: {
      type: Array,
      default: function () {
        return cloneDeep(popList);
      }
    },

    itemUploadList: {
      type: Array,
      default: function () {
        return [];
      }
    },

    listApi: {
      type: Object,
      default: function () {
        return {};
      }
    },

    listParams: {
      type: Object,
      default: function () {
        return {};
      }
    },

    // menuState：列表菜单
    menuState: {
      type: Boolean,
      default: false
    },

    // multiple: 是否多选
    multiple: {
      type: Boolean,
      default: false
    },

    option: {
      type: Object,
      default: function () {
        return { column: [] };
      }
    },

    page: {
      type: Object,
      default: function () {
        return {};
      }
    },

    popupOption: {
      type: Object,
      default: function () {
        return {};
      }
    },

    // readOnly: 是否只读模式
    readOnly: {
      type: Boolean,
      default: false
    },

    // rowStyle: 行样式
    rowStyle: {
      type: Function
    },

    // ticketNumber: 单据编号 (如采购申请单号)
    ticketNumber: {
      type: String,
      default: function () {
        return '';
      }
    },

    // type: 远程获取 表格字段数据配置- 后续扩充 from 类型
    type: {
      type: String,
      default: 'crud'
    },

    /* The functions are as follows */

    /**
     * @property {Function} handleMetadataFn - 设置表格元数据属性的方法
     */
    handleMetadataFn: {
      type: Function,
      default: null
    },

    // getTicketURLFn: 根据单据编号计算对应的 URL, 如编辑或详情页面的 URL
    getTicketURLFn: {
      type: Function,
      default: function () {
        return null;
      }
    },
    /**
     * 添加空行之前，进行前置操作
     *
     * @returns {integer} -1 中止操作不再添加数据, 1 继续添加数据的操作
     * @example
     *  addEmptyRowBeforeProcFn() {
     *   if (需要继续添加数据的操作) {
     *     return 1
     *   } else {
     *     // 不满足条件, 中止操作，不再添加数据
     *     return -1
     *  }
     * }
     */
    addEmptyRowBeforeProcFn: {
      type: Function,
      default: null
    },

    /**
     * 添加空行时，对数据进行初始化处理
     * @example
     *  addEmptyRowProc(item) {
     *   item.budgetPrice = '0';
     *   item.quantity = '0';
     *   item.subtotalAmount = '0';
     *
     *   return item;
     * }
     */
    addEmptyRowProcFn: {
      type: Function,
      default: null
    },

    /**
     * 打开批量添加对话框之前的验证, 不满足条件(throw Error)，不打开对话框
     */
    beforeOpenBatchAdditionFn: {
      type: Function,
      default: function () {
        return true;
      }
    },

    // cellClassNameFn: 用于设置 列 的 className, 参数: row, column, rowIndex, columnIndex
    cellClassNameFn: {
      type: Function,
      default: null
    },

    // cellClassNameFn: 添加数据时检查已有数据
    existingItemFn: {
      type: Function,
      default: null
    },

    // rowClassNameFn: 用于设置 行 的 className, 参数: row, rowIndex
    rowClassNameFn: {
      type: Function,
      default: null
    },

    /**
     * 编辑后更新行数据时，对数据进行处理，如果返回-1则可以不保存行数据
     * @example
     *  rowUpdateProc(data) {
     *    const budgetPriceString = Number(data.budgetPrice).toFixed(2);
     *
     *    const quantityString = data.quantity.toString();
     *
     *    data.budgetPrice = budgetPriceString;
     *    data.quantity = quantityString;
     *
     *    const subtotalAmount = format(
     *    chain(bignumber(budgetPriceString)).multiply(bignumber(quantityString)).done()
     *
     *    data.subtotalAmount = Number(subtotalAmount).toFixed(2);
     *    return -1
     * }
     */
    rowUpdateProcFn: {
      type: Function,
      default: null
    },

    /**
     * 打开弹窗选择一个数据, 确定后, 更新数据后对选取的数据进行处理
     * @example
     *  selectedItemProcFn(row, item) {
     *   row.queryUuid = row.uuid;
     *   delete item.uuid;
     * }
     */
    selectedItemProcFn: {
      type: Function,
      default: null
    },

    /**
     * 更新并且合并后对数据再处理, 修复 saveSelectedAll 方法中 selectedItemProcFn 处理后的行数据被 原始数据覆盖的问题
     * @example
     *  selectedItemAfterProcFn(row, item) {
     *   row.returnQuantity = item.noDeliverQuantity;
     * }
     */
    selectedItemAfterProcFn: {
      type: Function,
      default: null
    },

    /**
     * 打开弹窗选择一个数据, 确定后, 更新数据前对选取的数据进行检查处理, 不符合要求时返回 -1
     * 通常于用避免配置重复数据
     * @example
     *  selectingItemProcFn(row, item, data) {
     *   row.queryUuid = row.uuid
     * }
     */
    selectingItemProcFn: {
      type: Function,
      default: null
    },

    // spanMethod: 合并单元格，avue 内部方法
    spanMethod: {
      type: Function
      // default: function () {
      //   return null;
      // }
    }
  },
  data() {
    return {
      labels: LABELS,
      excelXlsx: EXCEL_XLSX,

      finalOption: {
        rowKey: 'prop'
      },
      reload: false,
      optionHash: '',
      obj: {},
      defaultValues: {},
      materialsColumn: [],
      showColumnProps: null,
      fitColumnWidth: false,

      requiredColumns: {},
      ipage: {},

      batchAdditionComponent: {
        addBtnText: '',
        component: ''
      },

      customSlotItems: []
    };
  },
  watch: {
    customSlots: {
      handler: async function (newVal, oldVar) {
        if (newVal) {
          this.handleCustomSlotsChanged(newVal);
        }
      },
      immediate: true,
      deep: true
    },
    // 列表菜单
    menuState(newVal) {
      this.finalOption.menu = newVal;
    },

    option: {
      handler: async function (newVal, oldVal) {
        if (newVal) {
          this.handleOptionChanged(newVal, oldVal);
        }
      },
      immediate: true,
      deep: true
    },

    page: {
      handler: async function (newVal, oldVar) {
        if (newVal) {
          this.ipage = newVal;
        }
      },
      immediate: true
    }
  },
  created() {
    if (this.readOnly) {
      this.finalOption.menu = this.menuState === true;
    }
  },
  methods: {
    addEmptyRow(option = { callback: null, callbackParams: {} }) {
      const item = {
        $cellEdit: true,
        id: `${new Date().valueOf()}${generateRandomString(6)}`
      };

      const showColumnProps = this.getShowColumnProps();

      for (const prop in showColumnProps) {
        item[prop] = this.defaultValues[prop] || '';
      }

      if (typeof this.addEmptyRowProcFn === 'function') {
        this.data.push(this.addEmptyRowProcFn(item));
      } else {
        this.data.push(item);
      }

      // TODO: 新增行数据时, avue 表格 body 部分上面的位置计算有误, 暂时在这里补偿，避免操作栏部分向上偏移
      this.$nextTick(() => {
        const elm = this.$refs.crud.$vnode.elm;

        const headerWrapper = elm.querySelector('.el-table__header-wrapper');

        const fixedRightBodyWrapper = elm.querySelector(
          '.el-table__fixed-right .el-table__fixed-body-wrapper'
        );

        const fixedRightBodyWrapperTop = parseInt(fixedRightBodyWrapper.style.top || '0', 10);

        if (fixedRightBodyWrapperTop === 0) {
          const headerWrapperHeight = headerWrapper.clientHeight;

          if (headerWrapperHeight > 0) {
            fixedRightBodyWrapper.style.top = `${headerWrapperHeight}px`;
          } else {
            fixedRightBodyWrapper.style.top = '';
          }
        }
      });

      typeof option.callback === 'function' && option.callback(item, option.callbackParams);
    },

    addRow() {
      if (this.addEmptyRowBeforeProcFn && typeof this.addEmptyRowBeforeProcFn === 'function') {
        if (this.addEmptyRowBeforeProcFn() > -1) {
          this.addEmptyRow();
        }
      } else {
        this.addEmptyRow();
      }
    },

    cancelBatchSelected() {
      const batchAdditionRef = this.$refs[this.batchAdditionComponent.refName];
      batchAdditionRef.dialogVisible = false;
      return -1;
    },

    async changeOption(finalOption) {
      this.reload = false;

      let cellBtn;

      if (typeof finalOption.cellBtn === 'boolean') {
        cellBtn = finalOption.cellBtn;
      }

      const fitColumnWidth =
        finalOption.fit === true || this.isManageList === true || this.isManageList === 'true';

      finalOption.fit = fitColumnWidth;
      this.fitColumnWidth = fitColumnWidth;

      this.filterPreSlots();

      this.finalOption = await this.filterColum(finalOption);

      if (typeof this.handleMetadataFn === 'function') {
        this.handleMetadataFn(this.finalOption.column);
      }

      this.finalOption.editBtn = false;

      if (!this.readOnly) {
        this.finalOption.cellBtn = true;
      }

      this.$nextTick(() => {
        this.reload = true;

        // 初始化后 cellBtn 会被重置为 true, 不能满足权限控制, 因此需要按配置重新赋值
        if (typeof cellBtn === 'boolean') {
          this.finalOption.cellBtn = cellBtn;
        }
      });
    },

    checkAddedMaterials(list) {
      const data = this.data;

      const addMaterialList = [];
      const existsList = [];
      let message = '';

      if (data.length < 1) {
        return {
          addList: list,
          existsList,
          message
        };
      } else {
        const materialNumberList = [];

        data.map((item) => {
          item.materialNumber && materialNumberList.push(item.materialNumber);
        });

        const index = 1;

        for (const item of list) {
          if (item.materialNumber && materialNumberList.includes(item.materialNumber)) {
            existsList.push({
              materialNumber: item.materialNumber,
              name: item.materialDesc || item.materialName || ''
            });

            if (!message) {
              message = `${LABELS.sameMaterialExistInLine.replace('?', index.toString())}: ${
                LABELS.materialNumber
              }(${item.materialNumber})`;
            }
          } else {
            addMaterialList.push(item);
          }
        }
      }

      return {
        addList: addMaterialList,
        existsList,
        message
      };
    },

    checkSlot(item) {
      const itemLinkList = this.itemLinkList;

      let itemLinkIndex = 0;

      // 列表页主键与 内部预置的 自定义的 column 冲突时, 按 主键的 形式展现, 见上面 template 中的 slot="ticketNumber"
      for (const item of itemLinkList) {
        if (this.ticketNumber && item.prop === this.ticketNumber) {
          itemLinkList.splice(itemLinkIndex, 1);
          break;
        }

        itemLinkIndex = itemLinkIndex + 1;
      }

      // 列表主键设置跳转路由 slot
      if (this.ticketNumber && item.prop === this.ticketNumber && this.getTicketURLFn) {
        item.slot = true;
        item.align = 'center';
      }

      this.itemLinkList.map((linkItem) => {
        // 加入只读判断 只读模式不使用slot
        if (item.prop === linkItem.prop && item.isReadOnly !== 'Y') {
          item.slot = true;
        }
      });

      if (item.datatype === 'uploadImg' || item.prop === 'picture') {
        this.itemUploadList.push(item);
      }

      if (item.datatype === 'uploadFile' || item.prop === 'url') {
        item.datatype = 'uploadFile';
        item.slot = true;
        this.itemUploadList.push(item);
      }

      this.itemUploadList.map((linkItem) => {
        if (item.prop === linkItem.prop) {
          item.slot = true;
        }
      });
    },

    async confirmImport(file) {
      return new Promise((resolve, reject) => {
        this.$confirm({
          title: LABELS.existingDataWillBeClearAndContinue,
          content: '',
          okText: LABELS.continueImport,
          cancelText: LABELS.cancel,
          onOk() {
            resolve(true);
          },
          onCancel() {
            reject(new Error());
          }
        });
      });
    },

    // slot 弹框 处理
    doSelect(func, row, event, params = []) {
      if (func === 'saveBatchSelected') {
        this[func](event, params);
      } else {
        const result = this[func](row, event, params);

        if (result !== -1) {
          this.$forceUpdate();
        }
      }
    },

    async filterColum(option) {
      if (!(option && option.column)) {
        return [];
      }

      option.emptyText = LABELS.noData;

      const columns = option.column;

      const customSlotPropItems = {};

      const customSlots = this.customSlots || [];

      customSlots.forEach((item) => {
        if (item.prop) {
          customSlotPropItems[item.prop] = 1;
        }
      });

      for (const item of columns) {
        if (customSlotPropItems[item.prop] && item.slot !== true) {
          item.slot = true;
        }

        if (this.enAbleI18n) {
          this.setI18n(item);
        } else {
          this.setDisplayName(item);
        }

        await checkDataType(item);

        if (typeof item.value !== 'undefined') {
          this.defaultValues[item.prop] = item.value;
          delete item.value;
        } else if (item.type === 'radio' && item.dicData && item.dicData.length > 0) {
          if (item.dicData[0] && typeof item.dicData[0].value !== 'undefined') {
            this.defaultValues[item.prop] = item.dicData[0].value;
          }
        }

        this.checkSlot(item);

        const isRequired = item.isRequired === true || item.isRequired === 'Y';

        if (isRequired) {
          this.requiredColumns[item.prop] = 1;

          setRequired(item);
        }

        if (
          item.isDisabled === 'Y' ||
          item.isReadOnly === 'Y' ||
          this.readOnly ||
          item.ref ||
          item.disabled === 'disabled'
        ) {
          item.disabled = 'disabled';
          item.cell = false;
          // item.rules = [];
        } else {
          item.cell = true;
        }

        setCellStyle(item, {
          fitColumnWidth: this.fitColumnWidth,
          isManageList: this.isManageList,
          readOnly: this.readOnly === true || this.readOnly === 'true'
        });

        item.display = true;
        // 如果是系统必填字段
        if (item && item.isRequired === 'Y') {
          this.requiredColumns[item.prop] = 1;

          setRequired(item);

          if (item.type === 'varchart' && item.isUserInput === 'N') {
            item.cell = false;
          }

          item.display = true;
        }
      }

      return option;
    },

    filterPreSlots() {
      if (this.excludePreSlots.length > 0) {
        const preSlots = this.itemLinkList.filter((item) => {
          return !this.excludePreSlots.includes(item.prop);
        });

        this.itemLinkList = preSlots;
      }
    },

    /* ! 删除。不知道起什么作用。如有需要再恢复
    getMaterialsRefs() {
      const refs = [];
      materialsFix.map((item) => {
        if (item.ref) {
          refs.push(item.prop);
        }
      });

      return refs;
    },
    */

    getSelectRefs(type) {
      const refs = [];
      this.finalOption.column.map((item) => {
        if (item.datatype !== 'popup' && item.isSystem !== 'Y') {
          refs.push(item.prop);
        }
      });
      return refs;
    },

    getShowColumnProps() {
      const showColumnProps = this.showColumnProps || {};

      if (!this.showColumnProps && this.reload) {
        const columns = this.$refs.crud.option.column;

        columns.forEach((item) => {
          showColumnProps[item.prop] = 1;
        });

        this.showColumnProps = showColumnProps;
      }

      return this.showColumnProps;
    },

    go(item, row) {
      if (window.parent) {
        const router = {
          name: item.label,
          src: item.url + row[item.prop]
        };

        const event = {
          name: 'openNewTag',
          props: router
        };

        window.parent.postMessage(event, '*');
      } else {
        window.location.href = item.url;
      }
    },

    handleCustomSlotsChanged(customSlots = []) {
      for (const customSlot of customSlots) {
        if (customSlot && customSlot.prop && customSlot.slotName) {
          this.customSlotItems.push(customSlot || {});
        }
      }
    },

    // excel 模板: 下载
    handleDownloadExcel() {
      DownloadAndImportService.downloadTemplate(this.businessTypeProperty, this.templateName).then(
        (res) => {
          // 处理返回的文件流
          const date = dayjs(new Date()).format('YYYYMMDDHHmmss');
          const fileName = `${this.templateName}_${date}.xlsx`;

          simulationDownload(res.data, fileName);
        }
      );
    },

    handleOpenBatchAddition() {
      try {
        this.beforeOpenBatchAdditionFn();

        this.batchAdditionComponent.listParams =
          this.listParams[this.batchAdditionComponent.prop] || {};

        const batchAdditionRef = this.$refs[this.batchAdditionComponent.refName];
        batchAdditionRef.dialogVisible = true;
      } catch (err) {
        console.log(err);
      }
    },

    async handleOptionChanged(optionValue, oldOptionValue) {
      const optionValData = cloneDeep(optionValue);
      delete optionValData.data;

      if (typeof optionValData.index !== 'boolean') {
        optionValData.index = true;
        optionValData.indexLabel = LABELS.serialNumber;
      }

      if (oldOptionValue) {
        const oldOptionValData = cloneDeep(oldOptionValue);
        delete oldOptionValData.data;

        const optionString = JSON.stringify(optionValData);
        const oldOptionString = JSON.stringify(oldOptionValData);

        if (optionString !== oldOptionString) {
          await this.changeOption(optionValData);
        }
      } else {
        await this.changeOption(optionValData);
      }
    },

    isUploadAble(item) {
      if (this.readOnly) {
        return false;
      }
      if (item.datatype === 'uploadImg' || item.datatype === 'uploadFile') {
        if (item.isReadOnly !== 'Y') {
          return true;
        }
      }
      return false;
    },

    async importExcel(data) {
      if (this.data.length > 0) {
        try {
          await this.confirmImport();
          this.importExcelData(data);
        } catch (err) {
          return false;
        }
      } else {
        this.importExcelData(data);
      }
    },

    importExcelData(data) {
      const params = {
        filePath: data.url || '',
        toElsAccount: this.toElsAccount
      };

      DownloadAndImportService.importExcel(params, this.importUrl)
        .then((res) => {
          if (res.data && res.data.code === '200' && res.data.data) {
            const resData = res.data.data || [];
            this.$emit('importExcel', resData);
          } else {
            console.log('importExcel fail', res.data);
            this.$message.error(res.data.message || LABELS.failedToImportExcel);
          }
        })
        .catch((err) => {
          console.log('importExcel error', err);
          this.$message.error(LABELS.failedToImportExcel);
        });
    },

    // excel 模板: 导入文件上传失败
    onFileError(rootFile, file, message, chunk) {
      this.$message.error(LABELS.uploadFailedPleaseTry);
    },

    // excel 模板: 导入文件上传中
    onFileProgress(rootFile, file, chunk) {
      console.log(LABELS.uploading);
    },

    // excel 模板: 导入文件上传成功
    onFileSuccess(rootFile, file, message, chunk) {
      this.$refs.upload.removeFile(file);

      const data = JSON.parse(message).data;

      this.importExcel(data);
    },

    resetBatchAdditionSComponent() {
      this.batchAdditionComponent = {
        addBtnText: '',
        component: ''
      };
    },

    rowDelete(row, index) {
      this.$emit('row-del', row);
      this.data.splice(index, 1);
    },

    // $ft(code, str) {
    //   return ft(code, str);
    // },

    rowUpdate(row, index, done, loading) {
      const data = this.data[index];

      if (row.queryUuid) {
        data.queryUuid = row.queryUuid;
      }

      // TODO: 强行添加 taxCode
      data.taxRate = data.taxCode;

      Object.assign(data, row);

      if (typeof this.rowUpdateProcFn === 'function') {
        // 如果返回的flag为-1，则代表改行数据有条件未满足，不能保存。
        const flag = this.rowUpdateProcFn(data);
        if (flag === -1) {
          loading();
          return;
        }
      }

      done();
    },

    saveBatchSelected(selectItems, option) {
      if (!this.batchAdditionProperty) {
        return;
      }

      const batchAdditionRef = this.$refs[this.batchAdditionComponent.refName];
      batchAdditionRef.dialogVisible = false;

      const existingItemFn =
        this.existingItemFn && typeof this.existingItemFn === 'function'
          ? this.existingItemFn
          : function () {
              return false;
            };

      for (const selectItem of selectItems) {
        const exists = existingItemFn(selectItem, this.data);

        if (!exists) {
          this.addEmptyRow({
            callback: (row, { selectItems, option }) => {
              this.saveSelected(row, selectItems, option);
            },
            callbackParams: { selectItems: [selectItem], option }
          });
        }
      }
    },

    saveSelected(row, list, option) {
      const item = list[0];

      if (this.selectingItemProcFn && typeof this.selectingItemProcFn === 'function') {
        if (this.selectingItemProcFn(row, item, this.data) === -1) {
          return -1;
        }
      }

      if (option.isOption === true) {
        const dataValue = item[option.dataValueProp];
        const dataName = item[option.dataNameProp];

        if (typeof dataValue !== 'undefined' && typeof row[option.valueProp] !== 'undefined') {
          row[option.valueProp] = dataValue;
        }

        if (typeof dataName !== 'undefined') {
          const nameProp = option.nameProp;

          if (nameProp && typeof row[nameProp] !== 'undefined') {
            row[nameProp] = dataName;
          } else {
            const aliasProps = option.aliasProps || [];
            for (const aliasProp of aliasProps) {
              if (typeof row[aliasProp] !== 'undefined') {
                row[aliasProp] = dataName;
                break;
              }
            }
          }
        }
      } else {
        const params = option;

        if (params) {
          row[params[0]] = item[params[2]];
          row[params[1]] = item[params[3]];
        }
      }

      if (this.selectedItemProcFn && typeof this.selectedItemProcFn === 'function') {
        this.selectedItemProcFn(row, item);
      }

      const refs = this.getSelectRefs();

      refs.map((prop) => {
        const isItem = !!item[prop];
        if (isItem) {
          row[prop] = item[prop];
        }
      });
    },

    saveSelectedAll(row, list, params) {
      const item = list[0];

      if (this.selectedItemProcFn && typeof this.selectedItemProcFn === 'function') {
        this.selectedItemProcFn(row, item);
      }

      Object.assign(row, item);

      if (this.selectedItemAfterProcFn && typeof this.selectedItemAfterProcFn === 'function') {
        this.selectedItemAfterProcFn(row, item);
      }
    },

    /**
     * 一键保存 明细数据, 用于外部调用
     * @example
     * this.$refs.crud.saveWithClick()
     */
    async saveWithClick() {
      this.data.forEach((item, index) => {
        this.$refs.crud.rowCellUpdate(item, index);
      });
    },

    selectedRowMaterails(row, materialList) {
      if (materialList && materialList.length > 0) {
        const result = this.checkAddedMaterials(materialList);

        if (result.existsList.length > 0) {
          if (result.message) {
            this.$message.warning(result.message, 5);
          }

          return;
        }

        const refs = this.getSelectRefs('materialNumber');
        const addMaterialList = result.addList;

        for (const material of addMaterialList) {
          refs.map((prop) => {
            row[prop] = material[prop] || '';
          });

          // 创建. 修改采购申请(新增的行将物料的uuid放入queryUuid字段), 重新选择了物料也要更新 queryUuid
          row.queryUuid = material.uuid;
          row.materialNumber = material.materialNumber;

          // 新选择的物料, 不要 uuid，也避免用物料的 uuid 覆盖表格数据自身的 uuid
          delete material.uuid;
          // ! 删除。不知道起什么作用。如有需要再恢复 start
          // const materialObject = row;
          // Object.assign(materialObject, material);
          // ! 删除。不知道起什么作用。如有需要再恢复 end
          // 设置与物料编辑关联的表单编辑状态
          // this.disableOpenForms(item.materialNumber);
        }
      } else {
        this.$message.warning(`${LABELS.pleaseSelect}${LABELS.materialDetails}`);
      }
    },

    selectionChange(selections) {
      this.$emit('selection-change', selections);
    },

    /**
     * 外部调用-设置批量添加组件
     * @param(string) property
     * @see packages/components/render/dialog/order.vue
     * @example this.$refs.crud.setBatchAdditionComponent('order')
     */
    setBatchAdditionComponent(property = '') {
      if (property) {
        const predefinedSlot = this.itemLinkList.find((item) => {
          return item.prop === property;
        });

        if (predefinedSlot) {
          const batchAdditionComponent = cloneDeep(predefinedSlot);
          batchAdditionComponent.refName = `batchAdd${predefinedSlot.prop}`;
          batchAdditionComponent.multiple = true;
          batchAdditionComponent.batchAddition = true;
          batchAdditionComponent.func = 'saveBatchSelected';
          batchAdditionComponent.listParams =
            this.listParams[this.batchAdditionComponent.prop] || {};

          this.batchAdditionComponent = batchAdditionComponent;
        }
      }
    },

    setCellClassName({ row, column, rowIndex, columnIndex }) {
      let cellClassName = '';

      if (this.cellClassNameFn && typeof this.cellClassNameFn === 'function') {
        cellClassName = this.cellClassNameFn({ row, column, rowIndex, columnIndex });
      }

      if (column && column.property && this.requiredColumns[column.property]) {
        cellClassName = cellClassName + ' is-required';
      }

      return cellClassName;
    }
  }
};
</script>
<style lang="scss">
@import './list.scss';
@import './required.scss';
</style>
