<template>
  <div class="row-list">
    <el-button
      v-if="!readOnly && addBtn && multipleMaterialAdd"
      size="small"
      @click.stop="handleAddMaterialRow()"
    >
      {{ labels.addMaterials }}
    </el-button>
    <el-button
      v-if="!readOnly && addBtn && !multipleMaterialAdd"
      size="small"
      @click.stop="handleAddRow()"
    >
      {{ labels.addRow }}
    </el-button>
    <el-button v-if="selectSupplierButton" size="small" @click.stop="handleShowSupplierSelector()">
      {{ labels.addSupplier }}
    </el-button>
    <el-button v-if="copyItem" size="small" @click.stop="handleCopyItemSelector()">
      {{ labels.copyRow }}
    </el-button>

    <el-button v-if="!saveAllItem && isSaveAll" size="small" @click.stop="showAllEdit()">
      {{ labels.editAll }}
    </el-button>

    <el-button
      size="small"
      :tip="labels.pleaseFillFirst"
      v-if="bitEditItem.length"
      @click.stop="bitFillAllItem()"
    >
      {{ labels.copyRowWithOneClick }}
    </el-button>
    <el-button v-if="status === 'edit'" size="small" @click.stop="saveAll()">
      {{ labels.oneClickSave }}
    </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.importExcelData"
      :isElementBtn="true"
      @file-error="onFileError"
      @file-progress="onFileProgress"
      @file-success="onFileSuccess"
    />

    <el-button v-if="showLadderButton" size="small" @click.stop="handleShowLadder()">
      {{ labels.setLadderQuotes }}
    </el-button>

    <el-button v-if="showCostTemplateButton" size="small" @click.stop="handleShowCostTemplate()">
      {{ labels.setCostQuotes }}
    </el-button>

    <slot :slot-scope="row" name="innerTopLeftBar" />
    <avue-crud
      ref="crud"
      v-if="reload"
      v-model="obj"
      v-on="$listeners"
      :cell-class-name="setCellClassName"
      :data.sync="data"
      :option.sync="finalOption"
      :row-class-name="rowClassNameFn"
      :row-style="rowStyle"
      @row-click="handleRowClick"
      @row-del="rowDelete"
      @row-update="rowUpdate"
      @selection-change="handleSelectChange"
    >
      <!-- 自定义 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>

      <!-- 将之前的放入slot中做默认值，防止影响其它模块 -->
      <template slot="menu" slot-scope="{ row }">
        <slot :slot-scope="row" name="operation">
          <el-button
            v-if="
              supplierQuote &&
              row.quoteMethod !== '0' &&
              row.noQuoted === 'Y' &&
              ['1', '3'].includes(row.itemStatus)
            "
            class="el-button el-button--text el-button--small"
            @click.stop="handleQuote(row)"
          >
            <i class="el-icon-edit" /><span v-text="labels.quote"></span>
          </el-button>
        </slot>
      </template>

      <template v-for="item in itemLinkList" :slot="item.prop" slot-scope="scope">
        <span v-if="!scope.row.$cellEdit" :key="item.prop"> {{ scope.row[item.prop] }}</span>
        <span v-else-if="readOnly && scope.row[item.prop]" :key="item.prop">
          {{ scope.row[item.prop] }}
        </span>
        <!-- 禁用预定义 slot 点击跳转, 将来根据业务需要处理
        <el-tag
          v-else-if="readOnly && scope.row[item.prop]"
          :key="item.prop"
          @click.stop="go(item, scope.row)"
        >
          {{ scope.row[item.prop] }}
        </el-tag>
        -->

        <component
          v-else-if="scope.row.$cellEdit"
          :api="listApi[item.prop]"
          :columns="listColumns[item.prop]"
          :customSlots="listCustomSlots[item.prop]"
          :is="item.component"
          :key="item.prop"
          :list-handlers="listHandlers[item.prop]"
          :list-params.sync="listParams[item.prop]"
          :selected.sync="scope.row[item.prop]"
          @selectDone="doSelect(item.func, scope.row, $event, item.option || item.params)"
        >
          <!-- 自定义 slots -->
          <template
            v-for="listCustomSlotItem in listCustomSlotItems"
            :slot="listCustomSlotItem.prop"
            slot-scope="{ row }"
          >
            <slot
              v-if="listCustomSlotItem && listCustomSlotItem.prop"
              :slot-scope="row"
              :name="listCustomSlotItem.slotName"
            />
          </template>
        </component>
      </template>

      <!-- itemUploadList 中自定义插槽会与原 物料编码等插槽冲突 -->
      <!-- 只能写列附件插槽先 -->
      <!-- <template v-for="item in itemUploadList" :slot="item.prop" slot-scope="scope">
        <span :key="item.prop" class="btnWrap">
          <span v-if="scope.row[item.prop]"> -->
      <template slot="filePath" slot-scope="scope">
        <span class="btnWrap">
          <span v-if="scope.row.filePath">
            <!-- <img v-if="item.datatype === 'uploadImg'" width="200px" :src="scope.row.filePath" /> -->
            <!-- <el-link class="link" target="_blank" :href="scope.row.filePath">
              下载
            </el-link> -->
            <el-link
              class="uploader-btn"
              v-if="scope.row.filePath"
              download
              target="_blank"
              @click="handleDownload(scope.row)"
            >
              {{ labels.download }}
            </el-link>
          </span>
          <fast2-upload
            v-if="!readOnly && canUploadFile"
            @file-success="onFileUploadSuccess"
            @file-progress="onFileUploadProgress"
            @file-error="onFileUploadError"
            @click="setUploadField('filePath')"
          />
        </span>
      </template>

      <template v-for="item in bitEditItem" :slot="item + 'Header'" slot-scope="{ column }">
        <span :key="item"> {{ (column || {}).label }} <i class="el-icon-finished"></i></span>
      </template>
    </avue-crud>
  </div>
</template>
<script>
import cloneDeep from 'lodash/cloneDeep';
import { checkDataType, checkFixDic, setRequired, setCellStyle } from '../core/utils.js';
import { EXCEL_XLSX } from '../../../lib/const.js';
import {
  ft,
  generateDictURLWithLang,
  getApiPath,
  generateRandomString,
  simulationDownload
} from '../../../lib/utils.js';
import { i18nConfig } from '../../../mixins/i18n';
import { validateNull } from '../../../lib/validate';
import popList from '../../../lib/popList';

import { DownloadAndImportService } from '../../../lib/api/downloadImport';
import { PriceRecordService } from '../../../lib/api/materials';
import dayjs from 'dayjs';

const baseUrl = getApiPath();

const LABELS = {
  add: ft('i18n_common_add', '添加'),
  addRow: ft('i18n_common_add_row', '添加行'),
  cancel: ft('i18n_common_cancel', '取消'),
  completeRequiredFieldsFist: ft(
    'i18n_common_please_complete_the_required_fields_first',
    '请先完善必填项'
  ),
  continueImport: ft('i18n_common_continue_to_import', '继续导入'),
  copyRow: ft('i18n_common_copy_a_row', '复制行'),
  copyRowWithOneClick: ft('i18n_common_copy_row_with_one_click', '一键行复制'),
  editAll: ft('i18n_common_edit_all', '全部编辑'),
  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数据'),
  linePleaseEnterDataFirst: ft('i18n_common_line_please_enter_data_first', '第?行: 请先录入数据'),
  materialDetails: ft('i18n_common_material_details', '物料明细'),
  materials: ft('i18n_common_materials', '物料'),
  materialNumber: ft('i18n_common_material_number', '物料编码'),
  noData: ft('i18n_common_no_data', '暂无数据'),
  oneClickSave: ft('i18n_common_one_click_save', '一键保存'),
  pleaseFillFirst: ft(
    'i18n_common_Please_fill_in_the_first_line_of_data_first',
    '请先填入第一行数据'
  ),
  pleaseSelect: ft('i18n_common_please_select', '请选择 '),
  quote: ft('i18n_common_quote', '报价'),
  quotes: ft('i18n_common_quotes', '报价'),
  sameMaterialExistInLine: ft(
    'i18n_common_same_material_exists_in_line',
    '第?行, 已存在相同的物料'
  ),
  serialNumber: ft('i18n_common_serial_number', '序号'),
  setCostQuotes: ft('i18n_common_set_cost_quotes', '设置成本报价'),
  setLadderQuotes: ft('i18n_common_set_ladder_quotes', '设置阶梯报价'),
  supplier: ft('i18n_common_supplier', '供应商'),
  uploadFailedPleaseTry: ft('i18n_common_upload_failed_please_try_again', '上传失败，请重试'),
  uploading: ft('i18n_common_uploading', '上传中...')
};

LABELS.addMaterials = `${LABELS.add}${LABELS.materials}`;
LABELS.addSupplier = `${LABELS.add}${LABELS.supplier}`;
LABELS.pleaseSelectMaterials = `${LABELS.pleaseSelect}${LABELS.materials}`;

export default {
  /**
   * 导入导出说明
   *
   * @param templateName 指定下载模板文件的名称
   * @param templateNumber
   * @param tempVersion { String } Excel 模板下载接口所需参数，版本号
   * @param showImportExcelButton { Boolean } 是否显示 Excel 导入、导出按钮，默认为 false
   * @event importExcel 侦听Excel导入行信息后，获取接口返回的行数据

   */
  name: 'RowProvider',
  mixins: [i18nConfig],
  props: {
    /*
     * 导入导出 begin
     */

    // confirmImportTip: 导入成功后的确认提示信息
    confirmImportTip: {
      type: String,
      default: ''
    },

    // 导入接口需要的自定义参数。
    customData: {
      type: Object,
      default: () => {
        return {};
      }
    },

    // exportButtonI18nKey: i18n key of export button text
    exportButtonI18nKey: {
      type: String,
      default: 'i18n_common_excel_download'
    },

    // exportButtonText: export button text
    exportButtonText: {
      type: String,
      default: 'Excel模板下载'
    },

    // exportTemplateFn: 导出下载 template 的方法
    exportTemplateFn: {
      type: Function,
      default: null
    },

    // importApiFn: 导入接口方法
    importApiFn: {
      type: Function,
      default: null
    },

    // importApiParams: 获取导入接口参数方法
    importApiParamsFn: {
      type: Function,
      default: null
    },

    // importExcelButtonText: import excel button text
    importExcelButtonText: {
      type: String,
      default: LABELS.importExcelData
    },

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

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

    // 模板名称，相当于下载下来的文件名
    templateName: {
      type: String,
      default: ''
    },

    templateNumber: {
      type: String,
      default: ''
    },

    // tempVersion: excel 模板, 版本号（有业务类型的使用）
    tempVersion: {
      type: String,
      default: ''
    },

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

    /*
     * 导入导出 end
     */

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

    bitEditItem: {
      type: Array,
      default: function () {
        return []; // 允许批量修改的元素
      }
    },

    // businessType: 业务类型
    businessType: {
      type: String,
      default: ''
    },

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

    copyItem: {
      type: Boolean,
      default: false // 是否显示复制行 按钮 默认不显示
    },

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

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

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

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

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

    // isSaveAll: 是否全部保存
    isSaveAll: {
      type: Boolean,
      default: false
    },

    // isShowHistoryPrice: 是否获取物料历史价格
    isShowHistoryPrice: {
      type: Boolean,
      default: false
    },

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

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

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

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

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

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

    /**
     * 明细表格中的 slot 字段绑定事件处理方法
     * 目前有 beforeCloseFn, searchResetFn
     * @see material.vue
     */
    listHandlers: {
      type: Object,
      default: function () {
        return {};
      }
    },

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

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

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

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

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

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

    selectSupplierButton: {
      type: Boolean,
      default: false // 是否显示添加供应商 按钮 默认不显示
    },

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

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

    status: {
      type: String,
      default: ''
    },

    supplierQuote: {
      type: Boolean,
      default: false // 是否供应商报价
    },

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

    /* The functions are as follows */

    /* 添加空行时，对数据进行初始化处理
      例如
      addEmptyRowProc(item) {
        item.budgetPrice = '0';
        item.quantity = '0';
        item.subtotalAmount = '0';

        return item;
      }
    */
    addEmptyRowProcFn: {
      type: Function,
      default: null
    },

    afterSelectedMaterialProcFn: {
      type: Function,
      default: null
    },

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

    /**
     * @property {Object[Function]} columnDefineProcFns - 列属性规则处理方法集合
     */
    columnDefineProcFns: {
      type: Object,
      default: null
    },

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

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

    // rowStyle: 用于设置行样式
    rowStyle: {
      type: Function,
      default: null
    },

    /* 编辑后更新行数据时，对数据进行处理
       例如
       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);
      }
    */
    rowUpdateProcFn: {
      type: Function,
      default: null
    },

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

    // selectionChangeFn: 多选方法
    selectionChangeFn: {
      type: Function,
      default: null
    },

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

    // valideBeforeSaveFn: validate before save
    valideBeforeSaveFn: {
      type: Boolean,
      default: null
    }
  },
  data() {
    return {
      labels: LABELS,
      tabCur: -1, // 当前行索引
      finalOption: {},
      reload: false,
      optionHash: '',
      obj: {},
      defaultValues: {},
      materialsColumn: [],
      showColumnProps: null,
      removedPreSlots: {},
      saveAllItem: false,
      requiredColumns: {},

      customSlotItems: [],
      listCustomSlotItems: [],

      uploadField: '',
      rules: {},

      excelXlsx: EXCEL_XLSX,

      commonDelieryData: null,
      commonDelieryData2: null
    };
  },
  watch: {
    customSlots: {
      handler: async function (newVal, oldVar) {
        if (newVal) {
          this.handleCustomSlotsChanged(newVal);
        }
      },
      immediate: true,
      deep: true
    },

    listCustomSlots: {
      handler: async function (newVal, oldVar) {
        if (newVal) {
          this.handleListCustomSlotsChanged(newVal);
        }
      },
      immediate: true,
      deep: true
    },

    option: {
      handler(newVal, oldVal) {
        if (newVal) {
          this.handleOptionChanged(newVal, oldVal);
        }
      },
      immediate: true, // 刷新加载 立马触发一次handler
      deep: true
    }
  },
  mounted() {
    if (this.isSaveAll) {
      this.showAllEdit();
    }
  },
  methods: {
    addEmptyRow() {
      const item = { $cellEdit: true };
      const showColumnProps = this.getShowColumnProps();

      item.id = `${new Date().valueOf()}${generateRandomString(6)}`;

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

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

    bitFillAllItem() {
      const obj = {};

      this.bitEditItem.forEach((item) => {
        if (!this.data[0][item]) {
          const columnItem = this.finalOption.column.find((i) => i.prop === item);
          const columnName =
            columnItem.displayName && columnItem.displayName !== 'undefined'
              ? columnItem.displayName
              : columnItem.label;
          this.$message.error(
            `${LABELS.linePleaseEnterDataFirst.replace('?', '1')} - ${columnName}`
          );
        }
        obj[item] = this.data[0][item];
      });
      this.data.map((item) => {
        this.bitEditItem.map((bitItem) => {
          if (obj[bitItem]) {
            item[bitItem] = obj[bitItem];
          }
        });
      });
      // this.reload = false;
      // this.$$nextTick(() => {
      //   this.reload = true;
      // });
    },

    async changeOption(finalOption) {
      this.recoveryPreSlots();

      let cellBtn;

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

      this.filterPreSlots();

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

      this.finalOption.editBtn = false;

      for (const columnItem of this.finalOption.column) {
        if (columnItem.prop === 'taxCode' && this.taxCodeSlot) {
          columnItem.slot = true;
          break;
        }
      }

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

      if (this.readOnly) {
        this.finalOption.cellBtn = false;
        this.finalOption.menu = false;
      } else {
        this.finalOption.cellBtn = true;
        this.finalOption.menu = true;

        if (this.selection) {
          this.finalOption.selection = true;
        }
      }
      if (this.isSaveAll) {
        this.finalOption.menu = false;
      }

      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.forEach((item) => {
          item.materialNumber &&
            materialNumberList.push({ materialNumber: item.materialNumber, factory: item.factory });
        });

        const index = 1;

        for (const item of list) {
          const i = materialNumberList.findIndex(
            (i) => item.materialNumber === i.materialNumber && item.factory === i.factory
          );
          if (item.materialNumber && i !== -1) {
            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
      };
    },

    checkForm(callback, failback) {
      this.$refs.form.validate((valid, done) => {
        if (valid) {
          callback && callback(done);
        } else {
          failback && failback();
          return false;
        }
      });
    },

    checkUploadSlot(item) {
      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;
        }
      });

      // 非只读元素 可以上传
      this.itemUploadList.map((linkItem) => {
        if (item.prop === linkItem.prop && item.isReadOnly === 'N') {
          linkItem.open = true;
          item.formslot = true;
          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());
          }
        });
      });
    },

    doCheck() {
      const column = this.finalOption.column;
      let errorRow = 0;
      column.forEach((item) => {
        if (item && item.rules && item.rules.length) {
          this.rules[item.prop] = item.rules;
        }
      });
      this.data.map((item) => {
        if (!item.fbk2 || item.fbk2 === '') {
          item.fbk2 = '3'; // 计划采购时间 无值设默认
        }
      });
      this.data.forEach((item, index) => {
        const isPass = this.rowCellUpdate(item, index);
        if (!isPass) {
          errorRow++;
        }
      });

      if (errorRow > 0) {
        this.$message.error(LABELS.completeRequiredFieldsFist);
        return false;
      }
      return true;
    },

    // slot 弹框 处理
    doSelect(func, row, event, params = []) {
      this[func](row, event, params);
      this.$forceUpdate();
    },

    download(blobUrl, fileName) {
      const a = document.createElement('a');
      a.download = fileName;
      a.href = blobUrl;
      a.click();
      document.body.removeChild(a);
    },

    enEditPreSlot(item) {
      this.recoveryPreSlot(item.prop);

      item.formslot = true;
      item.slot = true;
      delete item.type;
    },

    async filterColum(option) {
      option.emptyText = LABELS.noData;

      const waitUpdateDic = [];
      const crud = this.$refs.crud;

      const newColumns = [];

      const columns = option.column || [];
      const columnsCount = {};
      const bitEditItem = this.bitEditItem;

      const customSlotPropItems = {};

      const customSlots = this.customSlots || [];

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

      for (const item of columns) {
        const itemProp = this.rowPermission[item.prop];

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

        await checkDataType(item);

        if (bitEditItem.includes(item.prop)) {
          item.headerslot = true;
        }

        if (typeof itemProp !== 'undefined' && typeof itemProp.value !== 'undefined') {
          this.defaultValues[item.prop] = itemProp.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.checkUploadSlot(item);

        checkFixDic(item);
        // 普通字段控制
        if (itemProp) {
          item.isReadOnly = itemProp.isReadOnly;

          if (itemProp.display && itemProp.display !== false) {
            if (this.enAbleI18n) {
              this.setI18n(item);
            } else {
              this.setDisplayName(item);
            }

            if (itemProp.value) {
              item.value = itemProp.value;
            }

            if (itemProp.isRequired === 'Y') {
              itemProp.isRequired = true;
            }

            if (itemProp.isRequired === 'N') {
              itemProp.isRequired = false;
            }

            const isRequired = !!itemProp.isRequired;

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

              setRequired(item);
            }

            if (!validateNull(itemProp.bizDic)) {
              delete item.dicData;
              delete item.dicMethod;
              delete item.props;

              item.type = 'select';
              item.dicUrl = generateDictURLWithLang(
                `${baseUrl}/ElsSearchDictionaryService/no-auth/dict/${itemProp.bizDic}`,
                true
              );
              waitUpdateDic.push(item.prop);
            }

            if (itemProp.isDisabled === false) {
              delete item.disabled;
              item.cell = true;

              // if (item.datatype === 'popup' && this.removedPreSlots[item.prop]) {
              //   this.enEditPreSlot(item.prop);
              // }
            } else if (itemProp.isDisabled === true) {
              item.disabled = 'disabled';

              if (item.datatype === 'popup') {
                this.noEditPreSlot(item);
              }
            } else if (
              item.isDisabled === 'Y' ||
              this.readOnly ||
              item.ref ||
              item.disabled === 'disabled'
            ) {
              item.disabled = 'disabled';
              item.cell = false;
              item.rules = [];
            } else {
              item.cell = true;
              delete item.readonly;
            }

            if (item.isReadOnly === 'Y') {
              item.disabled = 'disabled';
              item.cell = false;
              item.rules = [];
            }

            if (item.prop === 'taxRate') {
              item.cell = false;
            }
            // 允许用户编辑字段 并 输入框类型 可以编辑
            if (item.type === 'varchart' && item.isUserInput === 'N') {
              item.cell = false;
            }

            setCellStyle(item, {
              fitColumnWidth: false
            });

            item.display = true;

            if (
              this.columnDefineProcFns &&
              typeof this.columnDefineProcFns[item.prop] === 'function'
            ) {
              this.columnDefineProcFns[item.prop].call(this, item);
            }

            if (!columnsCount[item.prop]) {
              newColumns.push(item);
              columnsCount[item.prop] = true;
            }
          }
        }

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

          setRequired(item);

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

          item.display = true;

          if (
            this.columnDefineProcFns &&
            typeof this.columnDefineProcFns[item.prop] === 'function'
          ) {
            this.columnDefineProcFns[item.prop].call(this, item);
          }

          if (!columnsCount[item.prop]) {
            newColumns.push(item);
            columnsCount[item.prop] = true;
          }
        }
      }

      if (crud) {
        waitUpdateDic.map((prop) => {
          if (prop) {
            crud.updateDic(prop);
          }
        });
      }

      option.column = newColumns;

      return this.initSlotList(option);
    },

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

        this.itemLinkList = preSlots;
      }
    },

    async getMaterialHistoryPrice(material) {
      const params = {
        materialNumber: material.materialNumber,
        businessType: this.businessType,
        businessMould: 'enquiry'
      };

      try {
        const res = await PriceRecordService.getPriceByMaterial(params);

        const resData = res.data;

        if (resData.code === '200' && resData.data) {
          return resData.data;
        } else {
          console.log('load material history price fail', resData);
          return null;
        }
      } catch (err) {
        console.log('load material history price error', err);
        return null;
      }
    },

    getNotSaveNumber(list = this.data) {
      const noSave = list.reduce((total, item) => {
        let isSave = 0;
        if (item.$cellEdit) {
          isSave = 1;
        }
        return total + isSave;
      });

      return noSave;
    },

    getSelectRefs(type) {
      const refs = [];
      /*
      if (!type) {
        return refs;
      }
      this.finalOption.column.map((item) => {
        const originItem = this.findObject(this.originColumn, item.prop);

        if (originItem.ref && originItem.ref.includes(type)) {
          refs.push(item.prop);
        }
      });
      */

      return refs;
    },

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

      if (!this.showColumnProps) {
        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 params = item.params;
        let url = item.url;
        const len = params.length;
        for (let index = 0; index < len; index++) {
          const param = params[index];
          if (index !== len - 1) {
            url = url + row[param] + '/';
          } else {
            url = url + row[param];
          }
        }
        console.log('url', url);
        const router = {
          name: item.label,
          src: url
        };
        const event = {
          name: 'openNewTag',
          props: router
        };

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

    handleAddMaterialRow() {
      this.$emit('handleAddMaterialShow');
    },

    handleAddRow(title, row) {
      this.$emit('handleAddShow');
    },

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

    handleListCustomSlotsChanged(propObject) {
      const propKeys = Object.keys(propObject);

      for (const propKey of propKeys) {
        const customSlots = propObject[propKey] || [];

        for (const customSlot of customSlots) {
          if (customSlot && customSlot.prop && customSlot.slotName) {
            this.listCustomSlotItems.push(customSlot || {});
          }
        }
      }
    },

    handleDownload(row) {
      const blobUrl = `${baseUrl}/servlet/downloadServlet?filePath=${row.filePath}`;
      const pathList = row.filePath.split('/');
      const index = pathList.length - 1;
      const fileName = pathList[index];
      this.download(blobUrl, fileName);
    },

    // excel 模板: 下载
    handleDownloadExcel() {
      if (this.exportTemplateFn && typeof this.exportTemplateFn === 'function') {
        this.exportTemplateFn();
      } else {
        DownloadAndImportService.downloadBusinessTemplate(
          this.templateNumber,
          this.tempVersion,
          this.templateName
        ).then((res) => {
          const date = dayjs(new Date()).format('YYYYMMDDHHmmss');
          const fileName = `${this.templateName}模板_${date}.xlsx`;

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

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

      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);
      }
    },

    handleQuote(row) {
      this.$emit('on-quote', row);
    },

    handleRowClick(row, event, column) {
      this.tableCur = row.$index;

      this.$emit('row-click', { row, event, column });
    },

    handleSelectChange(selection) {
      if (typeof this.selectionChangeFn === 'function') {
        this.selectionChangeFn(selection);
      }
    },

    // 显示供应商选择框
    handleShowSupplierSelector() {
      this.$emit('show-supplier-select');
    },

    handleShowLadder() {
      this.$emit('show-set-ladder');
    },

    handleShowCostTemplate() {
      this.$emit('show-set-cost-template');
    },

    handleCopyItemSelector() {
      this.$emit('copy-item');
    },

    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) {
      if (this.importApiFn && typeof this.importApiFn === 'function') {
        this.importExcelDataExtend(data);
      } else {
        this.importExcelDataEmbed(data);
      }
    },

    importExcelDataEmbed(data) {
      const params = Object.assign(
        {
          filePath: data.url || '',
          toElsAccount: this.toElsAccount
        },
        this.customData
      );

      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);
        });
    },

    importExcelDataExtend(data) {
      const params = {};

      if (this.importApiParamsFn && typeof this.importApiParamsFn === 'function') {
        Object.assign(params, this.importApiParamsFn() || {});
      }

      params.filePath = data.url || '';

      this.importApiFn(params)
        .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);
        });
    },

    initSlotList(option) {
      const list = option?.column;

      this.itemLinkList.map((item) => {
        const linkItem = this.findObject(list, item.prop);

        if (linkItem !== -1 && linkItem.isReadOnly !== 'Y') {
          linkItem.slot = true;
        }
      });

      return option;
    },

    isUploadAble(item) {
      if (this.readOnly) {
        return false;
      }

      if (item.datatype === 'uploadImg' || item.datatype === 'uploadFile') {
        if (item.isReadOnly !== 'Y') {
          return true;
        }
      }

      return false;
    },

    noEditPreSlot(item) {
      const slot = this.removePreSlot(item.prop);

      if (typeof slot === 'object') {
        delete item.formslot;
        delete item.slot;
        item.type = 'input';
      }
    },

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

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

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

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

      this.importExcel(data);
    },

    onFileUploadError(rootFile, file, message, chunk) {
      this.$message.error(LABELS.uploadFailedPleaseTry);
    },

    onFileUploadSuccess(req, req1, res) {
      const data = JSON.parse(res).data;
      const prop = this.uploadField || this.itemUploadList[0].prop;

      if (prop) {
        this.obj[prop] = data.url;
        this.data[this.tableCur][prop] = data.url;
      }

      this.$forceUpdate();
    },

    onFileUploadProgress() {
      console.log(LABELS.uploading);
    },

    recoveryPreSlot(prop) {
      const removedPreSlot = this.removedPreSlots[prop];

      if (removedPreSlot) {
        const itemLinkList = this.itemLinkList;

        const slot = itemLinkList.find((item) => {
          return item.prop === prop;
        });

        if (!slot) {
          itemLinkList.push(slot);
        }

        delete this.removedPreSlots[prop];
      }
    },

    recoveryPreSlots() {
      const removedPreSlots = this.removedPreSlots;

      const keys = Object.keys(removedPreSlots);

      if (keys.length > 0) {
        const itemLinkList = this.itemLinkList;

        for (const item of itemLinkList) {
          if (removedPreSlots[item.prop]) {
            delete removedPreSlots[item.prop];
          }
        }

        for (const prop in removedPreSlots) {
          itemLinkList.push(removedPreSlots[prop]);
        }

        this.removedPreSlots = {};
      }
    },

    removePreSlot(prop) {
      let index = 0;
      let propIndex = -1;
      let slot = null;

      const itemLinkList = this.itemLinkList;

      for (const item of itemLinkList) {
        const params = item.params || [];

        if (item.prop === prop || params.indexOf(prop) > -1) {
          propIndex = index;

          break;
        }

        index = index + 1;
      }

      if (propIndex > -1) {
        const removedSlots = this.itemLinkList.splice(propIndex, 1);

        slot = removedSlots[0];

        this.removedPreSlots[prop] = slot;
      }

      return slot;
    },

    resetPreSlot(prop) {
      let index = 0;
      let propIndex = -1;

      for (const item of this.itemLinkList) {
        const params = item.params || [];

        if (item.prop === prop || params.indexOf(prop) > -1) {
          propIndex = index;

          delete item.noEdit;

          break;
        }

        index = index + 1;
      }

      if (propIndex > -1) {
        // this.itemLinkList.splice(propIndex, 1);
      }
    },

    rowCellUpdate(row, index) {
      var result = this.$refs.crud.validateCellField(index);
      if (result) {
        this.$emit(
          'row-update',
          row,
          index,
          () => {
            row.$cellEdit = false;
            this.$set(this.list, index, row);
          },
          () => {}
        );
      }
      return result;
    },

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

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

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

      // TODO: taxRate为空，强行添加 taxCode
      if (data && !data.taxRate && data.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();
    },

    async saveAll() {
      if (this.valideBeforeSaveFn && typeof this.valideBeforeSaveFn === 'function') {
        if (await this.valideBeforeSaveFn(this.$refs.crud, this.data)) {
          this.saveAllItem = false;
        }
      } else {
        this.data.map((item, index) => {
          this.$refs.crud.rowCellUpdate(item, index);
        });

        const num = this.getNotSaveNumber();
        if (num > 0) {
          this.saveAllItem = false;
        }
      }
    },

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

      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;
        row[params[0]] = item[params[2]];
        row[params[1]] = item[params[3]];
      }

      if (row.prop) {
        const refs = this.getSelectRefs(row.prop);

        if (refs.length) {
          this.setRefs(refs, row, item);
        }
      }
    },

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

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

          return;
        }

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

        for (const materialItem of addMaterialList) {
          let material = materialItem;
          if (
            this.afterSelectedMaterialProcFn &&
            typeof this.afterSelectedMaterialProcFn === 'function'
          ) {
            try {
              const afterResult = await this.afterSelectedMaterialProcFn(
                material,
                this.extendData,
                row
              );
              if (afterResult) {
                material = afterResult;
              }
            } catch (err) {
              continue;
            }
          }

          if (this.isShowHistoryPrice) {
            const historyPrice = await this.getMaterialHistoryPrice(material);

            if (typeof historyPrice === 'object') {
              material.historyPriceIncludingTax = historyPrice.priceIncludingTax;
              material.historyPriceExcludingTax = historyPrice.priceExcludingTax;
            }
          }

          this.setRefs(refs, row, material);

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

          // 新选择的物料, 不要 uuid，也避免用物料的 uuid 覆盖表格数据自身的 uuid
          delete material.uuid;

          const materialObject = row;
          Object.assign(materialObject, material);

          // 设置与物料编辑关联的表单编辑状态
          // this.disableOpenForms(item.materialNumber);
        }
      } else {
        this.$message.warning(LABELS.pleaseSelectMaterials);
      }
    },

    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;
    },

    setRefs(list, row, origin) {
      list.map((item) => {
        if (origin[item]) {
          row[item] = origin[item];
        }
      });
    },

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

      return setRequired(item);
    },

    setUploadField(prop) {
      this.uploadField = prop;
    },

    showAllEdit() {
      this.data.map((item) => {
        item.$cellEdit = true;
      });

      this.saveAllItem = true;
    },

    toggleSelection(value) {
      this.$refs.crud.toggleSelection(value);
    }
  }
};
</script>
<style lang="scss">
@import './required.scss';
@import './list.scss';
</style>
