湟水峡
1.销售、采购模块不要客户合同号、项目名称
2.销售报价和销售台账价格导入按照客户类型赋值
3.人力资源字段增减
4.仓储物流四根模块tab标签页和查询条件修改
5.销售、采购模块字段增减
已添加1个文件
已修改32个文件
9114 ■■■■ 文件已修改
src/api/procurementManagement/procurementLedger.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/viewIndex.js 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFileOpenSea/index.vue 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/components/BlacklistTab.vue 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/components/HomeTab.vue 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/filesDia.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/index.vue 684 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/PlanModal.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/formDia.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/Record.vue 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/index.vue 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/issueManagement/Record.vue 570 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/issueManagement/index.vue 299 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/Record.vue 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/index.vue 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/index.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/dimission/components/formDia.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/dimission/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/components/BasicInfoSection.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentEntry/index.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementInvoiceLedger/index.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 3033 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceLedger/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 2400 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesQuotation/index.vue 468 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementLedger.js
@@ -122,4 +122,12 @@
        method: "get",
        params: id,
    });
}
// æŸ¥è¯¢é”€å”®è¯¦æƒ…(用于销售审批)
export function getSalesByCode(query) {
    return request({
        url: "/purchase/ledger/getSalesByCode",
        method: "get",
        params: query,
    });
}
src/api/viewIndex.js
@@ -144,12 +144,19 @@
// çº¿å½¢å›¾
export const getAmountHalfYear = () => {
  return request({
    url: "/sales/ledger/getAmountHalfYear",
    method: "get",
  });
};
    return request({
        url: '/sales/ledger/getAmountHalfYear',
        method: 'get'
    })
}
// é¦–页-待回款提醒
export const overdueReceivable = () => {
    return request({
        url: '/home/overdueReceivable',
        method: 'get'
    })
}
// å„生产订单的完成进度统计
// /home/progressStatistics
export const getProgressStatistics = () => {
src/views/basicData/customerFile/index.vue
@@ -15,10 +15,10 @@
                   style="width: 240px"
                   clearable
                   @change="handleQuery">
          <el-option label="零售客户"
                     value="零售客户" />
          <el-option label="进销商客户"
                     value="进销商客户" />
          <el-option label="一批商"
                     value="一批商" />
          <el-option label="终端商"
                     value="终端商" />
        </el-select>
        <el-button type="primary"
                   @click="handleQuery"
@@ -66,16 +66,6 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="纳税人识别号:"
                          prop="taxpayerIdentificationNumber">
              <el-input v-model="form.taxpayerIdentificationNumber"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公司地址:"
                          prop="companyAddress">
              <el-input v-model="form.companyAddress"
@@ -83,88 +73,22 @@
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公司电话:"
                          prop="companyPhone">
              <el-input v-model="form.companyPhone"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="银行基本户:"
                          prop="basicBankAccount">
              <el-input v-model="form.basicBankAccount"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="银行账号:"
                          prop="bankAccount">
              <el-input v-model="form.bankAccount"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="开户行号:"
                          prop="bankCode">
              <el-input v-model="form.bankCode"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户分类:"
                          prop="customerType">
              <el-select v-model="form.customerType"
                         placeholder="请选择"
                         clearable>
                <el-option label="零售客户"
                           value="零售客户" />
                <el-option label="进销商客户"
                           value="进销商客户" />
                <el-option label="一批商"
                           value="一批商" />
                <el-option label="终端商"
                           value="终端商" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30"
                v-for="(contact, index) in formYYs.contactList"
                :key="index">
          <el-col :span="12">
            <el-form-item label="联系人:"
                          prop="contactPerson">
              <el-input v-model="contact.contactPerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:"
                          prop="contactPhone">
              <div style="display: flex; align-items: center;width: 100%;">
                <el-input v-model="contact.contactPhone"
                          placeholder="请输入"
                          clearable />
                <el-button @click="removeContact(index)"
                           type="danger"
                           circle
                           style="margin-left: 5px;">
                  <el-icon>
                    <Close />
                  </el-icon>
                </el-button>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-button @click="addNewContact"
                   style="margin-bottom: 10px;">+ æ–°å¢žè”系人</el-button>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="维护人:"
@@ -390,56 +314,8 @@
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">纳税人识别号:</span>
                <span class="info-value">{{ detailForm.taxpayerIdentificationNumber }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">公司电话:</span>
                <span class="info-value">{{ detailForm.companyPhone }}</span>
              </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">公司地址:</span>
                <span class="info-value">{{ detailForm.companyAddress }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">银行基本户:</span>
                <span class="info-value">{{ detailForm.basicBankAccount }}</span>
              </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">银行账号:</span>
                <span class="info-value">{{ detailForm.bankAccount }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">开户行号:</span>
                <span class="info-value">{{ detailForm.bankCode }}</span>
              </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">联系人:</span>
                <span class="info-value">{{ detailForm.contactPerson }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">联系电话:</span>
                <span class="info-value">{{ detailForm.contactPhone }}</span>
              </div>
            </el-col>
          </el-row>
@@ -681,14 +557,7 @@
  const detailForm = reactive({
    customerName: "",
    customerType: "",
    taxpayerIdentificationNumber: "",
    companyPhone: "",
    companyAddress: "",
    basicBankAccount: "",
    bankAccount: "",
    bankCode: "",
    contactPerson: "",
    contactPhone: "",
    maintainer: "",
    maintenanceTime: "",
  });
@@ -712,33 +581,26 @@
  const tableColumn = ref([
    {
      label: "客户分类",
      prop: "customerType",
      width: 120,
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: 220,
    },
    {
      label: "纳税人识别码",
      prop: "taxpayerIdentificationNumber",
      width: 220,
      label: "客户分类",
      prop: "customerType",
      dataType: "tag",
      width: 120,
      formatType: value => {
        if (value === "一批商") {
          return "primary";
        }
        return "success";
      },
    },
    {
      label: "地址及联系方式",
      label: "公司地址",
      prop: "companyAddress",
      width: 250,
    },
    {
      label: "联系人",
      prop: "contactPerson",
    },
    {
      label: "联系电话",
      prop: "contactPhone",
      width: 150,
    },
    {
      label: "跟进进度",
@@ -749,21 +611,6 @@
      label: "跟进时间",
      prop: "followUpTime",
      width: 120,
    },
    {
      label: "银行基本户",
      prop: "basicBankAccount",
      width: 220,
    },
    {
      label: "银行账号",
      prop: "bankAccount",
      width: 220,
    },
    {
      label: "开户行号",
      prop: "bankCode",
      width: 220,
    },
    {
      label: "维护人",
@@ -844,15 +691,6 @@
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const formYYs = ref({
    // å…¶ä»–字段...
    contactList: [
      {
        contactPerson: "",
        contactPhone: "",
      },
    ],
  });
  const data = reactive({
    searchForm: {
      customerName: "",
@@ -860,34 +698,18 @@
    },
    form: {
      customerName: "",
      taxpayerIdentificationNumber: "",
      companyAddress: "",
      companyPhone: "",
      contactPerson: "",
      contactPhone: "",
      maintainer: "",
      maintenanceTime: "",
      basicBankAccount: "",
      bankAccount: "",
      bankCode: "",
      customerType: "",
    },
    rules: {
      customerName: [{ required: true, message: "请输入", trigger: "blur" }],
      taxpayerIdentificationNumber: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
      companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      // contactPerson: [{ required: true, message: "请输入", trigger: "blur" }],
      // contactPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      maintainer: [{ required: false, message: "请选择", trigger: "change" }],
      maintenanceTime: [
        { required: false, message: "请选择", trigger: "change" },
      ],
      basicBankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      bankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      bankCode: [{ required: true, message: "请输入", trigger: "blur" }],
      customerType: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
@@ -947,18 +769,6 @@
    },
  });
  const { searchForm, form, rules } = toRefs(data);
  const addNewContact = () => {
    formYYs.value.contactList.push({
      contactPerson: "",
      contactPhone: "",
    });
  };
  const removeContact = index => {
    if (formYYs.value.contactList.length > 1) {
      formYYs.value.contactList.splice(index, 1);
    }
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
@@ -1001,12 +811,6 @@
    operationType.value = type;
    form.value = {};
    form.value.maintainer = userStore.nickName;
    formYYs.value.contactList = [
      {
        contactPerson: "",
        contactPhone: "",
      },
    ];
    form.value.maintenanceTime = getCurrentDate();
    userListNoPage().then(res => {
      userList.value = res.data;
@@ -1014,14 +818,6 @@
    if (type === "edit") {
      getCustomerPrivatePoolById(row.id).then(res => {
        form.value = { ...res.data };
        formYYs.value.contactList = res.data.contactPerson
          .split(",")
          .map((item, index) => {
            return {
              contactPerson: item,
              contactPhone: res.data.contactPhone.split(",")[index],
            };
          });
      });
    }
    dialogFormVisible.value = true;
@@ -1040,15 +836,6 @@
  };
  // æäº¤æ–°å¢ž
  const submitAdd = () => {
    if (formYYs.value.contactList.length < 1) {
      return proxy.$modal.msgWarning("请至少添加一个联系人");
    }
    form.value.contactPerson = formYYs.value.contactList
      .map(item => item.contactPerson)
      .join(",");
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    addCustomerPrivate(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
@@ -1057,12 +844,6 @@
  };
  // æäº¤ä¿®æ”¹
  const submitEdit = () => {
    form.value.contactPerson = formYYs.value.contactList
      .map(item => item.contactPerson)
      .join(",");
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    updateCustomerPrivatePool(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
src/views/basicData/customerFileOpenSea/index.vue
@@ -15,10 +15,10 @@
                   style="width: 240px"
                   clearable
                   @change="handleQuery">
          <el-option label="零售客户"
                     value="零售客户" />
          <el-option label="进销商客户"
                     value="进销商客户" />
          <el-option label="一批商"
                     value="一批商" />
          <el-option label="终端商"
                     value="终端商" />
        </el-select>
        <el-button type="primary"
                   @click="handleQuery"
@@ -66,16 +66,6 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="纳税人识别号:"
                          prop="taxpayerIdentificationNumber">
              <el-input v-model="form.taxpayerIdentificationNumber"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公司地址:"
                          prop="companyAddress">
              <el-input v-model="form.companyAddress"
@@ -83,88 +73,22 @@
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公司电话:"
                          prop="companyPhone">
              <el-input v-model="form.companyPhone"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="银行基本户:"
                          prop="basicBankAccount">
              <el-input v-model="form.basicBankAccount"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="银行账号:"
                          prop="bankAccount">
              <el-input v-model="form.bankAccount"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="开户行号:"
                          prop="bankCode">
              <el-input v-model="form.bankCode"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户分类:"
                          prop="customerType">
              <el-select v-model="form.customerType"
                         placeholder="请选择"
                         clearable>
                <el-option label="零售客户"
                           value="零售客户" />
                <el-option label="进销商客户"
                           value="进销商客户" />
                <el-option label="一批商"
                           value="一批商" />
                <el-option label="终端商"
                           value="终端商" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30"
                v-for="(contact, index) in formYYs.contactList"
                :key="index">
          <el-col :span="12">
            <el-form-item label="联系人:"
                          prop="contactPerson">
              <el-input v-model="contact.contactPerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:"
                          prop="contactPhone">
              <div style="display: flex; align-items: center;width: 100%;">
                <el-input v-model="contact.contactPhone"
                          placeholder="请输入"
                          clearable />
                <el-button @click="removeContact(index)"
                           type="danger"
                           circle
                           style="margin-left: 5px;">
                  <el-icon>
                    <Close />
                  </el-icon>
                </el-button>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-button @click="addNewContact"
                   style="margin-bottom: 10px;">+ æ–°å¢žè”系人</el-button>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="维护人:"
@@ -459,56 +383,8 @@
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">纳税人识别号:</span>
                <span class="info-value">{{ detailForm.taxpayerIdentificationNumber }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">公司电话:</span>
                <span class="info-value">{{ detailForm.companyPhone }}</span>
              </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">公司地址:</span>
                <span class="info-value">{{ detailForm.companyAddress }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">银行基本户:</span>
                <span class="info-value">{{ detailForm.basicBankAccount }}</span>
              </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">银行账号:</span>
                <span class="info-value">{{ detailForm.bankAccount }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">开户行号:</span>
                <span class="info-value">{{ detailForm.bankCode }}</span>
              </div>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">联系人:</span>
                <span class="info-value">{{ detailForm.contactPerson }}</span>
              </div>
            </el-col>
            <el-col :span="12">
              <div class="info-item">
                <span class="info-label">联系电话:</span>
                <span class="info-value">{{ detailForm.contactPhone }}</span>
              </div>
            </el-col>
          </el-row>
@@ -771,14 +647,7 @@
  const detailForm = reactive({
    customerName: "",
    customerType: "",
    taxpayerIdentificationNumber: "",
    companyPhone: "",
    companyAddress: "",
    basicBankAccount: "",
    bankAccount: "",
    bankCode: "",
    contactPerson: "",
    contactPhone: "",
    maintainer: "",
    maintenanceTime: "",
  });
@@ -802,58 +671,26 @@
  const tableColumn = ref([
    {
      label: "客户分类",
      prop: "customerType",
      width: 120,
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: 220,
    },
    {
      label: "纳税人识别码",
      prop: "taxpayerIdentificationNumber",
      width: 220,
      label: "客户分类",
      prop: "customerType",
      dataType: "tag",
      width: 120,
      formatType: value => {
        if (value === "一批商") {
          return "primary";
        }
        return "success";
      },
    },
    {
      label: "地址及联系方式",
      prop: "addressPhone",
      label: "公司地址",
      prop: "companyAddress",
      width: 250,
    },
    {
      label: "联系人",
      prop: "contactPerson",
    },
    {
      label: "联系电话",
      prop: "contactPhone",
      width: 150,
    },
    // {
    //   label: "跟进进度",
    //   prop: "followUpLevel",
    //   width: 120,
    // },
    // {
    //   label: "跟进时间",
    //   prop: "followUpTime",
    //   width: 120,
    // },
    {
      label: "银行基本户",
      prop: "basicBankAccount",
      width: 220,
    },
    {
      label: "银行账号",
      prop: "bankAccount",
      width: 220,
    },
    {
      label: "开户行号",
      prop: "bankCode",
      width: 220,
    },
    {
      label: "维护人",
@@ -957,15 +794,6 @@
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const formYYs = ref({
    // å…¶ä»–字段...
    contactList: [
      {
        contactPerson: "",
        contactPhone: "",
      },
    ],
  });
  const data = reactive({
    searchForm: {
      customerName: "",
@@ -973,34 +801,18 @@
    },
    form: {
      customerName: "",
      taxpayerIdentificationNumber: "",
      companyAddress: "",
      companyPhone: "",
      contactPerson: "",
      contactPhone: "",
      maintainer: "",
      maintenanceTime: "",
      basicBankAccount: "",
      bankAccount: "",
      bankCode: "",
      customerType: "",
    },
    rules: {
      customerName: [{ required: true, message: "请输入", trigger: "blur" }],
      taxpayerIdentificationNumber: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
      companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      // contactPerson: [{ required: true, message: "请输入", trigger: "blur" }],
      // contactPhone: [{ required: true, message: "请输入", trigger: "blur" }],
      maintainer: [{ required: false, message: "请选择", trigger: "change" }],
      maintenanceTime: [
        { required: false, message: "请选择", trigger: "change" },
      ],
      basicBankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      bankAccount: [{ required: true, message: "请输入", trigger: "blur" }],
      bankCode: [{ required: true, message: "请输入", trigger: "blur" }],
      customerType: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
@@ -1060,18 +872,6 @@
    },
  });
  const { searchForm, form, rules } = toRefs(data);
  const addNewContact = () => {
    formYYs.value.contactList.push({
      contactPerson: "",
      contactPhone: "",
    });
  };
  const removeContact = index => {
    if (formYYs.value.contactList.length > 1) {
      formYYs.value.contactList.splice(index, 1);
    }
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
@@ -1115,12 +915,6 @@
    operationType.value = type;
    form.value = {};
    form.value.maintainer = userStore.nickName;
    formYYs.value.contactList = [
      {
        contactPerson: "",
        contactPhone: "",
      },
    ];
    form.value.maintenanceTime = getCurrentDate();
    userListNoPage().then(res => {
      userList.value = res.data;
@@ -1128,14 +922,6 @@
    if (type === "edit") {
      getCustomer(row.id).then(res => {
        form.value = { ...res.data };
        formYYs.value.contactList = res.data.contactPerson
          .split(",")
          .map((item, index) => {
            return {
              contactPerson: item,
              contactPhone: res.data.contactPhone.split(",")[index],
            };
          });
      });
    }
    dialogFormVisible.value = true;
@@ -1154,15 +940,6 @@
  };
  // æäº¤æ–°å¢ž
  const submitAdd = () => {
    if (formYYs.value.contactList.length < 1) {
      return proxy.$modal.msgWarning("请至少添加一个联系人");
    }
    form.value.contactPerson = formYYs.value.contactList
      .map(item => item.contactPerson)
      .join(",");
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    addCustomer(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
@@ -1171,12 +948,6 @@
  };
  // æäº¤ä¿®æ”¹
  const submitEdit = () => {
    form.value.contactPerson = formYYs.value.contactList
      .map(item => item.contactPerson)
      .join(",");
    form.value.contactPhone = formYYs.value.contactList
      .map(item => item.contactPhone)
      .join(",");
    updateCustomer(form.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
src/views/basicData/supplierManage/components/BlacklistTab.vue
@@ -56,72 +56,9 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item
                label="纳税人识别号:"
                prop="taxpayerIdentificationNum"
            >
              <el-input
                  v-model="form.taxpayerIdentificationNum"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公司地址:" prop="companyAddress">
              <el-input
                  v-model="form.companyAddress"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公司电话:" prop="companyPhone">
              <el-input
                  v-model="form.companyPhone"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="开户行:" prop="bankAccountName">
              <el-input
                  v-model="form.bankAccountName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="账号:" prop="bankAccountNum">
              <el-input
                  v-model="form.bankAccountNum"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="联系人:" prop="contactUserName">
              <el-input
                  v-model="form.contactUserName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:" prop="contactUserPhone">
              <el-input
                  v-model="form.contactUserPhone"
                  placeholder="请输入"
                  clearable
              />
@@ -157,26 +94,6 @@
                  placeholder="请选择"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商类型:" prop="supplierType">
              <el-select v-model="form.supplierType" placeholder="请选择" clearable>
                <el-option label="甲" value="甲" />
                <el-option label="乙" value="乙" />
                <el-option label="丙" value="丙" />
                <el-option label="丁" value="丁" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="是否白名单:" prop="isWhite">
              <el-select v-model="form.isWhite" placeholder="请选择" clearable>
                <el-option label="是" :value="0" />
                <el-option label="否" :value="1" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -260,49 +177,14 @@
    width: 250,
  },
  {
    label: "供应商类型",
    prop: "supplierType",
    width: 120,
  },
  {
    label: "纳税人识别号",
    prop: "taxpayerIdentificationNum",
    width: 230,
  },
  {
    label: "公司地址",
    prop: "companyAddress",
    width: 220,
  },
  {
    label: "联系方式",
    prop: "companyPhone",
    width:150
  },
  {
    label: "开户行",
    prop: "bankAccountName",
    width: 220,
  },
  {
    label: "账号",
    prop: "bankAccountNum",
    width: 220,
  },
  {
    label: "联系人",
    prop: "contactUserName",
  },
  {
    label: "联系电话",
    prop: "contactUserPhone",
    width: 150,
  },
  {
    label: "维护人",
    prop: "maintainUserName",
  },
  {
    label: "维护时间",
    prop: "maintainTime",
@@ -352,32 +234,15 @@
  },
  form: {
    supplierName: "",
    taxpayerIdentificationNum: "",
    companyAddress: "",
    companyPhone: "",
    bankAccountName: "",
    bankAccountNum: "",
    contactUserName: "",
    contactUserPhone: "",
    maintainUserId: "",
    maintainTime: "",
    supplierType: "",
    isWhite: "",
  },
  rules: {
    supplierName: [{ required: true, message: "请输入", trigger: "blur" }],
    taxpayerIdentificationNum: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
    companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
    bankAccountName: [{ required: true, message: "请输入", trigger: "blur" }],
    bankAccountNum: [{ required: true, message: "请输入", trigger: "blur" }],
    contactUserName: [{ required: false, message: "请输入", trigger: "blur" }],
    contactUserPhone: [{ required: false, message: "请输入", trigger: "blur" }],
    maintainUserId: [{ required: false, message: "请选择", trigger: "change" }],
    maintainTime: [{ required: false, message: "请选择", trigger: "change" }],
    supplierType: [{ required: true, message: "请选择供应商类型", trigger: "change" }],
  },
});
const { searchForm, form, rules } = toRefs(data);
src/views/basicData/supplierManage/components/HomeTab.vue
@@ -62,72 +62,9 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item
                label="纳税人识别号:"
                prop="taxpayerIdentificationNum"
            >
              <el-input
                  v-model="form.taxpayerIdentificationNum"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公司地址:" prop="companyAddress">
              <el-input
                  v-model="form.companyAddress"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公司电话:" prop="companyPhone">
              <el-input
                  v-model="form.companyPhone"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="开户行:" prop="bankAccountName">
              <el-input
                  v-model="form.bankAccountName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="账号:" prop="bankAccountNum">
              <el-input
                  v-model="form.bankAccountNum"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="联系人:" prop="contactUserName">
              <el-input
                  v-model="form.contactUserName"
                  placeholder="请输入"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="联系电话:" prop="contactUserPhone">
              <el-input
                  v-model="form.contactUserPhone"
                  placeholder="请输入"
                  clearable
              />
@@ -163,26 +100,6 @@
                  placeholder="请选择"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商类型:" prop="supplierType">
              <el-select v-model="form.supplierType" placeholder="请选择" clearable>
                <el-option label="甲" value="甲" />
                <el-option label="乙" value="乙" />
                <el-option label="丙" value="丙" />
                <el-option label="丁" value="丁" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="是否白名单:" prop="isWhite">
              <el-select v-model="form.isWhite" placeholder="请选择" clearable>
                <el-option label="是" :value="0" />
                <el-option label="否" :value="1" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -266,49 +183,14 @@
    width: 250,
  },
  {
    label: "供应商类型",
    prop: "supplierType",
    width: 120,
  },
  {
    label: "纳税人识别号",
    prop: "taxpayerIdentificationNum",
    width: 230,
  },
  {
    label: "公司地址",
    prop: "companyAddress",
    width: 220,
  },
  {
    label: "联系方式",
    prop: "companyPhone",
    width:150
  },
  {
    label: "开户行",
    prop: "bankAccountName",
    width: 220,
  },
  {
    label: "账号",
    prop: "bankAccountNum",
    width: 220,
  },
  {
    label: "联系人",
    prop: "contactUserName",
  },
  {
    label: "联系电话",
    prop: "contactUserPhone",
    width: 150,
  },
  {
    label: "维护人",
    prop: "maintainUserName",
  },
  {
    label: "维护时间",
    prop: "maintainTime",
@@ -358,32 +240,15 @@
  },
  form: {
    supplierName: "",
    taxpayerIdentificationNum: "",
    companyAddress: "",
    companyPhone: "",
    bankAccountName: "",
    bankAccountNum: "",
    contactUserName: "",
    contactUserPhone: "",
    maintainUserId: "",
    maintainTime: "",
    supplierType: "",
    isWhite: "",
  },
  rules: {
    supplierName: [{ required: true, message: "请输入", trigger: "blur" }],
    taxpayerIdentificationNum: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
    companyPhone: [{ required: true, message: "请输入", trigger: "blur" }],
    bankAccountName: [{ required: true, message: "请输入", trigger: "blur" }],
    bankAccountNum: [{ required: true, message: "请输入", trigger: "blur" }],
    contactUserName: [{ required: false, message: "请输入", trigger: "blur" }],
    contactUserPhone: [{ required: false, message: "请输入", trigger: "blur" }],
    maintainUserId: [{ required: false, message: "请选择", trigger: "change" }],
    maintainTime: [{ required: false, message: "请选择", trigger: "change" }],
    supplierType: [{ required: true, message: "请选择供应商类型", trigger: "change" }],
  },
});
const { searchForm, form, rules } = toRefs(data);
src/views/basicData/supplierManage/filesDia.vue
@@ -30,20 +30,10 @@
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="500"
          @pagination-change="paginationSearch"
          :total="total"
          :page="page.current"
          :limit="page.size"
          @pagination="paginationSearch"
          :page="{ ...page, total }"
      >
      </PIMTable>
            <pagination
                style="margin: 10px 0"
                v-show="total > 0"
                @pagination="paginationSearch"
                :total="total"
                :page="page.current"
                :limit="page.size"
            />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
@@ -200,4 +190,4 @@
<style scoped>
</style>
</style>
src/views/basicData/supplierManage/index.vue
@@ -1,43 +1,663 @@
<!-- åœ¨ä½ çš„主页面中 -->
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane label="正常供应商" name="home">
        <HomeTab ref="homeTab" />
      </el-tab-pane>
      <el-tab-pane label="黑名单" name="blacklist">
        <BlacklistTab ref="blacklistTab" />
      </el-tab-pane>
    </el-tabs>
    <div class="search_form">
      <div>
        <span class="search_title">供应商档案:</span>
        <el-input
          v-model="searchForm.supplierName"
          style="width: 240px"
          placeholder="输入供应商名称搜索"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
          >搜索</el-button
        >
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')"
          >新增供应商</el-button
        >
        <el-button @click="handleOut">导出</el-button>
        <el-button type="info" plain icon="Upload" @click="handleImport"
          >导入</el-button
        >
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
                @pagination="pagination"
      ></PIMTable>
    </div>
    <el-dialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增供应商信息' : '编辑供应商信息'"
      width="70%"
      @close="closeDia"
    >
      <el-form
        :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
      >
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商名称:" prop="supplierName">
              <el-input
                v-model="form.supplierName"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公司地址:" prop="companyAddress">
              <el-input
                v-model="form.companyAddress"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="维护人:" prop="maintainUserId">
              <el-select
                                filterable
                v-model="form.maintainUserId"
                placeholder="请选择"
                clearable
                disabled
              >
                <el-option
                  v-for="item in userList"
                  :key="item.nickName"
                  :label="item.nickName"
                  :value="item.userId"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="维护时间:" prop="maintainTime">
              <el-date-picker
                style="width: 100%"
                v-model="form.maintainTime"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="产品大类:" prop="productId">
              <el-tree-select
                v-model="form.productId"
                placeholder="请选择"
                clearable
                check-strictly
                :data="productOptions"
                :render-after-expand="false"
                style="width: 100%"
                @change="getModels"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select
                v-model="form.productModelId"
                placeholder="请选择"
                clearable
                style="width: 100%"
                @change="getProductModel"
              >
                <el-option
                  v-for="item in modelOptions"
                  :key="item.id"
                  :label="item.model"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- ä¾›åº”商导入对话框 -->
    <el-dialog
      :title="upload.title"
      v-model="upload.open"
      width="400px"
      append-to-body
    >
      <el-upload
        ref="uploadRef"
        :limit="1"
        accept=".xlsx, .xls"
        :headers="upload.headers"
        :action="upload.url + '?updateSupport=' + upload.updateSupport"
        :disabled="upload.isUploading"
        :on-progress="handleFileUploadProgress"
        :on-success="handleFileSuccess"
        :on-error="handleFileError"
        :auto-upload="false"
        drag
      >
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <template #tip>
          <div class="el-upload__tip text-center">
            <span>仅允许导入xls、xlsx格式文件。</span>
            <el-link
              type="primary"
              :underline="false"
              style="font-size: 12px; vertical-align: baseline"
              @click="importTemplate"
              >下载模板</el-link
            >
          </div>
        </template>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
          <el-button @click="upload.open = false">取 æ¶ˆ</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog
      v-model="historyDialogVisible"
      :title="`${currentHistorySupplierName || ''}合作历史`"
      width="80%"
    >
      <el-table
        :data="historyTableData"
        border
        v-loading="historyTableLoading"
        style="width: 100%"
        max-height="500"
      >
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="采购合同号" prop="purchaseContractNumber" min-width="180" show-overflow-tooltip />
        <el-table-column label="销售合同号" prop="salesContractNo" min-width="180" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" min-width="160" show-overflow-tooltip />
        <el-table-column label="付款方式" prop="paymentMethod" min-width="120" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" min-width="140" show-overflow-tooltip />
        <el-table-column label="录入人" prop="recorderName" min-width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" min-width="120" show-overflow-tooltip />
      </el-table>
      <pagination
        v-show="historyPage.total > 0"
        :total="historyPage.total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="historyPage.current"
        :limit="historyPage.size"
        @pagination="historyPagination"
      />
    </el-dialog>
        <files-dia ref="filesDia"></files-dia>
  </div>
</template>
<script>
import HomeTab from './components/HomeTab.vue'
import BlacklistTab from './components/BlacklistTab.vue'
<script setup>
import { onMounted, ref } from "vue";
import { Search } from "@element-plus/icons-vue";
import Pagination from "@/components/PIMTable/Pagination.vue";
import { delSupplier } from "@/api/basicData/supplierManageFile.js";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
import { purchaseListPage } from "@/api/procurementManagement/procurementLedger.js";
import { productTreeList, modelList } from "@/api/basicData/product.js";
import {
  addSupplier,
  getSupplier,
  listSupplier,
  updateSupplier,
} from "@/api/basicData/supplierManageFile.js";
import useUserStore from "@/store/modules/user";
import { getToken } from "@/utils/auth.js";
import FilesDia from "./filesDia.vue";
const { proxy } = getCurrentInstance();
const userStore = useUserStore();
export default {
  name: 'MainPage',
  components: {
    HomeTab,
    BlacklistTab
const tableColumn = ref([
  {
    label: "供应商名称",
    prop: "supplierName",
  },
  data() {
    return {
      activeTab: 'home'
    }
  {
    label: "公司地址",
    prop: "companyAddress",
  },
  methods: {
    handleTabChange(tabName) {
      this.activeTab = tabName
      this.$nextTick(() => {
        if (tabName === 'home') {
          this.$refs.homeTab && this.$refs.homeTab.getList && this.$refs.homeTab.getList()
        } else if (tabName === 'blacklist') {
          this.$refs.blacklistTab && this.$refs.blacklistTab.getList && this.$refs.blacklistTab.getList()
  {
    label: "维护人",
    prop: "maintainUserName",
  },
  {
    label: "维护时间",
    prop: "maintainTime",
    width:160
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
        fixed: 'right',
        width:230,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        }
      })
    },
  }
      },
      {
        name: "合作历史",
        type: "text",
        clickFun: (row) => {
          openHistoryDialog(row);
        }
      },
            {
                //资质附件
                name: "资质文件",
                type: "text",
                clickFun: (row) => {
                    openFilesFormDia(row)
                }
            }
    ],
  },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const userList = ref([]);
const tableLoading = ref(false);
const productOptions = ref([]);
const modelOptions = ref([]);
const historyDialogVisible = ref(false);
const historyTableData = ref([]);
const historyTableLoading = ref(false);
const currentHistorySupplierName = ref("");
const page = reactive({
  current: 1,
  size: 100,
  total: 0,
});
const historyPage = reactive({
  current: 1,
  size: 100,
  total: 0,
});
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    supplierName: "",
  },
  form: {
    supplierName: "",
    companyAddress: "",
    maintainUserId: "",
    maintainTime: "",
    productId: "",
    productCategory: "",
    productModelId: "",
    specificationModel: "",
  },
  rules: {
    supplierName: [{ required: true, message: "请输入", trigger: "blur" }],
    companyAddress: [{ required: true, message: "请输入", trigger: "blur" }],
    maintainUserId: [{ required: false, message: "请选择", trigger: "change" }],
    maintainTime: [{ required: false, message: "请选择", trigger: "change" }],
  },
});
const { searchForm, form, rules } = toRefs(data);
const filesDia = ref()
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
/** æäº¤ä¸Šä¼ æ–‡ä»¶ */
function submitFileForm() {
  upload.isUploading = true;
  proxy.$refs["uploadRef"].submit();
}
const getList = () => {
  tableLoading.value = true;
  listSupplier({ ...searchForm.value, ...page }).then((res) => {
    tableLoading.value = false;
    tableData.value = res.data.records;
    page.total = res.data.total;
  });
};
// æ‰“开附件弹框
const openFilesFormDia = (row) => {
    nextTick(() => {
        filesDia.value?.openDialog(row)
    })
};
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(供应商导入)
  open: false,
  // å¼¹å‡ºå±‚标题(供应商导入)
  title: "",
  // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
  isUploading: false,
  // æ˜¯å¦æ›´æ–°å·²ç»å­˜åœ¨çš„用户数据
  updateSupport: 1,
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/system/supplier/import",
});
/** å¯¼å…¥æŒ‰é’®æ“ä½œ */
function handleImport() {
  upload.title = "供应商导入";
  upload.open = true;
}
/** ä¸‹è½½æ¨¡æ¿ */
function importTemplate() {
  proxy.download("/system/supplier/downloadTemplate", {}, "供应商导入模板.xlsx");
}
/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
  upload.isUploading = true;
};
/** æ–‡ä»¶ä¸Šä¼ æˆåŠŸå¤„ç† */
const handleFileSuccess = (response, file, fileList) => {
  upload.isUploading = false;
  if(response.code === 200){
    proxy.$modal.msgSuccess("文件上传成功");
    upload.open = false;
    proxy.$refs["uploadRef"].clearFiles();
    getList();
  }else if(response.code === 500){
    proxy.$modal.msgError(response.msg);
  }else{
    proxy.$modal.msgWarning(response.msg);
  }
};
/** æ–‡ä»¶ä¸Šä¼ å¤±è´¥å¤„理 */
const handleFileError = (error, file, fileList) => {
  upload.isUploading = false;
  proxy.$modal.msgError("文件上传失败");
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const getProductTreeData = () => {
  return productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
    return productOptions.value;
  });
};
const getModels = (value) => {
  form.value.productId = value;
  form.value.productModelId = "";
  form.value.specificationModel = "";
  form.value.productCategory = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  });
};
const getProductModel = (value) => {
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    form.value.specificationModel = modelOptions.value[index].model;
  } else {
    form.value.specificationModel = "";
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label;
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode;
      }
    }
  }
  return "";
};
const findNodeIdByLabel = (nodes, label) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].label === label) {
      return nodes[i].id;
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const found = findNodeIdByLabel(nodes[i].children, label);
      if (found) {
        return found;
      }
    }
  }
  return "";
};
const convertIdToValue = (data) => {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id,
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
};
const getHistoryList = () => {
  if (!currentHistorySupplierName.value) {
    historyTableData.value = [];
    historyPage.total = 0;
    return;
  }
  historyTableLoading.value = true;
  purchaseListPage({
    supplierName: currentHistorySupplierName.value,
    current: historyPage.current,
    size: historyPage.size,
  })
    .then((res) => {
      historyTableData.value = res.data.records || [];
      historyPage.total = res.data.total || 0;
    })
    .finally(() => {
      historyTableLoading.value = false;
    });
};
const openHistoryDialog = (row) => {
  currentHistorySupplierName.value = row.supplierName || "";
  historyPage.current = 1;
  historyDialogVisible.value = true;
  getHistoryList();
};
const historyPagination = (obj) => {
  historyPage.current = obj.page;
  historyPage.size = obj.limit;
  getHistoryList();
};
// æ‰“开弹框
const openForm = (type, row) => {
  operationType.value = type;
  form.value = {};
  modelOptions.value = [];
  form.value.maintainUserId = userStore.id;
  form.value.maintainTime = getCurrentDate();
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
  if (type === "edit") {
    getProductTreeData().then(() => {
      getSupplier(row.id).then(async (res) => {
        form.value = { ...res.data };
        try {
          const productId = form.value.producutId || form.value.productId;
          if (productId) {
            form.value.productId = productId;
            form.value.productCategory = findNodeById(productOptions.value, productId);
            const models = await modelList({ id: productId });
            modelOptions.value = models || [];
            const currentModel = (modelOptions.value || []).find((m) => `${m.id}` === `${form.value.supplyProduct}`);
            if (currentModel) {
              form.value.productModelId = currentModel.id;
              form.value.specificationModel = currentModel.model;
            }
          }
        } catch (e) {
          console.error("加载产品规格型号失败", e);
        }
      });
    });
  } else {
    getProductTreeData();
  }
  dialogFormVisible.value = true;
};
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitEdit();
      } else {
        submitAdd();
      }
    }
  });
};
// æäº¤æ–°å¢ž
const submitAdd = () => {
  addSupplier({
    ...form.value,
    supplyProduct: form.value.productModelId,
  }).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
  });
};
// æäº¤ä¿®æ”¹
const submitEdit = () => {
  updateSupplier({
    ...form.value,
    supplyProduct: form.value.productModelId,
  }).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
    getList();
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/system/supplier/export", {}, "供应商档案.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      tableLoading.value = true;
      delSupplier(ids)
        .then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        })
        .finally(() => {
          tableLoading.value = false;
        });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss"></style>
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -2,7 +2,7 @@
  <div>
    <el-dialog
      v-model="dialogFormVisible"
      :title="operationType === 'approval' ? '审批' : '详情'"
      :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
      width="700px"
      @close="closeDia"
    >
@@ -16,7 +16,7 @@
                </el-row>
                <el-row>
                    <el-col :span="24">
                        <el-form-item label="申请部门:">
                        <el-form-item label="申请部门:" prop="approveDeptId">
                            <el-select
                                disabled
                                v-model="form.approveDeptId"
@@ -29,6 +29,14 @@
                                    :value="user.deptId"
                                />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <!-- å®¡æ‰¹æ ‡é¢˜ï¼ˆä»…自由协同审批显示) -->
                <el-row v-if="isFreeApproval">
                    <el-col :span="24">
                        <el-form-item label="审批标题:" prop="approveTitle">
                            <el-input v-model="form.approveTitle" placeholder="请输入" clearable disabled/>
                        </el-form-item>
                    </el-col>
                </el-row>
@@ -168,6 +176,53 @@
        </el-skeleton>
      </div>
      <!-- é”€å”®å®¡æ‰¹ï¼šå±•示销售详情 -->
      <div v-if="isSalesApproval" style="margin: 10px 0 18px;">
        <el-divider content-position="left">销售详情</el-divider>
        <el-skeleton :loading="salesLoading" animated>
          <template #template>
            <el-skeleton-item variant="h3" style="width: 30%" />
            <el-skeleton-item variant="text" style="width: 100%" />
            <el-skeleton-item variant="text" style="width: 100%" />
          </template>
          <template #default>
            <el-empty v-if="!currentSales || !currentSales.salesContractNo" description="未查询到对应销售详情" />
            <template v-else>
              <el-descriptions :column="2" border>
                <el-descriptions-item label="销售合同号">{{ currentSales.salesContractNo }}</el-descriptions-item>
                <el-descriptions-item label="客户名称">{{ currentSales.customerName }}</el-descriptions-item>
                <el-descriptions-item label="业务员">{{ currentSales.salesman }}</el-descriptions-item>
                <el-descriptions-item label="录入人">{{ currentSales.entryPersonName }}</el-descriptions-item>
                <el-descriptions-item label="签订日期">{{ currentSales.executionDate }}</el-descriptions-item>
                <el-descriptions-item label="录入日期">{{ currentSales.entryDate }}</el-descriptions-item>
                <el-descriptions-item label="付款方式">{{ currentSales.paymentMethod }}</el-descriptions-item>
                <el-descriptions-item label="合同金额" :span="2">
                  <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
                    Â¥{{ calculateSalesTotalAmount() }}
                  </span>
                </el-descriptions-item>
              </el-descriptions>
              <div style="margin-top: 20px;">
                <h4>产品明细</h4>
                <el-table :data="currentSales.productData || []" border style="width: 100%">
                  <el-table-column prop="productCategory" label="产品名称" />
                  <el-table-column prop="specificationModel" label="规格型号" />
                  <el-table-column prop="unit" label="单位" />
                  <el-table-column prop="quantity" label="数量" />
                  <el-table-column prop="taxInclusiveUnitPrice" label="含税单价">
                    <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveUnitPrice ?? 0).toFixed(2) }}</template>
                  </el-table-column>
                  <el-table-column prop="taxInclusiveTotalPrice" label="含税总价">
                    <template #default="scope">Â¥{{ Number(scope.row.taxInclusiveTotalPrice ?? 0).toFixed(2) }}</template>
                  </el-table-column>
                </el-table>
              </div>
            </template>
          </template>
        </el-skeleton>
      </div>
      <el-form :model="{ activities }" ref="formRef" label-position="top">
        <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
          <el-step
@@ -231,7 +286,7 @@
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
import { getPurchaseByCode, getSalesByCode } from "@/api/procurementManagement/procurementLedger.js";
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
@@ -253,8 +308,12 @@
const currentQuotation = ref({})
const purchaseLoading = ref(false)
const currentPurchase = ref({})
const salesLoading = ref(false)
const currentSales = ref({})
const isQuotationApproval = computed(() => Number(props.approveType) === 6)
const isPurchaseApproval = computed(() => Number(props.approveType) === 5)
const isSalesApproval = computed(() => Number(props.approveType) === 9)
const isFreeApproval = computed(() => Number(props.approveType) === 10)
const data = reactive({
    form: {
@@ -309,8 +368,8 @@
    getProductOptions().then(() => {
        // ç¡®ä¿å€¼ç±»åž‹åŒ¹é…ï¼ˆå¦‚果选项已加载)
        if (productOptions.value.length > 0 && form.value.approveDeptId) {
            const matchedOption = productOptions.value.find(opt =>
                opt.deptId == form.value.approveDeptId ||
            const matchedOption = productOptions.value.find(opt =>
                opt.deptId == form.value.approveDeptId ||
                String(opt.deptId) === String(form.value.approveDeptId)
            );
            if (matchedOption) {
@@ -351,6 +410,22 @@
        proxy.$modal.msgError('查询采购详情失败')
      }).finally(() => {
        purchaseLoading.value = false
      })
    }
  }
  // é”€å”®å®¡æ‰¹ï¼šç”¨å®¡æ‰¹äº‹ç”±å­—段承载的"销售合同号"去查销售详情
  if (isSalesApproval.value) {
    const salesContractNo = row?.approveReason;
    if (salesContractNo) {
      salesLoading.value = true
      getSalesByCode({ salesContractNo }).then((res) => {
        currentSales.value = res
      }).catch((err) => {
        console.error('查询销售详情失败:', err)
        proxy.$modal.msgError('查询销售详情失败')
      }).finally(() => {
        salesLoading.value = false
      })
    }
  }
@@ -407,8 +482,20 @@
  currentQuotation.value = {}
  purchaseLoading.value = false
  currentPurchase.value = {}
  salesLoading.value = false
  currentSales.value = {}
  emit('close')
};
// è®¡ç®—销售合同金额(产品明细含税总价之和)
const calculateSalesTotalAmount = () => {
  const products = currentSales.value?.productData || []
  const total = products.reduce((sum, item) => {
    return sum + Number(item.taxInclusiveTotalPrice || 0)
  }, 0)
  return total.toFixed(2)
}
defineExpose({
  openDialog,
});
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -19,9 +19,12 @@
            <el-form-item label="申请部门:" prop="approveDeptName">
<!--              <el-input v-model="form.approveDeptName" placeholder="请输入" clearable/>-->
                            <el-select
                                v-model="form.approveDeptId"
                                v-model="form.approveDeptIdArray"
                                placeholder="选择部门"
                multiple
                collapse-tags
                @change="handleDeptChange"
                style="width: 100%"
                            >
                                <el-option
                                    v-for="user in productOptions"
@@ -33,9 +36,17 @@
            </el-form-item>
          </el-col>
        </el-row>
        <!-- å®¡æ‰¹æ ‡é¢˜ï¼ˆä»…当 approveType ä¸º 9 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 9">
          <el-col :span="24">
            <el-form-item label="审批标题:" prop="approveTitle">
              <el-input v-model="form.approveTitle" placeholder="请输入审批标题" clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item :label="props.approveType == 5 ? '采购合同号:' : '审批事由:'" prop="approveReason">
            <el-form-item :label="getApproveReasonLabel()" prop="approveReason">
              <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" />
            </el-form-item>
          </el-col>
@@ -234,8 +245,9 @@
    approveTime: "",
    approveId: "",
    approveUser: "",
        approveDeptId: "",
        approveDeptIdArray: [],
    approveDeptName: "",
    approveTitle: "", // å®¡æ‰¹æ ‡é¢˜ï¼ˆè‡ªç”±ååŒå®¡æ‰¹ä½¿ç”¨ï¼‰
    approveReason: "",
    checkResult: "",
    tempFileIds: [],
@@ -249,7 +261,8 @@
    approveTime: [{ required: false, message: "请输入", trigger: "change" },],
    approveId: [{ required: false, message: "请输入", trigger: "blur" }],
    approveUser: [{ required: false, message: "请输入", trigger: "blur" }],
    approveDeptName: [{ required: true, message: "请输入", trigger: "blur" }],
    approveDeptName: [{ required: true, message: "请选择申请部门", trigger: "change" }],
    approveTitle: [{ required: true, message: "请输入审批标题", trigger: "blur" }],
    approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    startDate: [{ required: true, message: "请选择请假开始时间", trigger: "change" }],
@@ -268,6 +281,19 @@
  }
})
// èŽ·å–å®¡æ‰¹äº‹ç”±æ ‡ç­¾
const getApproveReasonLabel = () => {
  const type = Number(props.approveType)
  if (type === 5) {
    return '采购计划说明:'
  } else if (type === 9) {
    return '销售合同号:'
  } else if (type === 10) {
    return '审批事由:'
  }
  return '审批事由:'
}
// å®¡æ‰¹äººèŠ‚ç‚¹ç›¸å…³
const approverNodes = ref([
  { id: 1, userId: null }
@@ -281,12 +307,12 @@
  approverNodes.value.splice(index, 1)
}
// å¤„理部门选择变化
const handleDeptChange = (deptId) => {
  if (deptId) {
    const selectedDept = productOptions.value.find(dept => dept.deptId === deptId);
    if (selectedDept) {
      form.value.approveDeptName = selectedDept.deptName;
    }
const handleDeptChange = (deptIds) => {
  if (deptIds && deptIds.length > 0) {
    const selectedNames = productOptions.value
      .filter(dept => deptIds.includes(dept.deptId))
      .map(dept => dept.deptName);
    form.value.approveDeptName = selectedNames.join(',');
  } else {
    form.value.approveDeptName = '';
  }
@@ -298,6 +324,7 @@
    userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
  getProductOptions();
    form.value = {}
    approverNodes.value = [
        { id: 1, userId: null }
@@ -306,16 +333,26 @@
  form.value.approveTime = getCurrentDate();
  
  // èŽ·å–å½“å‰ç”¨æˆ·ä¿¡æ¯å¹¶è®¾ç½®éƒ¨é—¨ID
  form.value.approveDeptId = userStore.currentDeptId
  // åŠ è½½éƒ¨é—¨é€‰é¡¹ï¼Œå¹¶åœ¨åŠ è½½å®ŒæˆåŽè®¾ç½®éƒ¨é—¨åç§°
  getProductOptions();
  form.value.approveDeptIdArray = []
  if (operationType.value === 'edit') {
    fileList.value = row.commonFileList
    form.value.tempFileIds = fileList.value.map(file => file.id)
        currentApproveStatus.value = row.approveStatus
    approveProcessGetInfo({id: row.approveId,approveReason: '1'}).then(res => {
            form.value = {...res.data}
      // å¤„理部门反显(根据后端返回的 approveDeptId å¤„理为数组)
      const deptId = res.data.approveDeptId;
      if (deptId !== undefined && deptId !== null) {
        if (Array.isArray(deptId)) {
          form.value.approveDeptIdArray = deptId;
        } else if (typeof deptId === 'string' && deptId.includes(',')) {
          form.value.approveDeptIdArray = deptId.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
        } else if (typeof deptId === 'string' || typeof deptId === 'number') {
          form.value.approveDeptIdArray = [parseInt(deptId)];
        }
      } else {
        form.value.approveDeptIdArray = [];
      }
      // åæ˜¾å®¡æ‰¹äºº
      if (res.data && res.data.approveUserIds) {
        const userIds = res.data.approveUserIds.split(',')
@@ -332,18 +369,8 @@
  }
}
const getProductOptions = () => {
  return getDept().then((res) => {
  getDept().then((res) => {
    productOptions.value = res.data;
    // å¦‚果已有部门ID,自动设置部门名称(用于验证)
    if (form.value.approveDeptId && productOptions.value.length > 0) {
      const matchedDept = productOptions.value.find(dept =>
        dept.deptId == form.value.approveDeptId ||
        String(dept.deptId) === String(form.value.approveDeptId)
      );
      if (matchedDept) {
        form.value.approveDeptName = matchedDept.deptName;
      }
    }
  });
};
function convertIdToValue(data) {
@@ -403,13 +430,14 @@
  }
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      const submitData = { ...form.value };
      if (operationType.value === "add" || currentApproveStatus.value == 3) {
        approveProcessAdd(form.value).then(res => {
        approveProcessAdd(submitData).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
      } else {
        approveProcessUpdate(form.value).then(res => {
        approveProcessUpdate(submitData).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        })
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -6,9 +6,11 @@
      <el-tab-pane label="请假管理" name="2"></el-tab-pane>
      <el-tab-pane label="出差管理" name="3"></el-tab-pane>
      <el-tab-pane label="报销管理" name="4"></el-tab-pane>
      <el-tab-pane label="采购审批" name="5"></el-tab-pane>
      <el-tab-pane label="报价审批" name="6"></el-tab-pane>
      <el-tab-pane label="发货审批" name="7"></el-tab-pane>
      <el-tab-pane label="采购计划审批" name="5"></el-tab-pane>
<!--      <el-tab-pane label="报价审批" name="6"></el-tab-pane>-->
      <el-tab-pane label="出库审批" name="7"></el-tab-pane>
      <el-tab-pane label="销售审批" name="9"></el-tab-pane>
      <el-tab-pane label="自由协同审批" name="10"></el-tab-pane>
    </el-tabs>
    
    <div class="search_form">
@@ -35,18 +37,9 @@
        >
      </div>
      <div>
        <el-button
          type="primary"
          @click="openForm('add')"
          v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
        >新增</el-button>
        <el-button type="primary" @click="openForm('add')" v-if="currentApproveType !== 6 && currentApproveType !== 5 && currentApproveType !== 9">新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button
          type="danger"
          plain
          @click="handleDelete"
          v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
        >删除</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <div class="table_list">
@@ -113,7 +106,8 @@
  const isLeaveType = currentApproveType.value === 2; // è¯·å‡ç®¡ç†
  const isReimburseType = currentApproveType.value === 4; // æŠ¥é”€ç®¡ç†
  const isQuotationType = currentApproveType.value === 6; // æŠ¥ä»·å®¡æ‰¹
  const isPurchaseType = currentApproveType.value === 5; // é‡‡è´­å®¡æ‰¹
  const isSalesType = currentApproveType.value === 9; // é”€å”®å®¡æ‰¹
  const isFreeType = currentApproveType.value === 10; // è‡ªç”±ååŒå®¡æ‰¹
  
  // åŸºç¡€åˆ—配置
  const baseColumns = [
@@ -159,9 +153,16 @@
      prop: "approveDeptName",
      width: 220
    },
    // å®¡æ‰¹æ ‡é¢˜ï¼ˆä»…自由协同审批显示)
    ...(isFreeType ? [{
      label: "审批标题",
      prop: "approveTitle",
      width: 200
    }] : []),
    {
      label: isQuotationType ? "报价单号" : isPurchaseType ? "采购合同号" : "审批事由",
      label: isQuotationType ? "报价单号" : (isSalesType ? "销售合同号" : "审批事由"),
      prop: "approveReason",
      width: 200
    },
    {
      label: "申请人",
@@ -201,61 +202,44 @@
  });
  
  // æ“ä½œåˆ—
  const actionOperations = [
    {
      name: "编辑",
      type: "text",
      clickFun: (row) => {
        openForm("edit", row);
      },
      disabled: (row) =>
        currentApproveType.value === 5 ||
        currentApproveType.value === 6 ||
        currentApproveType.value === 7 ||
        row.approveStatus == 2 ||
        row.approveStatus == 1 ||
        row.approveStatus == 4
    },
    {
      name: "审核",
      type: "text",
      clickFun: (row) => {
        openApprovalDia("approval", row);
      },
      disabled: (row) =>
        row.approveUserCurrentId == null ||
        row.approveStatus == 2 ||
        row.approveStatus == 3 ||
        row.approveStatus == 4 ||
        row.approveUserCurrentId !== userStore.id
    },
    {
      name: "详情",
      type: "text",
      clickFun: (row) => {
        openApprovalDia("view", row);
      },
    },
  ];
  // æŠ¥ä»·å®¡æ‰¹ï¼ˆç±»åž‹ 6)不展示“附件”操作
  if (!isQuotationType) {
    actionOperations.push({
      name: "附件",
      type: "text",
      clickFun: (row) => {
        downLoadFile(row);
      },
    });
  }
  baseColumns.push({
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 230,
    operation: actionOperations,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        },
        disabled: (row) => currentApproveType.value === 6 || row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
      },
      {
        name: "审核",
        type: "text",
        clickFun: (row) => {
          openApprovalDia("approval", row);
        },
        disabled: (row) => row.approveUserCurrentId == null || row.approveStatus == 2 || row.approveStatus == 3 || row.approveStatus == 4 || row.approveUserCurrentId !== userStore.id
      },
      {
        name: "详情",
        type: "text",
        clickFun: (row) => {
          openApprovalDia('view', row);
        },
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          downLoadFile(row);
        },
      },
    ],
  });
  
  return baseColumns;
@@ -310,6 +294,8 @@
    5: "/approveProcess/exportFive",
    6: "/approveProcess/exportSix",
    7: "/approveProcess/exportSeven",
    9: "/approveProcess/exportEight",
    10: "/approveProcess/exportNine",
  }
  const url = urlMap[type] || urlMap[0]
  const nameMap = {
@@ -320,7 +306,9 @@
    4: "报销管理审批表",
    5: "采购申请审批表",
    6: "报价审批表",
    7: "发货审批表",
    7: "出库审批表",
    9: "销售审批表",
    10: "自由协同审批表",
  }
  const fileName = nameMap[type] || nameMap[0]
  proxy.download(url, {}, `${fileName}.xlsx`)
src/views/equipmentManagement/upkeep/Form/PlanModal.vue
@@ -32,6 +32,18 @@
          disabled
        />
      </el-form-item>
            <el-form-item label="保养项目">
                <el-input
                    v-model="form.maintenanceProject"
                placeholder="请输入保养项目"
                />
            </el-form-item>
            <el-form-item label="保养内容">
                <el-input
                    v-model="form.maintenanceContent"
                placeholder="请输入保养内容"
                />
            </el-form-item>
      <el-form-item label="类目">
        <el-input
            v-model="form.machineryCategory"
@@ -115,6 +127,8 @@
  createUser: undefined, // å½•入人
  status: 0, //保修状态
  machineryCategory: undefined,
    maintenanceProject: '',
    maintenanceContent: '',
});
const setDeviceModel = (deviceId) => {
src/views/equipmentManagement/upkeep/Form/formDia.vue
@@ -67,6 +67,24 @@
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="保养项目">
                        <el-input
                            v-model="form.maintenanceProject"
                            placeholder="请输入保养项目"
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="保养内容">
                        <el-input
                            v-model="form.maintenanceContent"
                            placeholder="请输入保养内容"
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="12">
                    <el-form-item label="任务频率" prop="frequencyType">
                        <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                            <el-option label="每日" value="DAILY"/>
@@ -159,6 +177,8 @@
        frequencyDetail: '',
        week: '',
        time: '',
        maintenanceProject: '',
        maintenanceContent: '',
        deviceModel: undefined, // è§„格型号
        registrationDate: ''
    },
src/views/equipmentManagement/upkeep/index.vue
@@ -397,6 +397,16 @@
            cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
    },
    {
        label: "保养项目",
        align: "center",
        prop: "maintenanceProject",
    },
    {
        label: "保养内容",
        align: "center",
        prop: "maintenanceContent",
    },
    {
        label: "保养结果",
        align: "center",
        prop: "maintenanceResult",
src/views/index.vue
@@ -249,13 +249,15 @@
import Echarts from "@/components/Echarts/echarts.vue";
import * as echarts from 'echarts';
import useUserStore from "@/store/modules/user.js";
import { ElNotification } from 'element-plus'
import {
  analysisCustomerContractAmounts, getAmountHalfYear,
  getBusiness,
  homeTodos,
  processDataProductionStatistics,
  statisticsReceivablePayable,
  qualityInspectionStatistics
  qualityInspectionStatistics,
    overdueReceivable
} from "@/api/viewIndex.js";
import { list } from '@/api/productionManagement/productionProcess';
@@ -446,11 +448,32 @@
  getBusinessData()
  analysisCustomer()
  todoInfoS()
    notifyOverdueReceivable()
  statisticsReceivable()
  qualityStatisticsInfo()
  getAmountHalfYearNum()
  getProcessList()
})
// å¾…回款提醒
const notifyOverdueReceivable = async () => {
    try {
        const res = await overdueReceivable()
        const data = res.data
        if (data > 0) {
            ElNotification({
                title: '待回款提醒',
                message: `当前有${data}条待回款`,
                type: 'warning',
                duration: 6000,
            })
        }
    } catch (e) {
        // ä¸å½±å“é¦–页正常加载
        console.error('overdueReceivable error:', e)
    }
}
// æ•°æ®ç»Ÿè®¡
const getBusinessData = () => {
  getBusiness().then((res) => {
src/views/inventoryManagement/dispatchLog/Record.vue
@@ -129,10 +129,9 @@
const total = ref(0);
const props = defineProps({
  type: {
    type: String,
    required: true,
    default: '0'
  productId: {
    type: [String, Number],
    default: ''
  }
})
@@ -163,7 +162,7 @@
};
const getList = () => {
    tableLoading.value = true;
    getStockOutPage({ ...searchForm.value, ...page, type: props.type })
    getStockOutPage({ ...searchForm.value, ...page, topParentProductId: props.productId })
        .then((res) => {
            tableLoading.value = false;
            tableData.value = res.data.records;
@@ -183,17 +182,20 @@
// èŽ·å–æ¥æºç±»åž‹é€‰é¡¹
const fetchStockRecordTypeOptions = () => {
  if (props.type === '0') {
    findAllQualifiedStockOutRecordTypeOptions()
        .then(res => {
          stockRecordTypeOptions.value = res.data;
        })
    return
  }
  findAllUnQualifiedStockOutRecordTypeOptions()
      .then(res => {
        stockRecordTypeOptions.value = res.data;
      })
  // åŒæ—¶èŽ·å–åˆæ ¼å’Œä¸åˆæ ¼çš„æ¥æºç±»åž‹é€‰é¡¹
  Promise.all([
    findAllQualifiedStockOutRecordTypeOptions(),
    findAllUnQualifiedStockOutRecordTypeOptions()
  ]).then(([qualifiedRes, unQualifiedRes]) => {
    const qualified = qualifiedRes.data || [];
    const unQualified = unQualifiedRes.data || [];
    // åˆå¹¶å¹¶åŽ»é‡
    const allOptions = [...qualified, ...unQualified];
    const uniqueOptions = allOptions.filter((item, index, self) =>
      index === self.findIndex((t) => t.value === item.value)
    );
    stockRecordTypeOptions.value = uniqueOptions;
  });
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
@@ -212,7 +214,7 @@
        type: "warning",
    })
        .then(() => {
            proxy.download("/stockOutRecord/exportStockOutRecord", {type: props.type}, props.type === '0' ? "合格出库台账.xlsx" : "不合格出库台账.xlsx");
            proxy.download("/stockOutRecord/exportStockOutRecord", { topParentProductId: props.productId }, "出库台账.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
src/views/inventoryManagement/dispatchLog/index.vue
@@ -1,38 +1,50 @@
<!-- åœ¨ä½ çš„主页面中 -->
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <record :type="tab.type" v-if="activeTab === tab.name" />
      </el-tab-pane>
    </el-tabs>
    <div v-loading="loading" element-loading-text="加载中..." style="min-height: 80vh;">
      <el-tabs v-model="activeTab" @tab-change="handleTabChange" v-if="!loading">
        <el-tab-pane v-for="tab in products"
                     :label="tab.productName"
                     :name="tab.id"
                     :key="tab.id">
          <Record :product-id="tab.id" v-if="tab.id === activeTab" />
        </el-tab-pane>
      </el-tabs>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { productTreeList } from "@/api/basicData/product.js";
import Record from "@/views/inventoryManagement/dispatchLog/Record.vue";
const activeTab = ref('qualified')
const type = ref(0)
const tabs = computed(() => {
  return [
    {
      label: '合格出库',
      name: 'qualified',
      type: '0'
    },
    {
      label: '不合格出库',
      name: 'unqualified',
      type: '1'
    }
  ]
})
const products = ref([])
const activeTab = ref(null)
const loading = ref(false)
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
  type.value = tabName === 'qualified' ? 0 : 1
}
const fetchProducts = async () => {
  loading.value = true;
  try {
    const res = await productTreeList();
    const productList = res.filter((item) => item.parentId === null).map(({ id, productName }) => ({ id, productName }));
    // å°†ç¬¬ä¸€ä¸ªtab的名称改为"销售"
    if (productList.length > 0) {
      productList[0].productName = '销售';
    }
    products.value = productList;
    if (products.value.length > 0) {
      activeTab.value = products.value[0].id;
    }
  } finally {
    loading.value = false;
  }
}
onMounted(() => {
  fetchProducts();
})
</script>
src/views/inventoryManagement/issueManagement/Record.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,570 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">出库时间:</span>
        <el-date-picker
          v-model="searchForm.timeStr"
          type="date"
          placeholder="请选择日期"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          clearable
          @change="handleQuery"
        />
        <span class="search_title ml10">产品名称:</span>
        <el-input v-model="searchForm.productName" style="width: 240px" placeholder="请输入" @change="handleQuery"
          clearable prefix-icon="Search" />
        <span class="search_title ml10">来源:</span>
        <el-select v-model="searchForm.recordType"
                   style="width: 240px"
                   placeholder="请选择"
                   clearable
                   @change="handleQuery">
          <el-option v-for="item in stockRecordTypeOptions"
                     :key="item.value"
                     :label="item.label"
                     :value="item.value"/>
        </el-select>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row, index) => index" style="width: 100%"
        :row-class-name="tableRowClassName" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="产品大类" prop="productName" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column label="合格库存数量" prop="qualifiedQuantity" show-overflow-tooltip />
        <el-table-column label="不合格库存数量" prop="unQualifiedQuantity" show-overflow-tooltip />
        <el-table-column label="合格冻结数量" prop="qualifiedLockedQuantity" show-overflow-tooltip />
        <el-table-column label="不合格冻结数量" prop="unQualifiedLockedQuantity" show-overflow-tooltip />
        <el-table-column label="库存预警数量" prop="warnNum"  show-overflow-tooltip />
        <el-table-column label="备注" prop="remark"  show-overflow-tooltip />
        <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="90" align="center">
          <template #default="scope">
            <el-button link type="primary" @click="showSubtractModal(scope.row)" :disabled="scope.row.unQualifiedUnLockedQuantity === 0 && scope.row.qualifiedUnLockedQuantity === 0">{{ actionButtonText }}</el-button>
            <el-button link type="primary" v-if="scope.row.unQualifiedUnLockedQuantity > 0 || scope.row.qualifiedUnLockedQuantity > 0" @click="showFrozenModal(scope.row)">冻结</el-button>
            <el-button link type="primary" v-if="scope.row.qualifiedLockedQuantity > 0 || scope.row.unQualifiedLockedQuantity > 0" @click="showThawModal(scope.row)">解冻</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <!-- é¢†ç”¨/发货弹框 -->
    <el-dialog
        v-model="isShowSubtractModal"
        :title="dialogTitle"
        width="800"
        @close="closeSubtractModal"
    >
      <el-form label-width="140px" :model="subtractForm" label-position="top" ref="subtractFormRef">
        <el-form-item
            label="产品名称"
            prop="productModelId"
            :rules="[{ required: true, message: '请选择产品', trigger: 'change' }]"
        >
          <el-button type="primary" disabled>
            {{ subtractForm.productName ? subtractForm.productName : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="规格" prop="productModelName">
          <el-input v-model="subtractForm.model" disabled />
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-input v-model="subtractForm.unit" disabled />
        </el-form-item>
        <el-form-item
            label="库存类型"
            prop="type"
            :rules="[{ required: true, message: '请选择库存类型', trigger: 'change' }]"
        >
          <el-select v-model="subtractForm.type" placeholder="请选择库存类型" @change="handleTypeChange">
            <el-option label="合格库存" value="qualified" :disabled="currentRecord.qualifiedUnLockedQuantity <= 0" />
            <el-option label="不合格库存" value="unqualified" :disabled="currentRecord.unQualifiedUnLockedQuantity <= 0" />
          </el-select>
        </el-form-item>
        <el-form-item label="数量" prop="qualitity">
          <el-input-number v-model="subtractForm.qualitity" :step="1" :min="1" :max="maxQuality" style="width: 100%" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="subtractForm.remark" type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubtractSubmit">确认</el-button>
          <el-button @click="closeSubtractModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- å†»ç»“/解冻弹框 -->
    <el-dialog
        v-model="isShowFrozenModal"
        :title="frozenDialogTitle"
        width="800"
        @close="closeFrozenModal"
    >
      <el-form label-width="140px" :model="frozenForm" label-position="top" ref="frozenFormRef">
        <el-form-item
            label="产品名称"
            prop="productModelId"
            :rules="[{ required: true, message: '请选择产品', trigger: 'change' }]"
        >
          <el-button type="primary" disabled>
            {{ frozenForm.productName ? frozenForm.productName : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="规格" prop="productModelName">
          <el-input v-model="frozenForm.model" disabled />
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-input v-model="frozenForm.unit" disabled />
        </el-form-item>
        <el-form-item
            label="库存类型"
            prop="type"
            :rules="[{ required: true, message: '请选择库存类型', trigger: 'change' }]"
        >
          <el-select v-model="frozenForm.type" placeholder="请选择库存类型" @change="handleFrozenTypeChange">
            <el-option
              label="合格库存"
              value="qualified"
              :disabled="frozenOperationType === 'frozen' ? currentRecord.qualifiedUnLockedQuantity <= 0 : currentRecord.qualifiedLockedQuantity <= 0"
            />
            <el-option
              label="不合格库存"
              value="unqualified"
              :disabled="frozenOperationType === 'frozen' ? currentRecord.unQualifiedUnLockedQuantity <= 0 : currentRecord.unQualifiedLockedQuantity <= 0"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="数量" prop="qualitity">
          <el-input-number v-model="frozenForm.qualitity" :step="1" :min="1" :max="frozenMaxQuality" style="width: 100%" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="frozenForm.remark" type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleFrozenSubmit">确认</el-button>
          <el-button @click="closeFrozenModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref, reactive, toRefs, getCurrentInstance, onMounted, computed } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import {
  getStockInventoryListPageCombined
} from "@/api/inventoryManagement/stockInventory.js";
import {
  subtractStockInventory
} from "@/api/inventoryManagement/stockInventory.js";
import {
  subtractStockUnInventory
} from "@/api/inventoryManagement/stockUninventory.js";
import {
  frozenStockInventory,
  thawStockInventory
} from "@/api/inventoryManagement/stockInventory.js";
import {
  frozenStockUninventory,
  thawStockUninventory
} from "@/api/inventoryManagement/stockUninventory.js";
import { getCurrentDate } from "@/utils/index.js";
import {
  findAllQualifiedStockOutRecordTypeOptions, findAllUnQualifiedStockOutRecordTypeOptions,
} from "@/api/basicData/enum.js";
const props = defineProps({
  productId: {
    type: [String, Number],
    default: ''
  },
  productName: {
    type: String,
    default: ''
  }
})
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const tableData = ref([])
const selectedRows = ref([])
const userList = ref([])
const tableLoading = ref(false)
// æ¥æºç±»åž‹é€‰é¡¹
const stockRecordTypeOptions = ref([])
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
// å½“前操作的记录
const currentRecord = ref({})
// æ˜¯å¦æ˜¾ç¤ºé¢†ç”¨/发货弹框
const isShowSubtractModal = ref(false)
// æ˜¯å¦æ˜¾ç¤ºå†»ç»“/解冻弹框
const isShowFrozenModal = ref(false)
// å†»ç»“/解冻操作类型
const frozenOperationType = ref('frozen')
// åˆ¤æ–­æ˜¯å¦æ˜¯æˆå“ï¼ˆäº§å“åç§°åŒ…含"成品")
const isFinishedProduct = computed(() => {
  return props.productName && props.productName.includes('成品')
})
// æŒ‰é’®æ–‡å­—:成品显示"发货",其他显示"领用"
const actionButtonText = computed(() => {
  return isFinishedProduct.value ? '发货' : '领用'
})
// å¼¹æ¡†æ ‡é¢˜
const dialogTitle = computed(() => {
  return isFinishedProduct.value ? '发货' : '领用'
})
// å†»ç»“/解冻弹框标题
const frozenDialogTitle = computed(() => {
  return frozenOperationType.value === 'frozen' ? '冻结' : '解冻'
})
// é¢†ç”¨/发货表单
const subtractFormRef = ref(null)
const subtractForm = ref({
  productId: undefined,
  productModelId: undefined,
  productName: "",
  model: "",
  unit: "",
  type: "",
  qualitity: 0,
  remark: '',
})
// å†»ç»“/解冻表单
const frozenFormRef = ref(null)
const frozenForm = ref({
  productId: undefined,
  productModelId: undefined,
  productName: "",
  model: "",
  unit: "",
  type: "",
  qualitity: 0,
  remark: '',
})
// é¢†ç”¨/发货最大数量
const maxQuality = computed(() => {
  let max = 0;
  if (subtractForm.value.type === 'qualified') {
    max = currentRecord.value.qualifiedUnLockedQuantity ? currentRecord.value.qualifiedUnLockedQuantity : 0;
  } else {
    max = currentRecord.value.unQualifiedUnLockedQuantity ? currentRecord.value.unQualifiedUnLockedQuantity : 0;
  }
  return Math.max(max, 1);
})
// å†»ç»“/解冻最大数量
const frozenMaxQuality = computed(() => {
  let max = 0;
  if (frozenOperationType.value === 'frozen') {
    if (frozenForm.value.type === 'qualified') {
      max = currentRecord.value.qualifiedUnLockedQuantity ? currentRecord.value.qualifiedUnLockedQuantity : 0;
    } else {
      max = currentRecord.value.unQualifiedUnLockedQuantity ? currentRecord.value.unQualifiedUnLockedQuantity : 0;
    }
  } else {
    if (frozenForm.value.type === 'qualified') {
      max = currentRecord.value.qualifiedLockedQuantity ? currentRecord.value.qualifiedLockedQuantity : 0;
    } else {
      max = currentRecord.value.unQualifiedLockedQuantity ? currentRecord.value.unQualifiedLockedQuantity : 0;
    }
  }
  return Math.max(max, 1);
})
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const dialogFormVisible = ref(false)
const data = reactive({
  searchForm: {
    productName: '',
    recordType: '',
    timeStr: '',
    topParentProductId: props.productId,
  },
  form: {
    productrecordId: '',
  },
  rules: {
    inboundTime: [{ required: true, message: "请选择", trigger: "change" }],
    inboundQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
    nickname: [{ required: true, message: "请选择", trigger: "change" }]
  }
})
const { searchForm, form, rules } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const getList = () => {
  tableLoading.value = true
  getStockInventoryListPageCombined({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
  }).catch(() => {
    tableLoading.value = false
  })
}
// èŽ·å–æ¥æºç±»åž‹é€‰é¡¹
const fetchStockRecordTypeOptions = () => {
  Promise.all([
    findAllQualifiedStockOutRecordTypeOptions(),
    findAllUnQualifiedStockOutRecordTypeOptions()
  ]).then(([qualifiedRes, unQualifiedRes]) => {
    const qualified = qualifiedRes.data || [];
    const unQualified = unQualifiedRes.data || [];
    const allOptions = [...qualified, ...unQualified];
    const uniqueOptions = allOptions.filter((item, index, self) =>
      index === self.findIndex((t) => t.value === item.value)
    );
    stockRecordTypeOptions.value = uniqueOptions;
  });
}
// ç‚¹å‡»é¢†ç”¨/发货
const showSubtractModal = (row) => {
  currentRecord.value = row
  subtractForm.value = {
    ...row,
    type: '',
    qualitity: 0,
    remark: '',
  }
  isShowSubtractModal.value = true
}
// å…³é—­é¢†ç”¨/发货弹框
const closeSubtractModal = () => {
  subtractForm.value = {
    productId: undefined,
    productModelId: undefined,
    productName: "",
    model: "",
    unit: "",
    type: "",
    qualitity: 0,
    remark: '',
  }
  isShowSubtractModal.value = false
}
// åº“存类型改变
const handleTypeChange = () => {
  subtractForm.value.qualitity = 0;
}
// å†»ç»“类型改变
const handleFrozenTypeChange = () => {
  frozenForm.value.qualitity = 0;
}
// æäº¤é¢†ç”¨/发货
const handleSubtractSubmit = () => {
  proxy.$refs["subtractFormRef"].validate(valid => {
    if (valid) {
      if (!subtractForm.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      if (subtractForm.value.type === 'qualified') {
        subtractStockInventory(subtractForm.value).then(res => {
          isShowSubtractModal.value = false;
          proxy.$modal.msgSuccess("提交成功");
          getList();
        })
      } else {
        subtractStockUnInventory(subtractForm.value).then(res => {
          isShowSubtractModal.value = false;
          proxy.$modal.msgSuccess("提交成功");
          getList();
        })
      }
    }
  })
}
// ç‚¹å‡»å†»ç»“
const showFrozenModal = (row) => {
  currentRecord.value = row
  frozenOperationType.value = 'frozen'
  frozenForm.value = {
    ...row,
    type: '',
    qualitity: 0,
    remark: '',
  }
  isShowFrozenModal.value = true
}
// ç‚¹å‡»è§£å†»
const showThawModal = (row) => {
  currentRecord.value = row
  frozenOperationType.value = 'thaw'
  frozenForm.value = {
    ...row,
    type: '',
    qualitity: 0,
    remark: '',
  }
  isShowFrozenModal.value = true
}
// å…³é—­å†»ç»“/解冻弹框
const closeFrozenModal = () => {
  frozenForm.value = {
    productId: undefined,
    productModelId: undefined,
    productName: "",
    model: "",
    unit: "",
    type: "",
    qualitity: 0,
    remark: '',
  }
  isShowFrozenModal.value = false
}
// æäº¤å†»ç»“/解冻
const handleFrozenSubmit = () => {
  proxy.$refs["frozenFormRef"].validate(valid => {
    if (valid) {
      if (!frozenForm.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      const isQualified = frozenForm.value.type === 'qualified'
      const isFrozen = frozenOperationType.value === 'frozen'
      if (isQualified) {
        if (isFrozen) {
          frozenStockInventory(frozenForm.value).then(res => {
            isShowFrozenModal.value = false;
            proxy.$modal.msgSuccess("冻结成功");
            getList();
          })
        } else {
          thawStockInventory(frozenForm.value).then(res => {
            isShowFrozenModal.value = false;
            proxy.$modal.msgSuccess("解冻成功");
            getList();
          })
        }
      } else {
        if (isFrozen) {
          frozenStockUninventory(frozenForm.value).then(res => {
            isShowFrozenModal.value = false;
            proxy.$modal.msgSuccess("冻结成功");
            getList();
          })
        } else {
          thawStockUninventory(frozenForm.value).then(res => {
            isShowFrozenModal.value = false;
            proxy.$modal.msgSuccess("解冻成功");
            getList();
          })
        }
      }
    }
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection.filter(item => item.id);
}
const expandedRowKeys = ref([])
// è¡¨æ ¼è¡Œç±»å
const tableRowClassName = ({ row }) => {
  const stock = Number(row?.qualifiedUnLockedQuantity ?? 0);
  const warn = Number(row?.warnNum ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
    return '';
  }
  return stock < warn ? 'row-low-stock' : '';
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm(
    '是否确认导出?',
    '导出', {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning',
    }
  ).then(() => {
    proxy.download("/stockInventory/exportStockInventory", {topParentProductId: props.productId}, '库存信息.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
onMounted(() => {
  getList()
  fetchStockRecordTypeOptions()
})
</script>
<style scoped lang="scss">
:deep(.row-low-stock td) {
  background-color: #fde2e2;
  color: #c45656;
}
:deep(.row-low-stock:hover > td) {
  background-color: #fcd4d4;
}
</style>
src/views/inventoryManagement/issueManagement/index.vue
@@ -1,285 +1,50 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div>
        <span class="search_title">供应商名称:</span>
        <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"
          clearable prefix-icon="Search" />
                <span class="search_title ml10">入库日期:</span>
                <el-date-picker
                    v-model="searchForm.timeStr"
                    type="date"
                    placeholder="请选择日期"
                    value-format="YYYY-MM-DD"
                    format="YYYY-MM-DD"
                    clearable
                    @change="handleQuery"
                />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
      </div>
      <div>
        <!-- <el-button type="primary" @click="openForm('add')">新增出库</el-button> -->
        <el-button @click="handleOut">导出</el-button>
        <!-- <el-button type="danger" plain @click="handleDelete">删除</el-button> -->
      </div>
    <div v-loading="loading" element-loading-text="加载中..." style="min-height: 80vh;">
      <el-tabs v-model="activeTab" @tab-change="handleTabChange" v-if="!loading">
        <el-tab-pane v-for="tab in products"
                     :label="tab.productName"
                     :name="tab.id"
                     :key="tab.id">
          <Record :product-id="tab.id" :product-name="tab.productName" v-if="tab.id === activeTab" />
        </el-tab-pane>
      </el-tabs>
    </div>
    <div class="table_list">
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="入库时间" prop="createTime" width="100" show-overflow-tooltip />
        <el-table-column label="入库批次" prop="inboundBatches" width="160" show-overflow-tooltip />
        <el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
        <el-table-column label="产品大类" prop="productCategory" width="100" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specificationModel" width="200" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
        <el-table-column label="入库数量" prop="inboundNum" width="90" show-overflow-tooltip />
        <el-table-column label="库存数量" prop="inboundNum0" width="90" show-overflow-tooltip />
        <el-table-column label="含税单价" prop="taxInclusiveUnitPrice" width="100" show-overflow-tooltip />
        <el-table-column label="含税总价" prop="taxInclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="税率(%)" prop="taxRate" width="80" show-overflow-tooltip />
        <el-table-column label="不含税总价" prop="taxExclusiveTotalPrice" width="100" show-overflow-tooltip />
        <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="60" align="center">
          <template #default="scope">
            <el-button link type="primary" @click="openForm(scope.row);">领用</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="'新增出库'" width="40%" @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="出库数量:" prop="salesContractNo">
          <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inboundQuantity" placeholder="请输入" clearable />
        </el-form-item>
        <el-form-item label="出库日期:" prop="projectName">
          <el-date-picker style="width: 100%" v-model="form.inboundTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
            type="date" placeholder="请选择" clearable />
        </el-form-item>
        <el-form-item label="出库人:" prop="entryPerson">
          <el-select v-model="form.nickName" placeholder="请选择" clearable>
            <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import pagination from '@/components/PIMTable/Pagination.vue'
import { ref } from 'vue'
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import {
  getStockInPage
} from "@/api/inventoryManagement/stockIn.js";
import {
  getStockManagePage,
    delStockManage,
    stockOut,
} from "@/api/inventoryManagement/stockManage.js";
import { ref, onMounted } from 'vue';
import { productTreeList } from "@/api/basicData/product.js";
import Record from "@/views/inventoryManagement/issueManagement/Record.vue";
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const tableData = ref([])
const selectedRows = ref([])
const userList = ref([])
const tableLoading = ref(false)
const page = reactive({
  current: 1,
  size: 100,
})
const total = ref(0)
const fileList = ref([])
const products = ref([])
const activeTab = ref(null)
const loading = ref(false)
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const dialogFormVisible = ref(false)
const data = reactive({
  searchForm: {
    supplierName: '',
    inboundQuantity:'',
    inboundTime:'',
    nickName: '',
    userId: '',
        timeStr: '',
  },
  form: {
    productrecordId: '',
  },
  rules: {
    inboundTime: [{ required: true, message: "请选择", trigger: "change" }],
    inboundQuantity: [{ required: true, message: "请输入", trigger: "blur" }],
    nickname: [{ required: true, message: "请选择", trigger: "change" }]
  }
})
const { searchForm, form, rules } = toRefs(data)
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1
  getList()
}
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList()
}
const getList = () => {
  tableLoading.value = true
  getStockInPage({ ...searchForm.value, ...page }).then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    total.value = res.data.total
    console.log('res', res.data.records)
  }).catch(() => {
    tableLoading.value = false
  })
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
}
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode.label; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter(item => item.id);
  console.log('selection', selectedRows.value)
}
const expandedRowKeys = ref([])
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, ['contractAmount', 'taxInclusiveTotalPrice', 'taxExclusiveTotalPrice']);
};
const currentRowId = ref(null) // æ–°å¢žï¼šå­˜å‚¨å½“前操作的行ID
const currentRowNum = ref(0)
const salesLedgerProductId = ref(null);
// æ‰“开弹框
const openForm = async (row) => {
  dialogFormVisible.value = true
  currentRowId.value = row.id
  currentRowNum.value = row.inboundNum0
  salesLedgerProductId.value = row.salesLedgerProductId
  form.value = {}
  // åˆå§‹åŒ–表单数据
  form.value = {
    productrecordId: '',
    inboundQuantity: '', // å‡ºåº“数量清空
    inboundTime: getCurrentDate(), // é»˜è®¤å½“前日期
    nickName: '', // é»˜è®¤å½“前用户
  }
  console.log('form',form.value)
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨
const fetchProducts = async () => {
  loading.value = true;
  try {
    const userLists = await userListNoPageByTenantId()
    userList.value = userLists.data
  } catch (error) {
    console.error('加载用户列表失败:', error)
  }
}
// æäº¤è¡¨å•
const submitForm = () => {
  let num = Number(form.value.inboundQuantity)
  if(num <= 0 || num > currentRowNum.value){
    return proxy.$modal.msgWarning("请填入有效数字")
  }
  proxy.$refs["formRef"].validate(valid => {
    if (valid && currentRowId.value) {
      const outData = {
        id: currentRowId.value, // åŽŸå§‹è®°å½•ID
        salesLedgerProductId: salesLedgerProductId.value,
        quantity: form.value.inboundQuantity, // å‡ºåº“数量
        time: form.value.inboundTime, // å‡ºåº“æ—¶é—´
        userId: form.value.nickName // æ“ä½œäºº
      }
      console.log(outData)
      stockOut(outData).then(res => {
        proxy.$modal.msgSuccess("提交成功")
        closeDia()
        getList()
      }).catch(err => {
        proxy.$modal.msgError("出库失败")
      })
    const res = await productTreeList();
    const productList = res.filter((item) => item.parentId === null).map(({ id, productName }) => ({ id, productName }));
    // å°†ç¬¬ä¸€ä¸ªtab的名称改为"销售"
    if (productList.length > 0) {
      productList[0].productName = '销售';
    }
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef")
  dialogFormVisible.value = false
    products.value = productList;
    if (products.value.length > 0) {
      activeTab.value = products.value[0].id;
    }
  } finally {
    loading.value = false;
  }
}
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm(
    '是否确认导出?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
  ).then(() => {
    proxy.download("/stockin/export", {}, '入库台账.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
// åˆ é™¤
const handleDelete = () => {
  let ids = []
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning('请选择数据')
    return
  }
  ElMessageBox.confirm(
    '选中的内容将被删除,是否确认删除?',
    '导出', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }
  ).then(() => {
    delStockManage(ids).then(res => {
      proxy.$modal.msgSuccess("删除成功")
      getList()
    })
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
}
onMounted(() => {
  getList()
  fetchProducts();
})
</script>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/Record.vue
@@ -115,10 +115,9 @@
const {proxy} = getCurrentInstance();
const props = defineProps({
  type: {
    type: String,
    required: true,
    default: '0'
  productId: {
    type: [String, Number],
    default: ''
  }
})
@@ -160,7 +159,7 @@
const getList = () => {
  tableLoading.value = true;
  const params = {...page, type: props.type};
  const params = {...page, topParentProductId: props.productId};
  params.timeStr = searchForm.value.timeStr;
  params.productName = searchForm.value.productName;
  params.recordType = searchForm.value.recordType;
@@ -175,17 +174,20 @@
// èŽ·å–æ¥æºç±»åž‹é€‰é¡¹
const fetchStockRecordTypeOptions = () => {
  if (props.type === '0') {
    findAllQualifiedStockInRecordTypeOptions()
        .then(res => {
          stockRecordTypeOptions.value = res.data;
        })
    return
  }
  findAllUnQualifiedStockInRecordTypeOptions()
      .then(res => {
        stockRecordTypeOptions.value = res.data;
      })
  // åŒæ—¶èŽ·å–åˆæ ¼å’Œä¸åˆæ ¼çš„æ¥æºç±»åž‹é€‰é¡¹
  Promise.all([
    findAllQualifiedStockInRecordTypeOptions(),
    findAllUnQualifiedStockInRecordTypeOptions()
  ]).then(([qualifiedRes, unQualifiedRes]) => {
    const qualified = qualifiedRes.data || [];
    const unQualified = unQualifiedRes.data || [];
    // åˆå¹¶å¹¶åŽ»é‡
    const allOptions = [...qualified, ...unQualified];
    const uniqueOptions = allOptions.filter((item, index, self) =>
      index === self.findIndex((t) => t.value === item.value)
    );
    stockRecordTypeOptions.value = uniqueOptions;
  });
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
@@ -203,8 +205,7 @@
    type: "warning",
  })
      .then(() => {
        // æ ¹æ®ä¸åŒçš„ tab ç±»åž‹è°ƒç”¨ä¸åŒçš„导出接口
        proxy.download("/stockInRecord/exportStockInRecord", {type: props.type}, props.type === '0' ? "合格入库.xlsx" : "不合格入库.xlsx");
        proxy.download("/stockInRecord/exportStockInRecord", { topParentProductId: props.productId }, "入库台账.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
src/views/inventoryManagement/receiptManagement/index.vue
@@ -1,36 +1,45 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <el-tab-pane v-for="tab in tabs"
                   :label="tab.label"
                   :name="tab.name"
                   :key="tab.name">
        <record :type="tab.type" v-if="activeTab === tab.name" />
      </el-tab-pane>
    </el-tabs>
    <div v-loading="loading" element-loading-text="加载中..." style="min-height: 80vh;">
      <el-tabs v-model="activeTab" @tab-change="handleTabChange" v-if="!loading">
        <el-tab-pane v-for="tab in products"
                     :label="tab.productName"
                     :name="tab.id"
                     :key="tab.id">
          <Record :product-id="tab.id" v-if="tab.id === activeTab" />
        </el-tab-pane>
      </el-tabs>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { productTreeList } from "@/api/basicData/product.js";
import Record from "@/views/inventoryManagement/receiptManagement/Record.vue";
const activeTab = ref('qualified')
const type = ref(0)
const tabs = ref([
  {
    label: '合格入库',
    name: 'qualified',
    type: '0'
  },
  {
    label: '不合格入库',
    name: 'unqualified',
    type: '1'
  }
])
const products = ref([])
const activeTab = ref(null)
const loading = ref(false)
const handleTabChange = (tabName) => {
  activeTab.value = tabName;
  type.value = tabName === 'qualified' ? 0 : 1
}
const fetchProducts = async () => {
  loading.value = true;
  try {
    const res = await productTreeList();
    products.value = res.filter((item) => item.parentId === null).map(({ id, productName }) => ({ id, productName }));
    if (products.value.length > 0) {
      activeTab.value = products.value[0].id;
    }
  } finally {
    loading.value = false;
  }
}
onMounted(() => {
  fetchProducts();
})
</script>
src/views/personnelManagement/contractManagement/index.vue
@@ -132,11 +132,6 @@
    prop: "postJob",
  },
  {
    label: "现住址",
    prop: "adress",
    width: 200
  },
  {
    label: "第一学历",
    prop: "firstStudy",
  },
@@ -164,15 +159,15 @@
    prop: "emergencyContactPhone",
    width: 150
  },
  // {
  //   label: "合同年限",
  //   prop: "contractTerm",
  // },
  // {
  //   label: "合同开始日期",
  //   prop: "contractStartTime",
  //   width: 120
  // },
  {
    label: "合同年限",
    prop: "contractTerm",
  },
  {
    label: "合同开始日期",
    prop: "contractStartTime",
    width: 120
  },
  {
    label: "合同结束日期",
    prop: "contractExpireTime",
@@ -238,7 +233,6 @@
  tableLoading.value = true;
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined
  params.staffState = 1
  staffOnJobListPage(params).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
src/views/personnelManagement/dimission/components/formDia.vue
@@ -51,11 +51,6 @@
                {{ currentStaffRecord.postName || '-' }}
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="现住址:">
                {{ currentStaffRecord.adress || '-' }}
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="30">
            <el-col :span="12">
@@ -135,6 +130,18 @@
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="工作交接:" prop="remark">
                <el-input
                    v-model="form.workTransfer"
                    type="textarea"
                    :rows="3"
                    placeholder="工作交接"
                    maxlength="500"
                    show-word-limit
                />
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
@@ -186,6 +193,7 @@
    leaveDate: "",
    reason: "",
    remark: "",
    workTransfer: "",
  },
  rules: {
    staffName: [{ required: true, message: "请选择人员" }],
@@ -213,6 +221,7 @@
    form.value.leaveDate = row.leaveDate
    form.value.reason = row.reason
    form.value.remark = row.remark
    form.value.workTransfer = row.workTransfer
    personList.value = [
      {
        staffName: row.staffName,
src/views/personnelManagement/dimission/index.vue
@@ -104,11 +104,6 @@
    prop: "postName",
  },
  {
    label: "现住址",
    prop: "adress",
    width:200
  },
  {
    label: "第一学历",
    prop: "firstStudy",
  },
@@ -137,6 +132,11 @@
    width:150
  },
  {
    label: "工作交接",
    prop: "workTransfer",
    width:150
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
src/views/personnelManagement/employeeRecord/components/BasicInfoSection.vue
@@ -67,44 +67,61 @@
        </el-form-item>
      </el-col>
    </el-row>
        <el-row :gutter="30">
            <el-col :span="5">
                <el-form-item label="身份证号:"
                            prop="identityCard">
                    <el-input v-model="form.identityCard"
                            placeholder="请输入"
                            clearable />
                </el-form-item>
            </el-col>
            <el-col :span="5">
                <el-form-item label="薪资:"
                            prop="salary">
                    <el-input v-model="form.salary"
                            placeholder="请输入"
                            clearable />
                </el-form-item>
            </el-col>
            <el-col :span="5">
                <el-form-item label="出生日期" prop="birthDate">
                    <el-date-picker
                        v-model="form.birthDate"
                        type="date"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        placeholder="请选择"
                        style="width: 100%"
                        clearable
                    />
                </el-form-item>
            </el-col>
            <el-col :span="5">
                <el-form-item label="年龄" prop="age">
                    <el-input-number
                        v-model="form.age"
                        :min="0"
                        :max="150"
                        :precision="0"
                        :step="1"
                        style="width: 100%"
                    />
                </el-form-item>
            </el-col>
            <el-col :span="4">
                <el-form-item label="籍贯" prop="nativePlace">
                    <el-input
                        v-model="form.nativePlace"
                        placeholder="请输入"
                        clearable
                        maxlength="50"
                        show-word-limit
                    />
                </el-form-item>
            </el-col>
        </el-row>
    <el-row :gutter="24">
      <el-col :span="5">
        <el-form-item label="出生日期" prop="birthDate">
          <el-date-picker
            v-model="form.birthDate"
            type="date"
            value-format="YYYY-MM-DD"
            format="YYYY-MM-DD"
            placeholder="请选择"
            style="width: 100%"
            clearable
          />
        </el-form-item>
      </el-col>
      <el-col :span="5">
        <el-form-item label="年龄" prop="age">
          <el-input-number
            v-model="form.age"
            :min="0"
            :max="150"
            :precision="0"
            :step="1"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
      <el-col :span="5">
        <el-form-item label="籍贯" prop="nativePlace">
          <el-input
            v-model="form.nativePlace"
            placeholder="请输入"
            clearable
            maxlength="50"
            show-word-limit
          />
        </el-form-item>
      </el-col>
      <el-col :span="5">
        <el-form-item label="民族" prop="nation">
          <el-input
@@ -116,7 +133,7 @@
          />
        </el-form-item>
      </el-col>
      <el-col :span="4">
      <el-col :span="5">
        <el-form-item label="婚姻状况" prop="maritalStatus">
          <el-select
            v-model="form.maritalStatus"
@@ -131,27 +148,24 @@
          </el-select>
        </el-form-item>
      </el-col>
    </el-row>
    <el-row :gutter="24">
      <el-col :span="10">
        <el-form-item label="角色" prop="roleId">
          <el-select
            v-model="form.roleId"
            placeholder="请选择"
            clearable
            style="width: 100%"
          >
            <el-option
              v-for="item in roleOptions"
              :key="item.roleId"
              :label="item.roleName"
              :value="item.roleId"
              :disabled="item.status == 1"
            />
          </el-select>
        </el-form-item>
      </el-col>
            <el-col :span="5">
                <el-form-item label="角色" prop="roleId">
                    <el-select
                        v-model="form.roleId"
                    placeholder="请选择"
                    clearable
                    style="width: 100%"
                    >
                        <el-option
                            v-for="item in roleOptions"
                        :key="item.roleId"
                        :label="item.roleName"
                        :value="item.roleId"
                        :disabled="item.status == 1"
                        />
                    </el-select>
                </el-form-item>
            </el-col>
    </el-row>
  </el-card>
</template>
src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue
@@ -111,6 +111,8 @@
  hukouType: "",
  email: "",
  currentAddress: "",
    identityCard: "",
    salary: "",
  // åœ¨èŒä¿¡æ¯
  contractStartTime: "",
  contractEndTime: "",
src/views/personnelManagement/employeeRecord/index.vue
@@ -166,6 +166,21 @@
    label: "性别",
    prop: "sex",
  },
    {
        label: "身份证号",
        prop: "identityCard",
        formatData: (value) => {
            if (!value) {
                return "";
            }
            const text = String(value);
            if (text.length <= 5) {
                return text;
            }
            return text.slice(0, 5) + "*".repeat(text.length - 5);
        },
        width:150
    },
  {
    label: "出生日期",
    prop: "birthDate",
@@ -298,6 +313,7 @@
  tableLoading.value = true;
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined
    params.staffState = 1
  staffOnJobListPage({...params}).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
src/views/procurementManagement/paymentEntry/index.vue
@@ -21,6 +21,28 @@
                @change="handleQuery"
              />
            </el-form-item>
            <el-form-item label="登记日期">
              <el-date-picker
                v-model="searchForm.registrationtDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
                @change="handleQuery"
              />
            </el-form-item>
            <el-form-item label="已付款金额">
              <el-input-number
                v-model="searchForm.ticketsTotal"
                :min="0"
                :precision="2"
                placeholder="请输入"
                style="width: 180px"
                clearable
                @change="handleQuery"
              />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
            </el-form-item>
@@ -322,6 +344,8 @@
    status: false,
    // åªæŸ¥è¯¢å®¡æ‰¹çŠ¶æ€ä¸º 3 çš„记录
    approvalStatus: 3,
    registrationtDate: "", // ç™»è®°æ—¥æœŸ
    ticketsTotal: undefined, // å·²ä»˜æ¬¾é‡‘额
  },
  form: {
    purchaseContractNumber: "",
src/views/procurementManagement/procurementInvoiceLedger/index.vue
@@ -34,6 +34,17 @@
          @change="getTableData"
        />
      </el-form-item>
      <el-form-item label="来票金额">
        <el-input-number
          v-model="filters.ticketsAmount"
          style="width: 180px"
          placeholder="请输入"
          :min="0"
          :precision="2"
          clearable
          @change="getTableData"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters"> é‡ç½® </el-button>
@@ -143,6 +154,7 @@
    purchaseContractNumber: undefined, // é‡‡è´­åˆåŒå·
    supplierName: undefined, // ä¾›åº”商
    createdAt: [], // æ¥ç¥¨æ—¥æœŸ
    ticketsAmount: undefined, // æ¥ç¥¨é‡‘额
  },
  [
    {
@@ -184,12 +196,12 @@
      },
    },
    {
      label: "开票日期",
      label: "来票日期",
      prop: "createdAt",
      width: 110,
    },
    {
      label: "开票金额",
      label: "来票金额",
      prop: "ticketsAmount",
      width: 200,
      formatData: (cell) => {
@@ -430,7 +442,7 @@
const handleDelete = (row) => {
    let ids = [];
    ids.push(row.id);
    ElMessageBox.confirm("该开票台账将被删除,是否确认删除", {
    ElMessageBox.confirm("该来票台账将被删除,是否确认删除", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
src/views/procurementManagement/procurementLedger/index.vue
@@ -2,464 +2,427 @@
  <div class="app-container">
    <div class="search_form">
      <div>
        <el-form :model="searchForm"
                 :inline="true">
        <el-form :model="searchForm" :inline="true">
          <el-form-item label="供应商名称:">
            <el-input v-model="searchForm.supplierName"
                      placeholder="请输入"
                      clearable
                      prefix-icon="Search"
            <el-input v-model="searchForm.supplierName" placeholder="请输入" clearable prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="采购合同号:">
            <el-input v-model="searchForm.purchaseContractNumber"
                      style="width: 240px"
                      placeholder="请输入"
                      @change="handleQuery"
                      clearable
                      :prefix-icon="Search" />
            <el-input
                v-model="searchForm.purchaseContractNumber"
                style="width: 240px"
                placeholder="请输入"
                @change="handleQuery"
                clearable
                :prefix-icon="Search"
            />
          </el-form-item>
          <el-form-item label="销售合同号:">
            <el-input v-model="searchForm.salesContractNo"
                      placeholder="请输入"
                      clearable
                      prefix-icon="Search"
            <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="录入日期:">
            <el-date-picker v-model="searchForm.entryDate"
                            value-format="YYYY-MM-DD"
                            format="YYYY-MM-DD"
                            type="daterange"
                            placeholder="请选择"
                            clearable
                            @change="changeDaterange" />
            <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                            placeholder="请选择" clearable @change="changeDaterange" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary"
                       @click="handleQuery"> æœç´¢ </el-button>
            <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <div class="table_list">
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="primary"
                   @click="openForm('add')">新增台账</el-button>
        <el-button type="primary" plain @click="handleImport">导入</el-button>
        <el-button type="primary" @click="openForm('add')">新增台账</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
                   @click="handleDelete">删除</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
      <el-table :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
                :expand-row-keys="expandedRowKeys"
                :row-key="(row) => row.id"
                show-summary
                :summary-method="summarizeMainTable"
                @expand-change="expandChange"
                height="calc(100vh - 21.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys"
        :row-key="(row) => row.id"
        show-summary
        :summary-method="summarizeMainTable"
        @expand-change="expandChange"
        height="calc(100vh - 19em)"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table :data="props.row.children"
                      border
                      show-summary
                      :summary-method="summarizeChildrenTable">
              <el-table-column align="center"
                               label="序号"
                               type="index"
                               width="60" />
              <el-table-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="单位"
                               prop="unit" />
              <el-table-column label="数量"
                               prop="quantity" />
              <el-table-column label="可用数量"
                               prop="availableQuality" />
              <el-table-column label="退货数量"
                               prop="returnQuality" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="formattedNumber" />
            <el-table
              :data="props.row.children"
              border
              show-summary
              :summary-method="summarizeChildrenTable"
            >
              <el-table-column
                align="center"
                label="序号"
                type="index"
                width="60"
              />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column
                label="含税单价(元)"
                prop="taxInclusiveUnitPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="含税总价(元)"
                prop="taxInclusiveTotalPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="不含税总价(元)"
                prop="taxExclusiveTotalPrice"
                :formatter="formattedNumber"
              />
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="采购合同号"
                         prop="purchaseContractNumber"
                         width="160"
                         show-overflow-tooltip />
        <el-table-column label="销售合同号"
                         prop="salesContractNo"
                          width="160"
                         show-overflow-tooltip />
        <el-table-column label="供应商名称"
                         prop="supplierName"
                          width="160"
                         show-overflow-tooltip />
        <el-table-column label="审批状态"
                         prop="approvalStatus"
                         width="100"
                         show-overflow-tooltip>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
          label="采购合同号"
          prop="purchaseContractNumber"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="销售合同号"
          prop="salesContractNo"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="供应商名称"
          prop="supplierName"
          show-overflow-tooltip
        />
                <el-table-column label="审批状态"
                           prop="approvalStatus"
                           width="100"
                           show-overflow-tooltip>
                    <template #default="scope">
                        <el-tag
                            :type="getApprovalStatusType(scope.row.approvalStatus)"
                      size="small">
                            {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
                        </el-tag>
                    </template>
                </el-table-column>
        <el-table-column
          label="付款方式"
          width="100"
          prop="paymentMethod"
          show-overflow-tooltip
        />
        <el-table-column
          label="合同金额(元)"
          prop="contractAmount"
          show-overflow-tooltip
          :formatter="formattedNumber"
        />
        <el-table-column
          label="录入人"
          prop="recorderName"
           width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="录入日期"
          prop="entryDate"
           width="100"
          show-overflow-tooltip
        />
        <el-table-column
          fixed="right"
          label="操作"
          width="100"
          align="center"
        >
          <template #default="scope">
            <el-tag
              :type="getApprovalStatusType(scope.row.approvalStatus)"
              size="small">
              {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="签订日期"
                         prop="executionDate"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="付款方式"
                         width="100"
                         prop="paymentMethod"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
                         prop="contractAmount"
                         width="200"
                         show-overflow-tooltip
                         :formatter="formattedNumber" />
        <el-table-column label="录入人"
                         prop="recorderName"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="录入日期"
                         prop="entryDate"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="备注"
                         prop="remarks"
                         width="200"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button link
                       type="primary"
                       @click="openForm('edit', scope.row)"
                       :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">编辑</el-button>
            <el-button link
                       type="primary"
                       @click="downLoadFile(scope.row)">附件</el-button>
            <el-button
              link
              type="primary"
              size="small"
              @click="openForm('edit', scope.row)"
              :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4"
              >编辑</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
      <pagination
        v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
    </div>
    <FormDialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
               :width="'70%'"
               :operation-type="operationType"
               @close="closeDia"
               @confirm="submitForm"
               @cancel="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
    <el-dialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
      width="70%"
      @close="closeDia"
    >
      <el-form
        :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
      >
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="采购合同号:"
                          prop="purchaseContractNumber">
              <el-input v-model="form.purchaseContractNumber"
                        placeholder="请输入"
                        clearable />
            <el-form-item label="采购合同号:" prop="purchaseContractNumber">
              <el-input
                v-model="form.purchaseContractNumber"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="销售合同号:"
                          prop="salesLedgerId">
              <el-select v-model="form.salesLedgerId"
                         placeholder="请选择"
                         filterable
                         clearable
                         @change="salesLedgerChange">
                <el-option v-for="item in salesContractList"
                           :key="item.id"
                           :label="item.salesContractNo"
                           :value="item.id" />
            <el-form-item label="销售合同号:" prop="salesLedgerId">
              <el-select
                v-model="form.salesLedgerId"
                placeholder="请选择"
                clearable
                @change="salesLedgerChange"
              >
                <el-option
                  v-for="item in salesContractList"
                  :key="item.id"
                  :label="item.salesContractNo"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="供应商名称:"
                          prop="supplierId">
              <el-select v-model="form.supplierId"
                         placeholder="请选择"
                         filterable
                         clearable>
                <el-option v-for="item in supplierList"
                           :key="item.id"
                           :label="item.supplierName"
                                                     :value="item.id" >{{item.supplierName + '---' + item.supplierType}}</el-option>
            <el-form-item label="供应商名称:" prop="supplierId">
              <el-select
                v-model="form.supplierId"
                placeholder="请选择"
                                filterable
                clearable
              >
                <el-option
                  v-for="item in supplierList"
                  :key="item.id"
                  :label="item.supplierName"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="付款方式">
                            <el-input
                                v-model="form.paymentMethod"
                                placeholder="请输入"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker
                                style="width: 100%"
                                v-model="form.executionDate"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                type="date"
                                placeholder="请选择"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="签订日期:"
                          prop="executionDate">
              <el-date-picker style="width: 100%"
                              v-model="form.executionDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入人:"
                          prop="recorderId">
              <el-select v-model="form.recorderId"
                         placeholder="请选择"
                         clearable
                         filterable>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
            <el-form-item label="录入人:" prop="recorderId">
              <el-select
                v-model="form.recorderId"
                placeholder="请选择"
                clearable
                disabled
              >
                <el-option
                  v-for="item in userList"
                  :key="item.userId"
                  :label="item.nickName"
                  :value="item.userId"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="录入日期:"
                          prop="entryDate">
              <el-date-picker style="width: 100%"
                              v-model="form.entryDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            <el-form-item label="录入日期:" prop="entryDate">
              <el-date-picker
                style="width: 100%"
                v-model="form.entryDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
                  <span>审批人选择:</span>
                  <el-button type="primary" size="small" @click="addApproverNode" icon="Plus">新增节点</el-button>
                </div>
              </template>
              <div class="approver-nodes-container">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  class="approver-node-item"
                >
                  <div class="approver-node-header">
                    <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                    <el-button
                      v-if="approverNodes.length > 1"
                      type="danger"
                      size="small"
                      text
                      @click="removeApproverNode(index)"
                      icon="Delete"
                    >删除</el-button>
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="请选择审批人"
                    filterable
                    style="width: 100%;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  </el-select>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item>
                            <template #label>
                                <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
                                    <span>审批人选择:</span>
                                    <el-button type="primary" size="small" @click="addApproverNode" icon="Plus">新增节点</el-button>
                                </div>
                            </template>
                            <div class="approver-nodes-container">
                                <div
                                    v-for="(node, index) in approverNodes"
                              :key="node.id"
                              class="approver-node-item"
                                >
                                    <div class="approver-node-header">
                                        <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                                        <el-button
                                            v-if="approverNodes.length > 1"
                                      type="danger"
                                      size="small"
                                      text
                                      @click="removeApproverNode(index)"
                                      icon="Delete"
                                        >删除</el-button>
                                    </div>
                                    <el-select
                                        v-model="node.userId"
                                  placeholder="请选择审批人"
                                  filterable
                                  style="width: 100%;"
                                    >
                                        <el-option
                                            v-for="user in userList"
                                      :key="user.userId"
                                      :label="user.nickName"
                                      :value="user.userId"
                                        />
                                    </el-select>
                                </div>
                            </div>
                        </el-form-item>
                    </el-col>
                </el-row>
        <el-row>
          <el-form-item label="产品信息:"
                        prop="entryDate">
            <el-button type="primary"
                       @click="openProductForm('add')">添加</el-button>
            <el-button plain
                       type="danger"
                       @click="deleteProduct">删除</el-button>
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button type="primary" @click="openProductForm('add')"
              >添加</el-button
            >
            <el-button plain type="danger" @click="deleteProduct"
              >删除</el-button
            >
          </el-form-item>
          <div class="select-button-group"
               style="width: 500px; margin: 20px 0;"
               v-if="operationType === 'add'">
            <el-select filterable
                       allow-create
                       :reserve-keyword="true"
                       :default-first-option="false"
                       clearable
                       v-model="templateName"
                       :input-value="filterInputValue"
                       @filter-change="onTemplateFilterChange"
                       @change="onTemplateChange"
                       @focus="getTemplateList"
                       style="width: 500px;"
                       placeholder="请选择模版或者输入新的模版名称后选择"
                       class="no-arrow-select">
              <el-option v-for="item in templateList"
                         :key="item.id || item.value"
                         :label="item.templateName"
                         :value="item.templateName">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                  <span>{{ item.templateName }}</span>
                  <el-icon
                    v-if="item.id"
                    class="delete-icon"
                    @click.stop="handleDeleteTemplate(item)"
                    style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
                    <Delete />
                  </el-icon>
                </div>
              </el-option>
            </el-select>
            <!-- æŒ‰é’®ï¼šä¸Ž Select é«˜åº¦åŒ¹é…ï¼ŒåŽ»æŽ‰å·¦ä¾§è¾¹æ¡†ï¼Œæ— ç¼è¡”æŽ¥ -->
            <el-button size="small"
                       style="height: 32px;margin-left: 8px;"
                       @click="handleButtonClick"
                       :disabled="!templateName || templateName.trim() === '' || (!currentTemplateId && isTemplateNameDuplicate)">
              ä¿å­˜
            </el-button>
          </div>
        </el-row>
        <el-table :data="productData"
                  border
                  @selection-change="productSelected"
                  show-summary
                  :summary-method="summarizeProTable">
          <el-table-column align="center"
                           type="selection"
                           width="55" />
          <el-table-column align="center"
                           label="序号"
                           type="index"
                           width="60" />
          <el-table-column label="产品大类"
                           prop="productCategory" />
          <el-table-column label="规格型号"
                           prop="specificationModel" />
          <el-table-column label="单位"
                           prop="unit"
                           width="70" />
          <el-table-column label="数量"
                           prop="quantity"
                           width="70" />
          <el-table-column label="库存预警数量"
                           prop="warnNum"
                           width="120"
                           show-overflow-tooltip />
          <el-table-column label="税率(%)"
                           prop="taxRate"
                           width="80" />
          <el-table-column label="含税单价(元)"
                           prop="taxInclusiveUnitPrice"
                           :formatter="formattedNumber"
                           width="150" />
          <el-table-column label="含税总价(元)"
                           prop="taxInclusiveTotalPrice"
                           :formatter="formattedNumber"
                           width="150" />
          <el-table-column label="不含税总价(元)"
                           prop="taxExclusiveTotalPrice"
                           :formatter="formattedNumber"
                           width="150" />
          <el-table-column label="是否质检"
                           prop="isChecked"
                           width="150">
        <el-table
          :data="productData"
          border
          @selection-change="productSelected"
          show-summary
          :summary-method="summarizeProTable"
        >
          <el-table-column align="center" type="selection" width="55" />
          <el-table-column
            align="center"
            label="序号"
            type="index"
            width="60"
          />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" width="70" />
          <el-table-column label="数量" prop="quantity" width="70" />
                    <el-table-column label="库存预警数量" prop="warnNum" width="120" show-overflow-tooltip />
          <el-table-column label="税率(%)" prop="taxRate" width="80" />
          <el-table-column
            label="含税单价(元)"
            prop="taxInclusiveUnitPrice"
            :formatter="formattedNumber"
            width="150"
          />
          <el-table-column
            label="含税总价(元)"
            prop="taxInclusiveTotalPrice"
            :formatter="formattedNumber"
            width="150"
          />
          <el-table-column
            label="不含税总价(元)"
            prop="taxExclusiveTotalPrice"
            :formatter="formattedNumber"
            width="150"
          />
          <el-table-column
            fixed="right"
            label="操作"
            min-width="60"
            align="center"
          >
            <template #default="scope">
              <el-tag :type="scope.row.isChecked ? 'success' : 'info'">
                {{ scope.row.isChecked ? '是' : '否' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column fixed="right"
                           label="操作"
                           min-width="60"
                           align="center">
            <template #default="scope">
              <el-button link
                         type="primary"
                         @click="openProductForm('edit', scope.row, scope.$index)">编辑</el-button>
              <el-button
                link
                type="primary"
                size="small"
                @click="openProductForm('edit', scope.row, scope.$index)"
                >编辑</el-button
              >
            </template>
          </el-table-column>
        </el-table>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="备注·:"
                          prop="remarks">
              <el-input v-model="form.remarks"
                        placeholder="请输入"
                        clearable
                        type="textarea"
                        :rows="2" />
            <el-form-item label="备注·:" prop="remark">
              <el-input
                v-model="form.remark"
                placeholder="请输入"
                clearable
                type="textarea"
                :rows="2"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:"
                          prop="purchaseLedgerFiles">
              <el-upload v-model:file-list="fileList"
                         :action="upload.url"
                         multiple
                         ref="fileUpload"
                         auto-upload
                         :headers="upload.headers"
                         :before-upload="handleBeforeUpload"
                         :on-error="handleUploadError"
                         :on-success="handleUploadSuccess"
                         :on-remove="handleRemove">
            <el-form-item label="附件材料:" prop="remark">
              <el-upload
                v-model:file-list="fileList"
                :action="upload.url"
                multiple
                ref="fileUpload"
                auto-upload
                :headers="upload.headers"
                :before-upload="handleBeforeUpload"
                :on-error="handleUploadError"
                :on-success="handleUploadSuccess"
                :on-remove="handleRemove"
              >
                <el-button type="primary">上传</el-button>
                <template #tip>
                  <div class="el-upload__tip">
@@ -472,1421 +435,1013 @@
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
    <!-- å¯¼å…¥å¼¹çª— -->
    <FormDialog
      v-model="importUpload.open"
      :title="importUpload.title"
      :width="'600px'"
      @close="importUpload.open = false"
      @confirm="submitImportFile"
      @cancel="importUpload.open = false"
    >
      <el-upload
        ref="importUploadRef"
        :limit="1"
        accept=".xlsx,.xls"
        :action="importUpload.url"
        :headers="importUpload.headers"
        :before-upload="importUpload.beforeUpload"
        :on-success="importUpload.onSuccess"
        :on-error="importUpload.onError"
        :on-progress="importUpload.onProgress"
        :on-change="importUpload.onChange"
        :auto-upload="false"
        drag
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          å°†æ–‡ä»¶æ‹–到此处,或<em>点击上传</em>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
        <template #tip>
          <div class="el-upload__tip">
            ä»…支持 xls/xlsx,大小不超过 10MB。
            <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button>
          </div>
        </template>
      </el-upload>
    </FormDialog>
    <FormDialog v-model="productFormVisible"
               :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
               :width="'40%'"
               :operation-type="productOperationType"
               @close="closeProductDia"
               @confirm="submitProduct"
               @cancel="closeProductDia">
      <el-form :model="productForm"
               label-width="140px"
               label-position="top"
               :rules="productRules"
               ref="productFormRef">
      </template>
    </el-dialog>
    <el-dialog
      v-model="productFormVisible"
      :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
      width="40%"
      @close="closeProductDia"
    >
      <el-form
        :model="productForm"
        label-width="140px"
        label-position="top"
        :rules="productRules"
        ref="productFormRef"
      >
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:"
                          prop="productId">
              <el-tree-select v-model="productForm.productId"
                              placeholder="请选择"
                              clearable
                              filterable
                              check-strictly
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
            <el-form-item label="产品大类:" prop="productId">
              <el-tree-select
                v-model="productForm.productId"
                placeholder="请选择"
                clearable
                check-strictly
                @change="getModels"
                :data="productOptions"
                :render-after-expand="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:"
                          prop="productModelId">
              <el-select v-model="productForm.productModelId"
                         placeholder="请选择"
                         filterable
                         clearable
                         @change="getProductModel">
                <el-option v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select
                v-model="productForm.productModelId"
                placeholder="请选择"
                clearable
                @change="getProductModel"
              >
                <el-option
                  v-for="item in modelOptions"
                  :key="item.id"
                  :label="item.model"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单位:"
                          prop="unit">
              <el-input v-model="productForm.unit"
                        placeholder="请输入"
                        clearable />
            <el-form-item label="单位:" prop="unit">
              <el-input
                v-model="productForm.unit"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="税率(%):" prop="taxRate">
<!--                            <el-select-->
<!--                                v-model="productForm.taxRate"-->
<!--                                placeholder="请选择"-->
<!--                                clearable-->
<!--                                @change="mathNum"-->
<!--                            >-->
<!--                                <el-option label="1" value="1" />-->
<!--                                <el-option label="6" value="6" />-->
<!--                                <el-option label="13" value="13" />-->
<!--                            </el-select>-->
              <el-input-number :step="1" :min="0" v-model="productForm.taxRate" style="width: 100%"
                               placeholder="请输入" clearable @change="calculateFromTaxRate" />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税/不含税单价(元):" prop="taxInclusiveUnitPrice">
              <el-input-number
                v-model="productForm.taxInclusiveUnitPrice"
                :precision="2"
                :step="0.1"
                clearable
                style="width: 100%"
                @change="mathNum"
              />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number
                                :step="0.1"
                                clearable
                                :precision="2"
                                style="width: 100%"
                                v-model="productForm.quantity"
                                placeholder="请输入"
                                @change="mathNum"
                            />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
              <el-input-number
                v-model="productForm.taxInclusiveTotalPrice"
                :precision="2"
                :step="0.1"
                clearable
                style="width: 100%"
                @change="reverseMathNum('taxInclusiveTotalPrice')"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="税率(%):"
                          prop="taxRate">
              <el-select v-model="productForm.taxRate"
                         placeholder="请选择"
                         clearable
                         @change="mathNum">
                <el-option label="1"
                           value="1" />
                <el-option label="6"
                           value="6" />
                <el-option label="13"
                           value="13" />
            <el-form-item
              label="不含税总价(元):"
              prop="taxExclusiveTotalPrice"
            >
              <el-input
                v-model="productForm.taxExclusiveTotalPrice"
                @change="reverseMathNum('taxExclusiveTotalPrice')"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="发票类型:" prop="invoiceType">
              <el-select
                v-model="productForm.invoiceType"
                placeholder="请选择"
                clearable
              >
                <el-option label="增普票" value="增普票" />
                <el-option label="增专票" value="增专票" />
              </el-select>
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="库存预警数量:" prop="warnNum">
                            <el-input-number
                                v-model="productForm.warnNum"
                                :precision="2"
                                :step="0.1"
                                clearable
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- æ‰«ç ç™»è®°å¯¹è¯æ¡† -->
    <el-dialog
      v-model="scanDialogVisible"
      title="扫码登记"
      width="60%"
      @close="closeScanDialog"
    >
      <el-form
        :model="scanForm"
        label-width="120px"
        label-position="left"
        :rules="scanRules"
        ref="scanFormRef"
      >
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="含税单价(元):"
                          prop="taxInclusiveUnitPrice">
              <el-input-number v-model="productForm.taxInclusiveUnitPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="mathNum" />
            <el-form-item label="采购合同号:">
              <el-input v-model="scanForm.purchaseContractNumber" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.1"
                               clearable
                               :precision="2"
                               :min="0"
                               style="width: 100%"
                               v-model="productForm.quantity"
                               placeholder="请输入"
                               @change="mathNum" />
            <el-form-item label="供应商名称:">
              <el-input v-model="scanForm.supplierName" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="含税总价(元):"
                          prop="taxInclusiveTotalPrice">
              <el-input-number v-model="productForm.taxInclusiveTotalPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="reverseMathNum('taxInclusiveTotalPrice')" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(元):"
                          prop="taxExclusiveTotalPrice">
              <el-input-number v-model="productForm.taxExclusiveTotalPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="reverseMathNum('taxExclusiveTotalPrice')" />
            <el-form-item label="扫码时间:">
              <el-input v-model="scanForm.scanTime" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="发票类型:"
                          prop="invoiceType">
              <el-select v-model="productForm.invoiceType"
                         placeholder="请选择"
                         clearable>
                <el-option label="增普票"
                           value="增普票" />
                <el-option label="增专票"
                           value="增专票" />
              </el-select>
            <el-form-item label="扫码人:">
              <el-input v-model="scanForm.scannerName" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="库存预警数量:"
                          prop="warnNum">
              <el-input-number v-model="productForm.warnNum"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%" />
            <el-form-item label="扫码状态:">
              <el-tag :type="scanForm.scanStatus === '已扫码' ? 'success' : 'warning'">
                {{ scanForm.scanStatus }}
              </el-tag>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="是否质检:"
                          prop="isChecked">
              <el-radio-group v-model="productForm.isChecked">
                <el-radio label="是"
                          :value="true" />
                <el-radio label="否"
                          :value="false" />
              </el-radio-group>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="扫码备注:">
              <el-input
                v-model="scanForm.scanRemark"
                type="textarea"
                :rows="3"
                placeholder="请输入扫码备注信息"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="扫码记录:">
              <el-table :data="scanRecords" border style="width: 100%">
                <el-table-column label="序号" type="index" width="60" align="center" />
                <el-table-column label="扫码时间" prop="scanTime" width="180" />
                <el-table-column label="扫码人" prop="scannerName" width="120" />
                <el-table-column label="扫码状态" prop="scanStatus" width="100">
                  <template #default="scope">
                    <el-tag :type="scope.row.scanStatus === '已扫码' ? 'success' : 'warning'">
                      {{ scope.row.scanStatus }}
                    </el-tag>
                  </template>
                </el-table-column>
                <el-table-column label="备注" prop="scanRemark" />
              </el-table>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
    <FileListDialog
      ref="fileListRef"
      v-model="fileListDialogVisible"
      title="附件列表"
    />
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitScan">确认扫码</el-button>
          <el-button @click="closeScanDialog">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import { getToken } from "@/utils/auth";
  import pagination from "@/components/PIMTable/Pagination.vue";
  import {
    ref,
    onMounted,
    reactive,
    toRefs,
    getCurrentInstance,
    nextTick,
  } from "vue";
  import { Search, Delete } from "@element-plus/icons-vue";
  import { ElMessageBox, ElMessage } from "element-plus";
  import { userListNoPage } from "@/api/system/user.js";
  import FormDialog from '@/components/Dialog/FormDialog.vue';
  import FileListDialog from '@/components/Dialog/FileListDialog.vue';
  import {
    getSalesLedgerWithProducts,
    addOrUpdateSalesLedgerProduct,
    delProduct,
    delLedgerFile,
    getProductInfoByContractNo,
  } from "@/api/salesManagement/salesLedger.js";
  import {
    addOrEditPurchase,
    addPurchaseTemplate,
    updatePurchaseTemplate,
    createPurchaseNo,
    delPurchase,
    getSalesNo,
    purchaseListPage,
    productList,
    getPurchaseById,
    getOptions,
    getPurchaseTemplateList,
    delPurchaseTemplate,
  } from "@/api/procurementManagement/procurementLedger.js";
  import useFormData from "@/hooks/useFormData.js";
import { getToken } from "@/utils/auth";
import pagination from "@/components/PIMTable/Pagination.vue";
import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { userListNoPage } from "@/api/system/user.js";
import {
  getSalesLedgerWithProducts,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile,
  getProductInfoByContractNo,
} from "@/api/salesManagement/salesLedger.js";
import {
  addOrEditPurchase,
  delPurchase,
  getSalesNo,
  purchaseListPage,
  productList,
  getPurchaseById,
  getOptions,
  createPurchaseNo,
} from "@/api/procurementManagement/procurementLedger.js";
import useFormData from "@/hooks/useFormData.js";
import QRCode from "qrcode";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const productData = ref([]);
const selectedRows = ref([]);
const productSelectedRows = ref([]);
const modelOptions = ref([]);
const userList = ref([]);
const productOptions = ref([]);
const salesContractList = ref([]);
const supplierList = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
});
const total = ref(0);
const fileList = ref([]);
import useUserStore from "@/store/modules/user";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import dayjs from "dayjs";
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
  const productData = ref([]);
  const selectedRows = ref([]);
  const productSelectedRows = ref([]);
  const modelOptions = ref([]);
  const userList = ref([]);
  const productOptions = ref([]);
  const salesContractList = ref([]);
  const supplierList = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const fileList = ref([]);
  import useUserStore from "@/store/modules/user";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import dayjs from "dayjs";
const userStore = useUserStore();
  const userStore = useUserStore();
  // å®¡æ‰¹äººèŠ‚ç‚¹ï¼ˆä»¿é”€å”®å°è´¦å‘è´§å®¡æ‰¹äººï¼‰
  const approverNodes = ref([{ id: 1, userId: null }]);
  let nextApproverId = 2;
  const addApproverNode = () => {
    approverNodes.value.push({ id: nextApproverId++, userId: null });
  };
  const removeApproverNode = (index) => {
    approverNodes.value.splice(index, 1);
  };
  // è®¢å•审批状态显示文本
  const approvalStatusText = {
    1: "待审核",
    2: "审批中",
    3: "审批通过",
    4: "审批失败",
  };
  // èŽ·å–å®¡æ‰¹çŠ¶æ€æ ‡ç­¾ç±»åž‹
  const getApprovalStatusType = (status) => {
    const typeMap = {
      1: "info",      // å¾…审核 - ç°è‰²
      2: "warning",   // å®¡æ‰¹ä¸­ - æ©™è‰²
      3: "success",   // å®¡æ‰¹é€šè¿‡ - ç»¿è‰²
      4: "danger",    // å®¡æ‰¹å¤±è´¥ - çº¢è‰²
    };
    return typeMap[status] || "";
  };
  const templateName = ref("");
  const filterInputValue = ref("");
  const templateList = ref([]);
  const isTemplateNameDuplicate = ref(false); // æ ‡è®°æ¨¡æ¿åç§°æ˜¯å¦é‡å¤
  // å½“前选中的模板ID(用于区分新增模板还是更新模板)
  const currentTemplateId = ref(null);
  // æ£€æŸ¥æ¨¡æ¿åç§°æ˜¯å¦é‡å¤
  const checkTemplateNameDuplicate = name => {
    if (!name || name.trim() === "") {
      isTemplateNameDuplicate.value = false;
      return false;
    }
    const isDuplicate = templateList.value.some(
      item => item.templateName === name.trim()
    );
    isTemplateNameDuplicate.value = isDuplicate;
    return isDuplicate;
  };
  // é˜²æŠ–定时器
  let duplicateCheckTimer = null;
  const onTemplateFilterChange = val => {
    filterInputValue.value = val ?? "";
    // æ¸…除之前的定时器
    if (duplicateCheckTimer) {
      clearTimeout(duplicateCheckTimer);
    }
    // å®žæ—¶æ£€æŸ¥æ¨¡æ¿åç§°æ˜¯å¦é‡å¤ï¼ˆé˜²æŠ–处理,避免频繁提示)
    if (val && val.trim()) {
      duplicateCheckTimer = setTimeout(() => {
        const isDuplicate = checkTemplateNameDuplicate(val);
        if (isDuplicate) {
          ElMessage({
            message: "模板名称已存在,请更换模板名称",
            type: "warning",
            duration: 2000,
          });
        }
      }, 300); // 300ms é˜²æŠ–
    } else {
      isTemplateNameDuplicate.value = false;
    }
  };
  // allow-create æ—¶ï¼Œè¾“入不存在的内容会作为 string å€¼è¿”回;这里同步回输入框以确保文字不丢
  const onTemplateChange = async val => {
    if (typeof val === "string") {
      filterInputValue.value = val;
      // é€‰æ‹©æˆ–输入时检查重复
      checkTemplateNameDuplicate(val);
    }
    // è¿‡æ»¤æ•°æ®ï¼ŒæŸ¥æ‰¾åŒ¹é…çš„æ¨¡æ¿
    const matchedTemplate = templateList.value.find(
      item => item.templateName === val
    );
    if (matchedTemplate?.id) {
      // è®°å½•当前选中的模板ID,后续保存时进行更新操作
      currentTemplateId.value = matchedTemplate.id;
      // é€‰ä¸­å·²æœ‰æ¨¡æ¿æ—¶ï¼Œä¸åº”视为“模板名称重复导致不可保存”
      isTemplateNameDuplicate.value = false;
      // å¦‚果找到模板,只赋值供应商、项目名称、付款方式和产品信息
      if (matchedTemplate.supplierId) {
        form.value.supplierId = matchedTemplate.supplierId;
      }
      if (matchedTemplate.supplierName) {
        form.value.supplierName = matchedTemplate.supplierName;
      }
      if (matchedTemplate.paymentMethod) {
        form.value.paymentMethod = matchedTemplate.paymentMethod;
      }
      // æ¨¡æ¿æ•°æ®ä¸­çš„产品字段是 productList,需要转换为 productData
      productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
    } else {
      // æœªåŒ¹é…åˆ°å·²æœ‰æ¨¡æ¿ï¼Œè§†ä¸ºæ–°æ¨¡æ¿
      currentTemplateId.value = null;
      // å¦‚果没有找到模板,重置表单(保持当前表单状态)
      const currentFormData = { ...form.value };
      const currentProductData = [...productData.value];
      // å¦‚果对话框未打开,先打开
      if (!dialogFormVisible.value) {
        operationType.value = "add";
        dialogFormVisible.value = true;
      }
      // ç­‰å¾…下一个 tick åŽæ¢å¤æ•°æ®
      await nextTick();
      form.value = {
        ...form.value,
        ...currentFormData,
      };
      productData.value = currentProductData;
    }
  };
  // ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
  const operationType = ref("");
  const dialogFormVisible = ref(false);
  const data = reactive({
    searchForm: {
      supplierName: "", // ä¾›åº”商名称
      purchaseContractNumber: "", // é‡‡è´­åˆåŒç¼–号
      salesContractNo: "", // é”€å”®åˆåŒç¼–号
      entryDate: null, // å½•入日期
      entryDateStart: undefined,
      entryDateEnd: undefined,
    },
    form: {
      purchaseContractNumber: "",
      salesLedgerId: "",
      recorderId: "",
      entryDate: "",
      productData: [],
      supplierName: "",
      supplierId: "",
      paymentMethod: "",
      executionDate: "",
      isChecked: true,
    },
    rules: {
      purchaseContractNumber: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      approverId: [
        { required: true, message: "请选择审批人", trigger: "change" },
      ],
      supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const { form, rules } = toRefs(data);
  const { form: searchForm } = useFormData({
    ...data.searchForm,
    // è®¾ç½®å½•入日期范围为当天
    entryDate: [
      dayjs().startOf("day").format("YYYY-MM-DD"),
      dayjs().endOf("day").format("YYYY-MM-DD"),
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    supplierName: "", // ä¾›åº”商名称
    purchaseContractNumber: "", // é‡‡è´­åˆåŒç¼–号
    salesContractNo: "", // é”€å”®åˆåŒç¼–号
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  form: {
    purchaseContractNumber: "",
    salesLedgerId: "",
    projectName: "",
    recorderId: "",
    entryDate: "",
    productData: [],
    supplierName: "",
    supplierId: "",
    paymentMethod: "",
        executionDate: "",
  },
  rules: {
    purchaseContractNumber: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    entryDateStart: dayjs().startOf("day").format("YYYY-MM-DD"),
    entryDateEnd: dayjs().endOf("day").format("YYYY-MM-DD"),
  });
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
        entryDate: [{ required: true, message: "请选择", trigger: "change" }],
        executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const {  form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
  // äº§å“è¡¨å•弹框数据
  const productFormVisible = ref(false);
  const productOperationType = ref("");
  const productOperationIndex = ref("");
  const currentId = ref("");
  const productFormData = reactive({
    productForm: {
      productId: "",
      productCategory: "",
      productModelId: "",
      specificationModel: "",
      unit: "",
      quantity: "",
      taxInclusiveUnitPrice: "",
      taxRate: "",
      taxInclusiveTotalPrice: "",
      taxExclusiveTotalPrice: "",
      invoiceType: "",
      warnNum: "",
      isChecked: true,
    },
    productRules: {
      productId: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      unit: [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      taxInclusiveUnitPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      taxRate: [{ required: true, message: "请选择", trigger: "change" }],
      warnNum: [{ required: true, message: "请选择", trigger: "change" }],
      taxInclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      taxExclusiveTotalPrice: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
      isChecked: [{ required: true, message: "请选择", trigger: "change" }],
    },
  });
  const { productForm, productRules } = toRefs(productFormData);
  const upload = reactive({
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
  });
// äº§å“è¡¨å•弹框数据
const productFormVisible = ref(false);
const productOperationType = ref("");
const productOperationIndex = ref("");
const currentId = ref("");
const productFormData = reactive({
  productForm: {
    productId: "",
    productCategory: "",
    productModelId: "",
    specificationModel: "",
    unit: "",
    quantity: "",
    taxInclusiveUnitPrice: "",
    taxRate: "",
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
        warnNum: "",
  },
  productRules: {
    productId: [{ required: true, message: "请选择", trigger: "change" }],
    productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    taxInclusiveUnitPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    // taxRate: [{ required: true, message: "请选择", trigger: "change" }],
        warnNum: [{ required: true, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxExclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    // invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const { productForm, productRules } = toRefs(productFormData);
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// å®¡æ‰¹äººèŠ‚ç‚¹ï¼ˆä»¿é”€å”®å°è´¦å‘è´§å®¡æ‰¹äººï¼‰
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
const addApproverNode = () => {
    approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
    approverNodes.value.splice(index, 1);
};
  // å¯¼å…¥ç›¸å…³
  const importUploadRef = ref(null);
  const importUpload = reactive({
    title: "导入采购台账",
    open: false,
    url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: (file) => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
      const isLt10M = file.size / 1024 / 1024 < 10;
      if (!isExcel) {
        proxy.$modal.msgError("上传文件只能是 xlsx/xls æ ¼å¼!");
        return false;
      }
      if (!isLt10M) {
        proxy.$modal.msgError("上传文件大小不能超过 10MB!");
        return false;
      }
      return true;
    },
    onChange: (file, fileList) => {
      // noop
    },
    onProgress: (event, file, fileList) => {
      // noop
    },
    onSuccess: (response, file, fileList) => {
      importUpload.isUploading = false;
      if (response?.code === 200) {
        proxy.$modal.msgSuccess("导入成功");
        importUpload.open = false;
        if (importUploadRef.value) {
          importUploadRef.value.clearFiles?.();
        }
        getList();
      } else {
        proxy.$modal.msgError(response?.msg || "导入失败");
      }
    },
    onError: () => {
      importUpload.isUploading = false;
      proxy.$modal.msgError("导入失败,请重试");
    },
  });
// è®¢å•审批状态显示文本
const approvalStatusText = {
    1: "待审核",
    2: "审批中",
    3: "审批通过",
    4: "审批失败",
};
  const handleImport = () => {
    importUpload.title = "导入采购台账";
    importUpload.open = true;
    importUpload.isUploading = false;
    if (importUploadRef.value) {
      importUploadRef.value.clearFiles?.();
    }
  };
// èŽ·å–å®¡æ‰¹çŠ¶æ€æ ‡ç­¾ç±»åž‹
const getApprovalStatusType = (status) => {
    const typeMap = {
        1: "info",      // å¾…审核 - ç°è‰²
        2: "warning",   // å®¡æ‰¹ä¸­ - æ©™è‰²
        3: "success",   // å®¡æ‰¹é€šè¿‡ - ç»¿è‰²
        4: "danger",    // å®¡æ‰¹å¤±è´¥ - çº¢è‰²
    };
    return typeMap[status] || "info";
};
  // ä¸‹è½½å¯¼å…¥æ¨¡æ¿ï¼ˆå¦‚后端路径不同,可在此处调整)
  const downloadTemplate = () => {
    proxy.download("/purchase/ledger/exportTemplate", {}, "采购台账导入模板.xlsx");
  };
const changeDaterange = (value) => {
  if (value) {
    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    searchForm.entryDateStart = undefined;
    searchForm.entryDateEnd = undefined;
  }
  handleQuery();
};
  const submitImportFile = () => {
    importUpload.isUploading = true;
    proxy.$refs["importUploadRef"]?.submit?.();
  };
  const changeDaterange = value => {
    if (value) {
      searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
      searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
    } else {
      searchForm.entryDateStart = undefined;
      searchForm.entryDateEnd = undefined;
    }
    handleQuery();
  };
  const formattedNumber = (row, column, cellValue) => {
    return parseFloat(cellValue).toFixed(2);
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  // ä¿å­˜æ¨¡æ¿
  const handleButtonClick = async () => {
    // æ£€æŸ¥æ¨¡æ¿åç§°æ˜¯å¦ä¸ºç©º
    if (!templateName.value || templateName.value.trim() === "") {
      ElMessage({
        message: "请输入模板名称",
        type: "warning",
      });
      return;
    }
    // å¦‚果是“新增模板”(没有选中已有模板),才需要做重名校验;
    // è‹¥æ˜¯é€‰ä¸­å·²æœ‰æ¨¡æ¿åŽä¿®æ”¹ï¼Œåˆ™å…è®¸ä½¿ç”¨åŽŸåç§°ï¼ˆè§†ä¸ºæ›´æ–°ï¼‰
    if (!currentTemplateId.value) {
      const isDuplicate = checkTemplateNameDuplicate(templateName.value);
      if (isDuplicate) {
        ElMessage({
          message: "模板名称已存在,请更换模板名称",
          type: "warning",
        });
        return;
      }
    }
    // æ£€æŸ¥ä¾›åº”商是否选择
    if (!form.value.supplierId) {
      ElMessage({
        message: "请先选择供应商",
        type: "warning",
      });
      return;
    }
    // æ£€æŸ¥æ˜¯å¦æœ‰äº§å“æ•°æ®
    if (!productData.value || productData.value.length === 0) {
      ElMessage({
        message: '请先添加产品信息',
        type: 'warning',
      });
      return;
    }
    try {
      // èŽ·å–å®¡æ‰¹äººID字符串
      const approveUserIds = approverNodes.value
        .filter(node => node.userId)
        .map(node => node.userId)
        .join(",");
      let params = {
        productData: proxy.HaveJson(productData.value),
        supplierId: form.value.supplierId,
        paymentMethod: form.value.paymentMethod,
        recorderId: form.value.recorderId,
        approveUserIds: approveUserIds,
        templateName: templateName.value.trim(),
      };
      console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value);
      // å¦‚æžœ currentTemplateId æœ‰å€¼ï¼Œè¯´æ˜Žå½“前是“编辑已有模板” â†’ è°ƒç”¨æ›´æ–°æŽ¥å£
      // å¦åˆ™ä¸ºâ€œæ–°å»ºæ¨¡æ¿â€ â†’ è°ƒç”¨æ–°å¢žæŽ¥å£
      let res;
      if (currentTemplateId.value) {
        res = await updatePurchaseTemplate({
          id: currentTemplateId.value,
          ...params,
        });
      } else {
        res = await addPurchaseTemplate(params);
      }
      if (res && res.code === 200) {
        ElMessage({
          message: currentTemplateId.value ? "模板更新成功" : "模板保存成功",
          type: "success",
        });
        // ä¿å­˜æˆåŠŸåŽé‡æ–°èŽ·å–æ¨¡æ¿åˆ—è¡¨
        await getTemplateList();
        // æ¸…空模板名称输入
        templateName.value = "";
        filterInputValue.value = "";
        isTemplateNameDuplicate.value = false;
        // ä¿å­˜/更新完成后,重置当前模板ID
        currentTemplateId.value = null;
      } else {
        ElMessage({
          message: res?.msg || "模板保存失败",
          type: "error",
        });
      }
    } catch (error) {
      console.error("保存模板失败:", error);
      ElMessage({
        message: "模板保存失败,请稍后重试",
        type: "error",
      });
    }
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeChildrenTable = param => {
    return proxy.summarizeTable(
      param,
      [
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
        "ticketsNum",
        "ticketsAmount",
        "futureTickets",
        "futureTicketsAmount",
      ],
      {
        ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
        futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      }
    );
  };
  const paginationChange = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    const { entryDate, ...rest } = searchForm;
    purchaseListPage({ ...rest, ...page })
      .then(res => {
        tableLoading.value = false;
        // tableData.value = res.data.records;
        tableData.value = res.data.records.map(record => ({
          ...record,
        }));
        // åˆå§‹åŒ–子数据数组
        tableData.value.forEach(item => {
          item.children = [];
        });
        total.value = res.data.total;
        expandedRowKeys.value = [];
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  const productSelected = selectedRows => {
    productSelectedRows.value = selectedRows;
  };
  const expandedRowKeys = ref([]);
  // å±•开行
  const expandChange = async (row, expandedRows) => {
    if (expandedRows.length > 0) {
      expandedRowKeys.value = [];
      try {
        const res = await productList({ salesLedgerId: row.id, type: 2 });
        const index = tableData.value.findIndex(item => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data || [];
          expandedRowKeys.value.push(row.id);
        }
      } catch (error) {
        console.error("加载产品列表失败:", error);
        proxy.$modal.msgError("加载产品列表失败");
        // å±•开失败时,移除展开状态
        const index = expandedRows.findIndex(item => item.id === row.id);
        if (index > -1) {
          expandedRows.splice(index, 1);
        }
      }
    } else {
      expandedRowKeys.value = [];
    }
  };
  // ä¸»è¡¨åˆè®¡æ–¹æ³•
  const summarizeMainTable = param => {
    return proxy.summarizeTable(param, ["contractAmount"]);
  };
  // å­è¡¨åˆè®¡æ–¹æ³•
  const summarizeProTable = param => {
    return proxy.summarizeTable(param, [
const formattedNumber = (row, column, cellValue) => {
  return parseFloat(cellValue).toFixed(2);
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(
    param,
    [
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
    ]);
  };
  // æ‰“开弹框
  const openForm = async (type, row) => {
    // ç¼–辑时检查审核状态,只有待审核(1)和审批失败(4)才能编辑
    if (type === "edit" && row) {
      if (row.approvalStatus !== 1 && row.approvalStatus !== 4) {
        proxy.$modal.msgWarning("只有待审核和审批失败状态的记录才能编辑");
        return;
      }
      "ticketsNum",
      "ticketsAmount",
      "futureTickets",
      "futureTicketsAmount",
    ],
    {
      ticketsNum: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
      futureTickets: { noDecimal: true }, // ä¸ä¿ç•™å°æ•°
    }
    await getTemplateList();
    operationType.value = type;
    form.value = {};
    productData.value = [];
    fileList.value = [];
    templateName.value = "";
    filterInputValue.value = "";
    isTemplateNameDuplicate.value = false;
    // é‡ç½®å®¡æ‰¹äººèŠ‚ç‚¹ï¼ˆé»˜è®¤ä¸€ä¸ªç©ºèŠ‚ç‚¹ï¼‰
    approverNodes.value = [{ id: 1, userId: null }];
    nextApproverId = 2;
    try {
      // å¹¶è¡ŒåŠ è½½åŸºç¡€æ•°æ®
      const [userRes, salesRes, supplierRes] = await Promise.all([
        userListNoPage(),
        getSalesNo(),
        getOptions(),
      ]);
      userList.value = userRes.data || [];
      salesContractList.value = salesRes || [];
      // ä¾›åº”商过滤出isWhite=0 çš„æ•°æ®
      supplierList.value = (supplierRes.data || []).filter(
        item => item.isWhite === 0
      );
      // è®¾ç½®é»˜è®¤å€¼
      form.value.recorderId = userStore.id;
      form.value.entryDate = getCurrentDate();
      if (type === "add") {
        // æ–°å¢žæ—¶ç”Ÿæˆé‡‡è´­åˆåŒå·
        try {
          const purchaseNoRes = await createPurchaseNo();
          if (purchaseNoRes?.data) {
            form.value.purchaseContractNumber = purchaseNoRes.data;
          }
        } catch (error) {
          console.error("生成采购合同号失败:", error);
          proxy.$modal.msgWarning("生成采购合同号失败");
        }
      } else if (type === "edit" && row?.id) {
        // ç¼–辑时加载数据
        currentId.value = row.id;
        try {
          const purchaseRes = await getPurchaseById({ id: row.id, type: 2 });
          form.value = { ...purchaseRes };
          productData.value = purchaseRes.productData || [];
          fileList.value = purchaseRes.salesLedgerFiles || [];
          // å¦‚果编辑时有审批人,解析审批人ID字符串并设置到节点中
          if (purchaseRes.approveUserIds) {
            const approverIds = purchaseRes.approveUserIds.split(",");
            approverNodes.value = approverIds.map((id, index) => ({
              id: index + 1,
              userId: Number(id)
            }));
            nextApproverId = approverIds.length + 1;
          }
        } catch (error) {
          console.error("加载采购台账数据失败:", error);
          proxy.$modal.msgError("加载数据失败");
          return;
        }
      }
      if (form.value.salesLedgerId == -1) {
        form.value.salesLedgerId = null;
      }
      console.log(form.value, "form.value===========");
      dialogFormVisible.value = true;
    } catch (error) {
      console.error("打开表单失败:", error);
      proxy.$modal.msgError("加载基础数据失败");
    }
  };
  // ä¸Šä¼ å‰æ ¡æ£€
  function handleBeforeUpload(file) {
    // æ ¡æ£€æ–‡ä»¶å¤§å°
    if (file.size > 1024 * 1024 * 10) {
      proxy.$modal.msgError("上传文件大小不能超过10MB!");
      return false;
    }
    proxy.$modal.loading("正在上传文件,请稍候...");
    return true;
  }
  // ä¸Šä¼ å¤±è´¥
  function handleUploadError(err) {
    proxy.$modal.msgError("上传文件失败");
    proxy.$modal.closeLoading();
  }
  // ä¸Šä¼ æˆåŠŸå›žè°ƒ
  function handleUploadSuccess(res, file, uploadFiles) {
    proxy.$modal.closeLoading();
    if (res.code === 200) {
      file.tempId = res.data.tempId;
      proxy.$modal.msgSuccess("上传成功");
    } else {
      proxy.$modal.msgError(res.msg);
      proxy.$refs.fileUpload.handleRemove(file);
    }
  }
  // ç§»é™¤æ–‡ä»¶
  async function handleRemove(file) {
    if (!file?.id) {
      return;
    }
    console.log("handleRemove", file.id);
    if (file.size > 1024 * 1024 * 10) {
      // ä»…前端清理,不调用删除接口和提示
      return;
    }
    if (operationType.value === "edit" && file.id) {
      try {
        await delLedgerFile([file.id]);
        proxy.$modal.msgSuccess("删除成功");
      } catch (error) {
        console.error("删除文件失败:", error);
        proxy.$modal.msgError("删除文件失败");
      }
    }
  }
  // æäº¤è¡¨å•
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        // å®¡æ‰¹äººå¿…填校验(所有节点都要选人)
        const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
        if (hasEmptyApprover) {
          proxy.$modal.msgError("请为所有审批节点选择审批人!");
          return;
        }
        const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
        if (productData.value.length > 0) {
          // æ–°å¢žæ—¶ï¼Œéœ€è¦ä»Žæ¯ä¸ªäº§å“å¯¹è±¡ä¸­åˆ é™¤ id å­—段
          let processedProductData = productData.value;
          if (operationType.value === "add") {
            processedProductData = productData.value.map(product => {
              const { id, ...rest } = product;
              return rest;
            });
          }
          form.value.productData = proxy.HaveJson(processedProductData);
        } else {
          proxy.$modal.msgWarning("请添加产品信息");
          return;
        }
        let tempFileIds = [];
        if (fileList.value.length > 0) {
          tempFileIds = fileList.value.map(item => item.tempId);
        }
        form.value.tempFileIds = tempFileIds;
        form.value.type = 2;
        form.value.approveUserIds = approveUserIds;
        // å¦‚æžœsalesLedgerId为空,则不传递salesContractNo
        if (!form.value.salesLedgerId) {
          form.value.salesContractNo = "";
        }
        // æ–°å¢žæ—¶ä¸ä¼ é€’id
        const submitData = { ...form.value };
        if (operationType.value === "add") {
          delete submitData.id;
        }
        addOrEditPurchase(submitData).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
          getList();
        });
      }
    });
  };
  // å…³é—­å¼¹æ¡†
  const closeDia = () => {
    proxy.resetForm("formRef");
    // é‡ç½®å®¡æ‰¹äººèŠ‚ç‚¹ï¼ˆé»˜è®¤ä¸€ä¸ªç©ºèŠ‚ç‚¹ï¼‰
    approverNodes.value = [{ id: 1, userId: null }];
    nextApproverId = 2;
    dialogFormVisible.value = false;
  };
  // æ‰“开产品弹框
  const openProductForm = async (type, row, index) => {
    productOperationType.value = type;
    productOperationIndex.value = index;
    productForm.value = {};
    proxy.resetForm("productFormRef");
    productFormVisible.value = true;
    // å…ˆèŽ·å–äº§å“é€‰é¡¹ï¼Œç¡®ä¿æ•°æ®åŠ è½½å®Œæˆ
    await getProductOptions();
    // ç­‰å¾… DOM æ›´æ–°
    await nextTick();
    if (type === "edit") {
      // å¤åˆ¶è¡Œæ•°æ®
      productForm.value = { ...row };
      // å¦‚果是从模板加载的数据,可能没有 productId å’Œ productModelId
      // éœ€è¦æ ¹æ® productCategory å’Œ specificationModel æ¥æŸ¥æ‰¾å¯¹åº”çš„ ID
      if (!productForm.value.productId && productForm.value.productCategory) {
        // æ ¹æ® productCategory æŸ¥æ‰¾ productId
        const findProductIdByCategory = (nodes, categoryName) => {
          for (let i = 0; i < nodes.length; i++) {
            if (nodes[i].label === categoryName) {
              return nodes[i].value;
            }
            if (nodes[i].children && nodes[i].children.length > 0) {
              const found = findProductIdByCategory(nodes[i].children, categoryName);
              if (found) return found;
            }
          }
          return null;
        };
        const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory);
        if (productId) {
          productForm.value.productId = productId;
          // èŽ·å–åž‹å·åˆ—è¡¨å¹¶ç­‰å¾…å®Œæˆ
          const modelRes = await modelList({ id: productId });
          modelOptions.value = modelRes;
          // ç­‰å¾… DOM æ›´æ–°
          await nextTick();
          // æ ¹æ® specificationModel æŸ¥æ‰¾ productModelId
          if (productForm.value.specificationModel && modelOptions.value.length > 0) {
            const modelItem = modelOptions.value.find(
              item => item.model === productForm.value.specificationModel
            );
            if (modelItem) {
              productForm.value.productModelId = modelItem.id;
              // è®¾ç½®è§„格型号和单位
              getProductModel(modelItem.id);
            }
          }
        }
      } else if (productForm.value.productId) {
        // å¦‚果有 productId,正常加载型号列表
        await getModels(productForm.value.productId);
        // ç­‰å¾… DOM æ›´æ–°
        await nextTick();
        if (productForm.value.productModelId) {
          getProductModel(productForm.value.productModelId);
        }
      }
      // æœ€åŽå†ç­‰å¾…一次 DOM æ›´æ–°ï¼Œç¡®ä¿æ‰€æœ‰æ•°æ®éƒ½å·²è®¾ç½®
      await nextTick();
    }
  };
  const getProductOptions = () => {
    return productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
      return res;
    });
  };
  const getModels = value => {
    if (value) {
      productForm.value.productCategory =
        findNodeById(productOptions.value, value) || "";
      return modelList({ id: value }).then(res => {
        modelOptions.value = res;
        return res;
  );
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  purchaseListPage({ ...rest, ...page })
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      tableData.value.map((item) => {
        item.children = [];
      });
    } else {
      productForm.value.productCategory = "";
      modelOptions.value = [];
      return Promise.resolve([]);
    }
  };
  const getProductModel = value => {
    const index = modelOptions.value.findIndex(item => item.id === value);
    if (index !== -1) {
      productForm.value.specificationModel = modelOptions.value[index].model;
      productForm.value.unit = modelOptions.value[index].unit;
    } else {
      productForm.value.specificationModel = null;
      productForm.value.unit = null;
    }
  };
  const findNodeById = (nodes, productId) => {
    for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].value === productId) {
        return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹çš„label
      }
      if (nodes[i].children && nodes[i].children.length > 0) {
        const foundNode = findNodeById(nodes[i].children, productId);
        if (foundNode) {
          return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œç›´æŽ¥è¿”å›žï¼ˆå·²ç»æ˜¯label字符串)
        }
      }
    }
    return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
  };
  function convertIdToValue(data) {
    return data.map(item => {
      const { id, children, ...rest } = item;
      const newItem = {
        ...rest,
        value: id, // å°† id æ”¹ä¸º value
      };
      if (children && children.length > 0) {
        newItem.children = convertIdToValue(children);
      }
      return newItem;
    });
  }
  // æäº¤äº§å“è¡¨å•
  const submitProduct = () => {
    proxy.$refs["productFormRef"].validate(valid => {
      if (valid) {
        if (operationType.value === "edit") {
          submitProductEdit();
        } else {
          if (productOperationType.value === "add") {
            productData.value.push({ ...productForm.value });
            console.log("productData.value---", productData.value);
          } else {
            productData.value[productOperationIndex.value] = {
              ...productForm.value,
            };
          }
          closeProductDia();
        }
      }
    });
  };
  const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 2;
    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeProductDia();
      getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
        productData.value = res.productData;
      });
    });
  };
  // åˆ é™¤äº§å“
  const deleteProduct = () => {
    if (productSelectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    if (operationType.value === "add") {
      productSelectedRows.value.forEach(selectedRow => {
        const index = productData.value.findIndex(
          product => product.id === selectedRow.id
        );
        if (index !== -1) {
          productData.value.splice(index, 1);
        }
      });
    } else {
      let ids = [];
      if (productSelectedRows.value.length > 0) {
        ids = productSelectedRows.value.map(item => item.id);
      }
      ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delProduct(ids).then(res => {
            proxy.$modal.msgSuccess("删除成功");
            closeProductDia();
            getPurchaseById({ id: currentId.value, type: 2 }).then(
              res => {
                productData.value = res.productData;
              }
            );
          });
        })
        .catch(() => {
          proxy.$modal.msg("已取消");
        });
    }
  };
  // å…³é—­äº§å“å¼¹æ¡†
  const closeProductDia = () => {
    proxy.resetForm("productFormRef");
    productFormVisible.value = false;
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
      total.value = res.data.total;
      expandedRowKeys.value = [];
    })
      .then(() => {
        proxy.download("/purchase/ledger/export", {}, "采购台账.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // åˆ é™¤
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.filter(item => item.salesLedgerId === null).map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delPurchase(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  // èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
  function getCurrentDate() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
    const day = String(today.getDate()).padStart(2, "0");
    return `${year}-${month}-${day}`;
  }
  const mathNum = () => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    if (!productForm.value.taxInclusiveUnitPrice) {
      return;
    }
    if (!productForm.value.quantity) {
      return;
    }
    // å«ç¨Žæ€»ä»·è®¡ç®—
    productForm.value.taxInclusiveTotalPrice =
      proxy.calculateTaxIncludeTotalPrice(
        productForm.value.taxInclusiveUnitPrice,
        productForm.value.quantity
      );
    if (productForm.value.taxRate) {
      // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
      productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          productForm.value.taxRate
        );
    }
  };
  const reverseMathNum = field => {
    if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      return;
    }
    const taxRate = Number(productForm.value.taxRate);
    if (!taxRate) return;
    // ç¡®ä¿è¾“入值不为负数
    if (
      field === "taxInclusiveTotalPrice" ||
      field === "taxExclusiveTotalPrice"
    ) {
      const value = Number(productForm.value[field]);
      if (value < 0) {
        productForm.value[field] = "0";
        proxy.$modal.msgWarning("值不能小于0");
        return;
      }
    }
    if (field === "taxInclusiveTotalPrice") {
      // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œæ•°é‡ï¼Œåç®—含税单价
      if (productForm.value.quantity) {
        productForm.value.taxInclusiveUnitPrice = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.quantity)
        ).toFixed(2);
        // ç¡®ä¿ç»“果不为负数
        if (Number(productForm.value.taxInclusiveUnitPrice) < 0) {
          productForm.value.taxInclusiveUnitPrice = "0";
        }
      }
      // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œå«ç¨Žå•价,反算数量
      else if (productForm.value.taxInclusiveUnitPrice) {
        productForm.value.quantity = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.taxInclusiveUnitPrice)
        ).toFixed(2);
        // ç¡®ä¿ç»“果不为负数
        if (Number(productForm.value.quantity) < 0) {
          productForm.value.quantity = "0";
        }
      }
      // åç®—不含税总价
      productForm.value.taxExclusiveTotalPrice = (
        Number(productForm.value.taxInclusiveTotalPrice) /
        (1 + taxRate / 100)
      ).toFixed(2);
      // ç¡®ä¿ç»“果不为负数
      if (Number(productForm.value.taxExclusiveTotalPrice) < 0) {
        productForm.value.taxExclusiveTotalPrice = "0";
      }
    } else if (field === "taxExclusiveTotalPrice") {
      // åç®—含税总价
      productForm.value.taxInclusiveTotalPrice = (
        Number(productForm.value.taxExclusiveTotalPrice) *
        (1 + taxRate / 100)
      ).toFixed(2);
      // ç¡®ä¿ç»“果不为负数
      if (Number(productForm.value.taxInclusiveTotalPrice) < 0) {
        productForm.value.taxInclusiveTotalPrice = "0";
      }
      // å·²çŸ¥æ•°é‡ï¼Œåç®—含税单价
      if (productForm.value.quantity) {
        productForm.value.taxInclusiveUnitPrice = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.quantity)
        ).toFixed(2);
        // ç¡®ä¿ç»“果不为负数
        if (Number(productForm.value.taxInclusiveUnitPrice) < 0) {
          productForm.value.taxInclusiveUnitPrice = "0";
        }
      }
      // å·²çŸ¥å«ç¨Žå•价,反算数量
      else if (productForm.value.taxInclusiveUnitPrice) {
        productForm.value.quantity = (
          Number(productForm.value.taxInclusiveTotalPrice) /
          Number(productForm.value.taxInclusiveUnitPrice)
        ).toFixed(2);
        // ç¡®ä¿ç»“果不为负数
        if (Number(productForm.value.quantity) < 0) {
          productForm.value.quantity = "0";
        }
      }
    }
  };
  // é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
  const salesLedgerChange = async row => {
    console.log("row", row);
    var index = salesContractList.value.findIndex(item => item.id == row);
    console.log("index", index);
    if (index > -1) {
      await querygProductInfoByContractNo();
    }
  };
  const querygProductInfoByContractNo = async () => {
    const { code, data } = await getProductInfoByContractNo({
      contractNo: form.value.salesLedgerId,
    .catch(() => {
      tableLoading.value = false;
    });
    if (code == 200) {
      productData.value = data;
    }
  };
  const fileListRef = ref(null);
  const fileListDialogVisible = ref(false);
  const downLoadFile = row => {
    if (fileListRef.value) {
      fileListRef.value.open(row.salesLedgerFiles);
    }
  };
  // èŽ·å–æ¨¡æ¿ä¿¡æ¯
  const getTemplateList = async () => {
    let res = await getPurchaseTemplateList();
    if (res && res.code === 200 && Array.isArray(res.data)) {
      templateList.value = res.data;
    }
  };
  // åˆ é™¤æ¨¡æ¿
  const handleDeleteTemplate = async (item) => {
    if (!item.id) {
      proxy.$modal.msgWarning("无法删除该模板");
      return;
    }
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const productSelected = (selectedRows) => {
  productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
// å±•开行
const expandChange = (row, expandedRows) => {
  if (expandedRows.length > 0) {
    expandedRowKeys.value = [];
    try {
      await ElMessageBox.confirm(
        `确定要删除模板"${item.templateName}"吗?`,
        "删除确认",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
      productList({ salesLedgerId: row.id, type: 2 }).then((res) => {
        const index = tableData.value.findIndex((item) => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
      );
      const res = await delPurchaseTemplate([item.id]);
      if (res && res.code === 200) {
        ElMessage({
          message: "删除成功",
          type: "success",
        });
        // å¦‚果删除的是当前选中的模板,清空选择
        if (templateName.value === item.templateName) {
          templateName.value = "";
          filterInputValue.value = "";
        }
        // é‡æ–°èŽ·å–æ¨¡æ¿åˆ—è¡¨
        await getTemplateList();
      } else {
        ElMessage({
          message: res?.msg || "删除失败",
          type: "error",
        });
      }
        expandedRowKeys.value.push(row.id);
      });
    } catch (error) {
      if (error !== "cancel") {
        console.error("删除模板失败:", error);
        ElMessage({
          message: "删除失败,请稍后重试",
          type: "error",
        });
      }
      console.log(error);
    }
  };
  onMounted(() => {
    getList();
    getTemplateList();
  } else {
    expandedRowKeys.value = [];
  }
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, ["contractAmount"]);
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeProTable = (param) => {
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// æ‰“开弹框
const openForm = (type, row) => {
  operationType.value = type;
  form.value = {};
  productData.value = [];
  fileList.value = [];
  if (operationType.value == "add") {
    createPurchaseNo().then((res) => {
      form.value.purchaseContractNumber = res.data;
    });
  }
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
  getSalesNo().then((res) => {
    salesContractList.value = res;
  });
  getOptions().then((res) => {
    supplierList.value = res.data;
  });
  form.value.recorderId = userStore.id;
  form.value.entryDate = getCurrentDate();
  if (type === "edit") {
    currentId.value = row.id;
    getPurchaseById({ id: row.id, type: 2 }).then((res) => {
      form.value = { ...res };
      productData.value = form.value.productData;
      if (form.value.salesLedgerFiles) {
        fileList.value = form.value.salesLedgerFiles;
      } else {
        fileList.value = [];
      }
    });
  }
  dialogFormVisible.value = true;
};
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  if (file.size > 1024 * 1024 * 10) {
    proxy.$modal.msgError("上传文件大小不能超过10MB!");
    return false;
  }
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
// ä¸Šä¼ å¤±è´¥
function handleUploadError(err) {
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
}
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
  console.log("handleRemove", file.id);
  if (file.size > 1024 * 1024 * 10) {
    // ä»…前端清理,不调用删除接口和提示
    return;
  }
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
    });
  }
}
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
            // å®¡æ‰¹äººå¿…填校验(所有节点都要选人)
            const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
            if (hasEmptyApprover) {
                proxy.$modal.msgError("请为所有审批节点选择审批人!");
                return;
            }
            const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
      if (productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
        proxy.$modal.msgWarning("请添加产品信息");
        return;
      }
      let tempFileIds = [];
      if (fileList.value.length > 0) {
        tempFileIds = fileList.value.map((item) => item.tempId);
      }
      form.value.tempFileIds = tempFileIds;
      form.value.type = 2;
            form.value.approveUserIds = approveUserIds;
      addOrEditPurchase(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
      });
    }
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
};
// æ‰“开产品弹框
const openProductForm = (type, row, index) => {
  productOperationType.value = type;
  productOperationIndex.value = index;
  productForm.value = {};
  proxy.resetForm("productFormRef");
  if (type === "edit") {
    productForm.value = { ...row };
  }
  productFormVisible.value = true;
  getProductOptions();
};
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
const getModels = (value) => {
  if (value) {
    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
  } else {
    productForm.value.productCategory = "";
    modelOptions.value = [];
  }
};
const getProductModel = (value) => {
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹çš„label
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œç›´æŽ¥è¿”å›žï¼ˆå·²ç»æ˜¯label字符串)
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
  proxy.$refs["productFormRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
      } else {
        if (productOperationType.value === "add") {
          productData.value.push({ ...productForm.value });
          console.log("productData.value---", productData.value);
        } else {
          productData.value[productOperationIndex.value] = {
            ...productForm.value,
          };
        }
        closeProductDia();
      }
    }
  });
};
const submitProductEdit = () => {
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 2;
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeProductDia();
    getPurchaseById({ id: currentId.value, type: 2 }).then((res) => {
      productData.value = res.productData;
    });
  });
};
// åˆ é™¤äº§å“
const deleteProduct = () => {
  if (productSelectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach((selectedRow) => {
      const index = productData.value.findIndex(
        (product) => product.id === selectedRow.id
      );
      if (index !== -1) {
        productData.value.splice(index, 1);
      }
    });
  } else {
    let ids = [];
    if (productSelectedRows.value.length > 0) {
      ids = productSelectedRows.value.map((item) => item.id);
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delProduct(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
          getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
            (res) => {
              productData.value = res.productData;
            }
          );
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  }
};
// å…³é—­äº§å“å¼¹æ¡†
const closeProductDia = () => {
  proxy.resetForm("productFormRef");
  productFormVisible.value = false;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/purchase/ledger/export", {}, "采购台账.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      delPurchase(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º YYYY-MM-DD
function getCurrentDate() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, "0"); // æœˆä»½ä»Ž0开始
  const day = String(today.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}
// ç¨ŽçŽ‡å¯èƒ½ä¸ºç©ºï¼šè¿”å›ž number æˆ– null
const getTaxRateNumberOrNull = () => {
  const raw = productForm.value?.taxRate;
  if (raw === null || raw === undefined || raw === "") return null;
  const n = Number(raw);
  return Number.isFinite(n) ? n : null;
};
const mathNum = () => {
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
  if (!productForm.value.taxInclusiveUnitPrice) {
    return;
  }
  if (!productForm.value.quantity) {
    return;
  }
  // å«ç¨Žæ€»ä»·è®¡ç®—
  productForm.value.taxInclusiveTotalPrice =
    proxy.calculateTaxIncludeTotalPrice(
      productForm.value.taxInclusiveUnitPrice,
      productForm.value.quantity
    );
  // ä¸å«ç¨Žæ€»ä»·è®¡ç®—(税率可能为空)
  const taxRate = getTaxRateNumberOrNull();
  if (taxRate === null) {
    // æœªå¡«å†™ç¨ŽçŽ‡æ—¶ï¼šæŒ‰â€œå«ç¨Ž/不含税一致”处理,避免出现 NaN
    productForm.value.taxExclusiveTotalPrice = productForm.value.taxInclusiveTotalPrice;
  } else {
    productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
        productForm.value.taxInclusiveTotalPrice,
        taxRate
      );
  }
  // if (productForm.value.taxRate) {
  //   // ä¸å«ç¨Žæ€»ä»·è®¡ç®—
  //   productForm.value.taxExclusiveTotalPrice =
  //     proxy.calculateTaxExclusiveTotalPrice(
  //       productForm.value.taxInclusiveTotalPrice,
  //       productForm.value.taxRate
  //     );
  // }
};
const reverseMathNum = (field) => {
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
  const taxRate = getTaxRateNumberOrNull();
  if (field === 'taxInclusiveTotalPrice') {
    // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œæ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
    }
    // å·²çŸ¥å«ç¨Žæ€»ä»·å’Œå«ç¨Žå•价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
    }
    // åç®—不含税总价(税率可能为空)
    if (taxRate === null) {
      productForm.value.taxExclusiveTotalPrice = productForm.value.taxInclusiveTotalPrice;
    } else {
      productForm.value.taxExclusiveTotalPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
    }
  } else if (field === 'taxExclusiveTotalPrice') {
    // åç®—含税总价(税率可能为空)
    if (taxRate === null) {
      productForm.value.taxInclusiveTotalPrice = productForm.value.taxExclusiveTotalPrice;
    } else {
      productForm.value.taxInclusiveTotalPrice =
        (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
    }
    // å·²çŸ¥æ•°é‡ï¼Œåç®—含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
    }
    // å·²çŸ¥å«ç¨Žå•价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
    }
  }
};
// é”€å”®åˆåŒé€‰æ‹©æ”¹å˜æ–¹æ³•
const salesLedgerChange = async (row) => {
  console.log("row", row);
  var index = salesContractList.value.findIndex((item) => item.id == row);
  console.log("index", index);
  if (index > -1) {
    form.value.projectName = salesContractList.value[index].projectName;
    await querygProductInfoByContractNo();
  }
};
const querygProductInfoByContractNo = async () => {
  const { code, data } = await getProductInfoByContractNo({
    contractNo: form.value.salesLedgerId,
  });
  if (code == 200) {
    productData.value = data;
  }
};
// æ‰«ç ç™»è®°å¯¹è¯æ¡†ç›¸å…³å˜é‡
const scanDialogVisible = ref(false);
const scanForm = reactive({
  purchaseContractNumber: "",
  supplierName: "",
  projectName: "",
  scanTime: "",
  scannerName: "",
  scanStatus: "未扫码",
  scanRemark: "",
});
const scanRules = {
  scanRemark: [{ required: true, message: "请输入扫码备注", trigger: "blur" }],
};
const scanRecords = ref([]);
// æ‰“开扫码登记对话框
const openScanDialog = (row) => {
  scanForm.purchaseContractNumber = row.purchaseContractNumber;
  scanForm.supplierName = row.supplierName;
  scanForm.projectName = row.projectName;
  scanForm.scanTime = getCurrentDateTime();
  scanForm.scannerName = userStore.nickName;
  scanForm.scanStatus = "未扫码";
  scanForm.scanRemark = "";
  scanRecords.value = [];
  scanDialogVisible.value = true;
};
// å…³é—­æ‰«ç ç™»è®°å¯¹è¯æ¡†
const closeScanDialog = () => {
  scanDialogVisible.value = false;
  proxy.resetForm("scanFormRef");
};
// æäº¤æ‰«ç ç™»è®°
const submitScan = () => {
  proxy.$refs["scanFormRef"].validate((valid) => {
    if (valid) {
      // æ·»åŠ æ‰«ç è®°å½•
      scanRecords.value.push({
        ...scanForm,
        id: Date.now(), // æ¨¡æ‹ŸID
        scanTime: getCurrentDateTime(),
      });
      scanForm.scanStatus = "已扫码";
      scanForm.scanRemark = scanForm.scanRemark || "无";
      proxy.$modal.msgSuccess("扫码登记成功!");
      closeScanDialog();
    }
  });
};
// èŽ·å–å½“å‰æ—¥æœŸæ—¶é—´
function getCurrentDateTime() {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, "0");
  const day = String(now.getDate()).padStart(2, "0");
  const hours = String(now.getHours()).padStart(2, "0");
  const minutes = String(now.getMinutes()).padStart(2, "0");
  const seconds = String(now.getSeconds()).padStart(2, "0");
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss">
src/views/salesManagement/invoiceLedger/index.vue
@@ -18,6 +18,9 @@
        <el-form-item label="不显示有发票行">
          <el-checkbox v-model="searchForm.status" @change="handleQuery" />
        </el-form-item>
        <el-form-item label="发票金额">
          <el-input-number v-model="searchForm.invoiceTotal" :min="0" :precision="2" placeholder="请输入" style="width: 180px" @change="handleQuery" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
          <el-button @click="resetForm"> é‡ç½® </el-button>
@@ -178,6 +181,7 @@
    invoiceDateStart: undefined,
    invoiceDateEnd: undefined,
    createTimeStart: "", // å½•入日期
    invoiceTotal: undefined, // å‘票金额
  },
  form: {
    salesLedgerId: "",
@@ -203,7 +207,19 @@
  },
});
const { form, rules } = toRefs(data);
const { form: searchForm, resetForm } = useFormData(data.searchForm);
const { form: searchForm } = useFormData(data.searchForm);
// è‡ªå®šä¹‰é‡ç½®å‡½æ•°
const resetForm = () => {
  searchForm.searchText = "";
  searchForm.status = false;
  searchForm.invoiceDate = null;
  searchForm.invoiceDateStart = undefined;
  searchForm.invoiceDateEnd = undefined;
  searchForm.createTimeStart = "";
  searchForm.invoiceTotal = undefined;
  handleQuery();
};
const currentId = ref("");
const userStore = useUserStore();
const maxInvoiceAmount = ref(0); // å‘票金额最大值
src/views/salesManagement/salesLedger/index.vue
@@ -14,6 +14,10 @@
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
            placeholder="请选择" clearable @change="changeDaterange" />
        </el-form-item>
        <el-form-item label="产品大类:">
          <el-input v-model="searchForm.productCategory" placeholder="请输入" clearable prefix-icon="Search"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
        </el-form-item>
@@ -26,80 +30,27 @@
          <el-button type="primary" @click="openForm('add')">
            æ–°å¢žå°è´¦
          </el-button>
          <el-button type="primary" plain @click="handleImport">导入</el-button>
          <el-button @click="handleOut">导出</el-button>
          <el-button type="danger" plain @click="handleDelete">删除</el-button>
          <el-button type="primary" plain @click="handlePrint">打印</el-button>
        </div>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" fixed="left"/>
        <el-table-column type="expand" width="60" fixed="left">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table :data="props.row.children" border show-summary :summary-method="(param) => summarizeChildrenTable(param, props.row)">
              <el-table-column align="center" label="序号" type="index"/>
            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
              <el-table-column align="center" label="序号" type="index" width="60" />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
                            <el-table-column label="产品状态"
                                                             width="100px"
                                                             align="center">
                <template #default="scope">
                                    <el-tag v-if="scope.row.approveStatus === 1 && (!scope.row.shippingDate || !scope.row.shippingCarNumber)"
                                                    type="success">充足</el-tag>
                                    <el-tag v-else-if="scope.row.approveStatus === 0 && (scope.row.shippingDate || scope.row.shippingCarNumber)"
                                                    type="success">已出库</el-tag>
                                    <el-tag v-else type="danger">不足</el-tag>
                </template>
              </el-table-column>
                            <el-table-column label="发货状态" width="140" align="center">
                                <template #default="scope">
                                    <el-tag :type="getShippingStatusType(scope.row)" size="small">
                                        {{ getShippingStatusText(scope.row) }}
                                    </el-tag>
                                </template>
                            </el-table-column>
                            <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip />
                            <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip />
              <el-table-column label="发货车牌" minWidth="100px" align="center">
                <template #default="scope">
                  <div>
                    <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
                    <el-tag v-else type="info">-</el-tag>
                  </div>
                </template>
              </el-table-column>
                            <el-table-column label="发货日期"
                                                             minWidth="100px"
                                                             align="center">
                <template #default="scope">
                  <div>
                    <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
                                        <el-tag v-else
                                                        type="info">-</el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="sensitiveAmountFormatter" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="sensitiveAmountFormatter" />
            <!--操作-->
              <el-table-column Width="60px" label="操作" align="center">
                <template #default="scope">
                  <el-button
                    link
                    type="primary"
                    :disabled="!canShip(scope.row)"
                    @click="openDeliveryForm(scope.row)">
                    å‘è´§
                  </el-button>
                </template>
              </el-table-column>
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
            </el-table>
          </template>
        </el-table-column>
@@ -107,35 +58,63 @@
        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
        <el-table-column label="审批状态" prop="approvalStatus" width="100" align="center" show-overflow-tooltip>
          <template #default="scope">
            <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)" size="small">
              {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip />
        <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" width="130" align="center">
        <el-table-column label="发货状态" prop="shippingStatus" width="140" align="center" show-overflow-tooltip />
        <el-table-column label="发货日期" prop="shippingDate" width="140" align="center" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="140" align="center">
          <template #default="scope">
            <el-button link type="primary" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">编辑</el-button>
            <el-button link type="primary" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button
              link
              type="primary"
              size="small"
              @click="openForm('edit', scope.row)"
              :disabled="scope.row.approvalStatus === 3 && scope.row.entryPerson !== userStore.id"
            >编辑</el-button>
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button
              link
              type="primary"
              size="small"
              @click="openDeliveryForm(scope.row)"
              :disabled="scope.row.shippingStatus === '已发货'"
            >
              å‘è´§
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'" :width="'70%'"
      :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
    <FormDialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增销售台账页面' : '编辑销售台账页面'"
      :width="'70%'"
      :operation-type="operationType"
      @close="closeDia"
      @confirm="submitForm"
      @cancel="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
                <!-- æŠ¥ä»·å•导入入口:放在表单顶部,选择后反显客户/业务员等 -->
                <el-row v-if="operationType === 'add'" style="margin-bottom: 10px;">
                    <el-col :span="24" style="text-align: right;">
                        <el-button type="primary" plain @click="openQuotationDialog">
                            ä»Žé”€å”®æŠ¥ä»·å¯¼å…¥
                        </el-button>
                    </el-col>
                </el-row>
<!--                <el-row v-if="operationType === 'add'" style="margin-bottom: 10px;">-->
<!--                    <el-col :span="24" style="text-align: right;">-->
<!--                        <el-button type="primary" plain @click="openQuotationDialog">-->
<!--                            ä»Žé”€å”®æŠ¥ä»·å¯¼å…¥-->
<!--                        </el-button>-->
<!--                    </el-col>-->
<!--                </el-row>-->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="销售合同号:" prop="salesContractNo">
@@ -144,7 +123,9 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
              <el-select v-model="form.salesman"
                                                 filterable
                         :reserve-keyword="false" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                  :value="item.nickName" />
              </el-select>
@@ -157,220 +138,194 @@
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                    item.customerName+'-'+item.customerType
                  }}
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
                                                            format="YYYY-MM-DD" type="date" placeholder="请选择" clearable :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="付款方式">
                            <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人:" prop="entryPerson">
                            <el-select v-model="form.entryPerson"
                                                 filterable
                                                 default-first-option
                                                 :reserve-keyword="false" placeholder="请选择" clearable @change="changs">
                         default-first-option
                         :reserve-keyword="false" placeholder="请选择" clearable @change="changs">
                                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="录入日期:" prop="entryDate">
                            <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                                                            type="date" placeholder="请选择" clearable />
                        </el-form-item>
                    </el-col>
                </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="交货日期:" prop="entryDate">
              <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                              type="date" placeholder="请选择" clearable />
            <el-form-item label="录入日期:" prop="entryDate">
              <el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
                <el-row>
                    <el-form-item label="产品信息:" prop="entryDate">
                        <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
                        <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
                    </el-form-item>
                </el-row>
                <el-table :data="productData" border @selection-change="productSelected" show-summary
                                    :summary-method="summarizeMainTable">
                    <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
                        :selectable="(row) => !isProductShipped(row)" />
                    <el-table-column align="center" label="序号" type="index" width="60" />
                    <el-table-column label="产品大类" prop="productCategory" />
                    <el-table-column label="规格型号" prop="specificationModel" />
                    <el-table-column label="单位" prop="unit" />
                    <el-table-column label="数量" prop="quantity" />
                    <el-table-column label="税率(%)" prop="taxRate" />
                    <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
                    <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
                    <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
                    <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
                        <template #default="scope">
                            <el-button link type="primary" size="small"
                                :disabled="isProductShipped(scope.row)"
                                @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
                        </template>
                    </el-table-column>
                </el-table>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="备注:" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="附件材料:" prop="salesLedgerFiles">
                            <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                                                 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                                                 :on-success="handleUploadSuccess" :on-remove="handleRemove">
                                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                                <template #tip v-if="operationType !== 'view'">
                                    <div class="el-upload__tip">
                                        æ–‡ä»¶æ ¼å¼æ”¯æŒ
                                        doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                                    </div>
                                </template>
                            </el-upload>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
        </FormDialog>
        <!-- ä»ŽæŠ¥ä»·å•导入(仅审批通过) -->
        <el-dialog
            v-model="quotationDialogVisible"
            title="选择审批通过的销售报价单"
            width="80%"
            :close-on-click-modal="false"
        >
            <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
                <el-input
                    v-model="quotationSearchForm.quotationNo"
                    placeholder="请输入报价单号"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-input
                    v-model="quotationSearchForm.customer"
                    placeholder="请输入客户名称"
                    clearable
                    style="max-width: 260px;"
                    @change="fetchQuotationList"
                />
                <el-button type="primary" @click="fetchQuotationList">搜索</el-button>
                <el-button @click="resetQuotationSearch">重置</el-button>
            </div>
            <el-table
                :data="quotationList"
                border
                stripe
                v-loading="quotationLoading"
                height="420px"
            >
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column prop="quotationNo" label="报价单号" width="180" show-overflow-tooltip />
                <el-table-column prop="customer" label="客户名称" min-width="220" show-overflow-tooltip />
                <el-table-column prop="salesperson" label="业务员" width="120" show-overflow-tooltip />
                <el-table-column prop="quotationDate" label="报价日期" width="140" />
                <el-table-column prop="status" label="审批状态" width="120" align="center" />
                <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right">
                    <template #default="scope">
                        {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
                    </template>
                </el-table-column>
                <el-table-column fixed="right" label="操作" width="120" align="center">
                    <template #default="scope">
                        <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button>
                    </template>
                </el-table-column>
            </el-table>
            <pagination
                v-show="quotationPage.total > 0"
                :total="quotationPage.total"
                layout="total, sizes, prev, pager, next, jumper"
                :page="quotationPage.current"
                :limit="quotationPage.size"
                @pagination="quotationPaginationChange"
            />
            <template #footer>
                <el-button @click="quotationDialogVisible = false">关闭</el-button>
            </template>
        </el-dialog>
        <FormDialog
            v-model="productFormVisible"
            :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
            :width="'40%'"
            :operation-type="productOperationType"
            @close="closeProductDia"
            @confirm="submitProduct"
            @cancel="closeProductDia">
            <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="产品大类:" prop="productCategory">
                            <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable filterable check-strictly
                                            @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="规格型号:" prop="productModelId">
                            <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel" filterable>
                                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="单位:" prop="unit">
                            <el-input v-model="productForm.unit" placeholder="请输入" clearable />
                        </el-form-item>
                    </el-col>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
                  <span>审批人选择:</span>
                  <el-button v-if="operationType !== 'view'" type="primary" size="small" @click="addApproverNode" icon="Plus">新增节点</el-button>
                </div>
              </template>
              <div class="approver-nodes-container">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  class="approver-node-item"
                >
                  <div class="approver-node-header">
                    <span class="approver-node-label">审批节点 {{ index + 1 }}</span>
                    <el-button
                      v-if="approverNodes.length > 1 && operationType !== 'view'"
                      type="danger"
                      size="small"
                      text
                      @click="removeApproverNode(index)"
                      icon="Delete"
                    >删除</el-button>
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="请选择审批人"
                    filterable
                    style="width: 100%;"
                    :disabled="operationType === 'view'"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  </el-select>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
            <el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >删除</el-button>
          </el-form-item>
        </el-row>
        <el-table :data="productData" border @selection-change="productSelected" show-summary
          :summary-method="summarizeMainTable">
          <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
          <el-table-column align="center" label="序号" type="index" width="60" />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" />
          <el-table-column label="数量" prop="quantity" />
          <el-table-column label="税率(%)" prop="taxRate" />
          <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
          <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
            <template #default="scope">
              <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
            </template>
          </el-table-column>
        </el-table>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="备注·:" prop="remark">
              <el-input v-model="form.remark" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:" prop="remark">
              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                :on-success="handleUploadSuccess" :on-remove="handleRemove">
                <el-button type="primary" v-if="operationType !== 'view'">上传</el-button>
                <template #tip v-if="operationType !== 'view'">
                  <div class="el-upload__tip">
                    æ–‡ä»¶æ ¼å¼æ”¯æŒ
                    doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
                  </div>
                </template>
              </el-upload>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </FormDialog>
    <el-dialog v-model="productFormVisible" :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" width="40%"
      @close="closeProductDia">
      <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:" prop="productCategory">
              <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable>
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
              </el-select> -->
              <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly
                @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单位:" prop="unit">
              <el-input v-model="productForm.unit" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="税率(%):" prop="taxRate">
                            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate">
                                <el-option label="1" value="1" />
                                <el-option label="6" value="6" />
                                <el-option label="13" value="13" />
                            </el-select>
<!--                            <el-select v-model="productForm.taxRate" placeholder="请选择" clearable @change="calculateFromTaxRate">-->
<!--                                <el-option label="1" value="1" />-->
<!--                                <el-option label="6" value="6" />-->
<!--                                <el-option label="13" value="13" />-->
<!--                            </el-select>-->
              <el-input-number :step="1" :min="0" v-model="productForm.taxRate" style="width: 100%"
                               placeholder="请输入" clearable @change="calculateFromTaxRate" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
                            <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税/不含税单价(元):" prop="taxInclusiveUnitPrice">
              <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
                                                             :precision="2"
                                                             placeholder="请输入" clearable @change="calculateFromUnitPrice" />
                        </el-form-item>
                    </el-col>
            </el-form-item>
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
@@ -378,72 +333,37 @@
                                                                @change="calculateFromQuantity" style="width: 100%" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
                            <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
                            <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="发票类型:" prop="invoiceType">
                            <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
                                <el-option label="增普票" value="增普票" />
                                <el-option label="增专票" value="增专票" />
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
        </FormDialog>
        <!-- å¯¼å…¥å¼¹çª— -->
        <FormDialog
            v-model="importUpload.open"
            :title="importUpload.title"
            :width="'600px'"
            @close="importUpload.open = false"
            @confirm="submitImportFile"
            @cancel="importUpload.open = false"
        >
            <el-upload
                ref="importUploadRef"
                :limit="1"
                accept=".xlsx,.xls"
                :action="importUpload.url"
                :headers="importUpload.headers"
                :before-upload="importUpload.beforeUpload"
                :on-success="importUpload.onSuccess"
                :on-error="importUpload.onError"
                :on-progress="importUpload.onProgress"
                :on-change="importUpload.onChange"
                :auto-upload="false"
                drag
            >
                <i class="el-icon-upload"></i>
                <div class="el-upload__text">
                    å°†æ–‡ä»¶æ‹–到此处,或<em>点击上传</em>
                </div>
                <template #tip>
                    <div class="el-upload__tip">
                        ä»…支持 xls/xlsx,大小不超过 10MB。
                        <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button>
                    </div>
                </template>
            </el-upload>
        </FormDialog>
        <!-- é™„件列表弹窗 -->
        <FileListDialog
            ref="fileListRef"
            v-model="fileListDialogVisible"
            title="附件列表"
        />
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
              <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
              <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="发票类型:" prop="invoiceType">
              <el-select v-model="productForm.invoiceType" placeholder="请选择" clearable>
                <el-option label="增普票" value="增普票" />
                <el-option label="增专票" value="增专票" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
        <!-- æ‰“印预览弹窗 -->
        <el-dialog
            v-model="printPreviewVisible"
@@ -467,9 +387,10 @@
                    <div v-for="(item, index) in printData" :key="index" class="print-page">
                        <div class="delivery-note">
                            <div class="header">
                                <div class="company-name">青海湟水峡农业发展有限公司</div>
                                <div class="document-title">零售发货单</div>
                            </div>
                            <div class="info-section">
                                <div class="info-row">
                                    <div>
@@ -484,7 +405,7 @@
                                <div class="info-row">
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName }}</span>
                                        <span class="value">{{ item.customerName || '张爱有' }}</span>
                                    </div>
                                    <span class="label">单号:</span>
                                    <span class="value">{{ item.salesContractNo }}</span>
@@ -564,7 +485,7 @@
        <el-dialog
            v-model="deliveryFormVisible"
            title="发货信息"
        width="40%"
            width="40%"
            @close="closeDeliveryDia"
        >
            <el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
@@ -575,6 +496,7 @@
                                v-model="deliveryForm.type"
                                placeholder="请选择发货类型"
                                style="width: 100%"
                                @change="handleShippingTypeChange"
                            >
                                <el-option label="货车" value="货车" />
                                <el-option label="快递" value="快递" />
@@ -582,50 +504,81 @@
                        </el-form-item>
                    </el-col>
                </el-row>
        <!-- å®¡æ‰¹äººé€‰æ‹©ï¼ˆä»¿ååŒå®¡æ‰¹é‡Œçš„审批人节点选择) -->
        <el-row>
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 20px; text-align: center; margin-bottom: 10px;"
                >
                  <div>
                    <span>审批人</span>
                    â†’
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="选择人员"
                    filterable
                    style="width: 140px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                    >删除</el-button>
                  </div>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货日期:" prop="shippingDate">
                            <el-date-picker
                                style="width: 100%"
                                v-model="deliveryForm.shippingDate"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                type="date"
                                placeholder="请选择发货日期"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="24" v-if="deliveryForm.type === '货车'">
                        <el-form-item label="发货车牌号:" prop="shippingCarNumber">
                            <el-input
                                v-model="deliveryForm.shippingCarNumber"
                                placeholder="请输入发货车牌号"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="24" v-else>
                        <el-form-item label="快递公司:" prop="expressCompany">
                            <el-input
                                v-model="deliveryForm.expressCompany"
                                placeholder="请输入快递公司"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30" v-if="deliveryForm.type === '快递'">
                    <el-col :span="24">
                        <el-form-item label="快递单号:" prop="expressNumber">
                            <el-input
                                v-model="deliveryForm.expressNumber"
                                placeholder="请输入快递单号"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                    <el-row :gutter="30">
                        <el-col :span="24">
                            <el-form-item label="发货图片:">
                                <el-upload
                                    v-model:file-list="deliveryFileList"
                                    :action="upload.url"
                                    multiple
                                    ref="deliveryFileUpload"
                                    auto-upload
                                    :headers="upload.headers"
                                    :data="{ type: 9 }"
                                    :before-upload="handleDeliveryBeforeUpload"
                                    :on-error="handleDeliveryUploadError"
                                    :on-success="handleDeliveryUploadSuccess"
                                    :on-remove="handleDeliveryRemove"
                                    list-type="picture-card"
                                    :limit="9"
                                    accept="image/png,image/jpeg,image/jpg"
                                >
                                    <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
                                    <template #tip>
                                        <div class="el-upload__tip">
                                            æ”¯æŒ jpg、jpeg、png æ ¼å¼ï¼Œæœ€å¤šä¸Šä¼  9 å¼ ï¼Œå•张大小不超过 10MB
                                        </div>
                                    </template>
                                </el-upload>
                            </el-form-item>
                        </el-col>
                    </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
@@ -634,7 +587,64 @@
                </div>
            </template>
        </el-dialog>
    </div>
        <!-- é”€å”®æŠ¥ä»·å•选择对话框 -->
        <el-dialog
            v-model="quotationDialogVisible"
            title="选择销售报价单"
            width="80%"
            :close-on-click-modal="false"
            @close="resetQuotationSearch"
        >
            <div class="search_form" style="margin-bottom: 15px;">
                <el-form :model="quotationSearchForm" :inline="true">
                    <el-form-item label="报价单号:">
                        <el-input v-model="quotationSearchForm.quotationNo" placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="客户:">
                        <el-input v-model="quotationSearchForm.customer" placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item>
                        <el-button type="primary" @click="fetchQuotationList">搜索</el-button>
                        <el-button @click="resetQuotationSearch">重置</el-button>
                    </el-form-item>
                </el-form>
            </div>
            <el-table
                :data="quotationList"
                border
                v-loading="quotationLoading"
                height="400"
                style="width: 100%"
            >
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column label="报价单号" prop="quotationNo"show-overflow-tooltip />
                <el-table-column label="客户" prop="customer" show-overflow-tooltip />
                <el-table-column label="业务员" prop="salesperson" show-overflow-tooltip />
                <el-table-column label="报价日期" prop="quotationDate" show-overflow-tooltip />
                <el-table-column label="状态" prop="status" width="100" align="center">
                    <template #default="scope">
                        <el-tag v-if="scope.row.status === '通过'" type="success" size="small">已通过</el-tag>
                        <el-tag v-else type="info" size="small">{{ scope.row.status }}</el-tag>
                    </template>
                </el-table-column>
                <el-table-column label="操作" width="100" align="center" fixed="right">
                    <template #default="scope">
                        <el-button link type="primary" size="small" @click="applyQuotation(scope.row)">选择</el-button>
                    </template>
                </el-table-column>
            </el-table>
            <pagination
                v-show="quotationPage.total > 0"
                :total="quotationPage.total"
                layout="total, sizes, prev, pager, next, jumper"
                :page="quotationPage.current"
                :limit="quotationPage.size"
                style="margin-bottom: 15px;"
                @pagination="quotationPaginationChange"
            />
        </el-dialog>
    <FileList ref="fileListRef" />
  </div>
</template>
<script setup>
@@ -643,10 +653,10 @@
import {onMounted, ref, getCurrentInstance} from "vue";
import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
import { ElMessageBox, ElMessage } from "element-plus";
import { UploadFilled, Download } from "@element-plus/icons-vue";
import { UploadFilled, Plus } from "@element-plus/icons-vue";
import useUserStore from "@/store/modules/user";
import { userListNoPage } from "@/api/system/user.js";
import FileListDialog from '@/components/Dialog/FileListDialog.vue';
import FileList from '@/views/salesManagement/salesLedger/fileList.vue';
import FormDialog from '@/components/Dialog/FormDialog.vue';
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import {
@@ -660,6 +670,7 @@
    delProduct,
    delLedgerFile, getProductInventory,
} from "@/api/salesManagement/salesLedger.js";
import { getQuotationDetail } from "@/api/salesManagement/salesQuotation.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
@@ -678,43 +689,53 @@
const modelOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
    current: 1,
    size: 100,
  current: 1,
  size: 100,
});
const total = ref(0);
const fileList = ref([]);
const deliveryFileList = ref([]);
// å®¡æ‰¹äººèŠ‚ç‚¹ï¼ˆä»¿é‡‡è´­å°è´¦å®¡æ‰¹äººï¼‰
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
  approverNodes.value.splice(index, 1);
};
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
    searchForm: {
        customerName: "", // å®¢æˆ·åç§°
        salesContractNo: "", // é”€å”®åˆåŒç¼–号
        entryDate: null, // å½•入日期
        entryDateStart: undefined,
        entryDateEnd: undefined,
    },
    form: {
        salesContractNo: "",
        salesman: "",
        customerId: "",
        entryPerson: "",
        entryDate: "",
    deliveryDate: "",
        maintenanceTime: "",
        productData: [],
        executionDate: "",
    hasProductionRecord: false,
    },
    rules: {
        salesman: [{ required: true, message: "请选择", trigger: "change" }],
        customerId: [{ required: true, message: "请选择", trigger: "change" }],
        entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
        entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    deliveryDate: [{ required: true, message: "请选择", trigger: "change" }],
        executionDate: [{ required: true, message: "请选择", trigger: "change" }],
    },
  searchForm: {
    customerName: "", // å®¢æˆ·åç§°
    salesContractNo: "", // é”€å”®åˆåŒç¼–号
    productCategory: "", // äº§å“å¤§ç±»
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  form: {
    salesContractNo: "",
    salesman: "",
    customerId: "",
    entryPerson: "",
    entryDate: "",
    maintenanceTime: "",
    productData: [],
    executionDate: "",
    paymentMethod: "",
  },
  rules: {
    salesman: [{ required: true, message: "请选择", trigger: "change" }],
    customerId: [{ required: true, message: "请选择", trigger: "change" }],
    entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
    entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    executionDate: [{ required: true, message: "请选择", trigger: "change" }],
    },
});
const { form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
@@ -723,46 +744,46 @@
const productOperationType = ref("");
const currentId = ref("");
const productFormData = reactive({
    productForm: {
        productCategory: "",
        specificationModel: "",
        unit: "",
        quantity: "",
        taxInclusiveUnitPrice: "",
        taxRate: "",
        taxInclusiveTotalPrice: "",
        taxExclusiveTotalPrice: "",
        invoiceType: "",
    },
    productRules: {
        productCategory: [{ required: true, message: "请选择", trigger: "change" }],
  productForm: {
    productCategory: "",
    specificationModel: "",
    unit: "",
    quantity: "",
    taxInclusiveUnitPrice: "",
    taxRate: "",
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
  },
  productRules: {
    productCategory: [{ required: true, message: "请选择", trigger: "change" }],
        productModelId: [{ required: true, message: "请选择", trigger: "change" }],
        specificationModel: [
            { required: true, message: "请选择", trigger: "change" },
        ],
        unit: [{ required: true, message: "请输入", trigger: "blur" }],
        quantity: [{ required: true, message: "请输入", trigger: "blur" }],
        taxInclusiveUnitPrice: [
            { required: true, message: "请输入", trigger: "blur" },
        ],
        taxRate: [{ required: true, message: "请选择", trigger: "change" }],
        taxInclusiveTotalPrice: [
            { required: true, message: "请输入", trigger: "blur" },
        ],
        taxExclusiveTotalPrice: [
            { required: true, message: "请输入", trigger: "blur" },
        ],
        invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
    },
    specificationModel: [
      { required: true, message: "请选择", trigger: "change" },
    ],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
    taxInclusiveUnitPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    // taxRate: [{ required: true, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxExclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    // invoiceType: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const { productForm, productRules } = toRefs(productFormData);
// é˜²æ­¢å¾ªçŽ¯è®¡ç®—çš„æ ‡å¿—
const isCalculating = ref(false);
const upload = reactive({
    // ä¸Šä¼ çš„地址
    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
    // è®¾ç½®ä¸Šä¼ çš„请求头部
    headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// æ‰“印相关
const printPreviewVisible = ref(false);
@@ -790,344 +811,280 @@
const deliveryFormData = reactive({
  deliveryForm: {
    type: "货车", // è´§è½¦, å¿«é€’
    shippingDate: "",
    shippingCarNumber: "",
    expressCompany: "",
    expressNumber: "", // å¿«é€’单号
    shippingImages: "", // å‘货图片,多个用逗号分隔
  },
  deliveryRules: {
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ]
    ],
    shippingDate: [
      { required: true, message: "请选择发货日期", trigger: "change" }
    ],
    shippingCarNumber: [
      { validator: (_, value, callback) => validateShippingCarNumber(value, callback), trigger: "blur" }
    ],
    expressCompany: [
      { validator: (_, value, callback) => validateExpressCompany(value, callback), trigger: "blur" }
    ],
  },
});
const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
// å‘货审批人节点(仿协同审批 infoFormDia.vue)
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
  approverNodes.value.splice(index, 1);
};
// å¯¼å…¥ç›¸å…³
const importUploadRef = ref(null);
const importUpload = reactive({
    title: "导入销售台账",
    open: false,
    url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: (file) => {
        const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
        const isLt10M = file.size / 1024 / 1024 < 10;
        if (!isExcel) {
            proxy.$modal.msgError("上传文件只能是 xlsx/xls æ ¼å¼!");
            return false;
        }
        if (!isLt10M) {
            proxy.$modal.msgError("上传文件大小不能超过 10MB!");
            return false;
        }
        return true;
    },
    onChange: (file, fileList) => {
        console.log('文件状态改变', file, fileList);
    },
    onProgress: (event, file, fileList) => {
        console.log('上传中...', event.percent);
    },
    onSuccess: (response, file, fileList) => {
        console.log('上传成功', response, file, fileList);
        importUpload.isUploading = false;
        if (response.code === 200) {
            proxy.$modal.msgSuccess("导入成功");
            importUpload.open = false;
            if (importUploadRef.value) {
                importUploadRef.value.clearFiles();
            }
            getList();
        } else {
            proxy.$modal.msgError(response.msg || "导入失败");
        }
    },
    onError: (error, file, fileList) => {
        console.error('上传失败', error, file, fileList);
        importUpload.isUploading = false;
        proxy.$modal.msgError("导入失败,请重试");
    },
});
const changeDaterange = (value) => {
    if (value) {
        searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
        searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
    } else {
        searchForm.entryDateStart = undefined;
        searchForm.entryDateEnd = undefined;
    }
    handleQuery();
  if (value) {
    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    searchForm.entryDateStart = undefined;
    searchForm.entryDateEnd = undefined;
  }
  handleQuery();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
    // åªæœ‰åœ¨ç‚¹å‡»æœç´¢æŒ‰é’®æ—¶æ‰é‡ç½®é¡µç åˆ°ç¬¬ä¸€é¡µ
    // é¿å…è¡¨å•字段change事件干扰分页
    if (arguments.length === 0) {
        page.current = 1;
    }
  // åªæœ‰åœ¨ç‚¹å‡»æœç´¢æŒ‰é’®æ—¶æ‰é‡ç½®é¡µç åˆ°ç¬¬ä¸€é¡µ
  // é¿å…è¡¨å•字段change事件干扰分页
  if (arguments.length === 0) {
    page.current = 1;
  }
    expandedRowKeys.value = [];
    getList();
  getList();
};
const paginationChange = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
    tableLoading.value = true;
    const { entryDate, ...rest } = searchForm;
    // å°†èŒƒå›´æ—¥æœŸå­—段传递给后端
    const params = { ...rest, ...page };
    // ç§»é™¤å½•入日期的默认值设置,只保留范围日期字段
    delete params.entryDate;
    return ledgerListPage(params)
        .then((res) => {
            tableLoading.value = false;
            tableData.value = res.records;
            tableData.value.map((item) => {
                item.children = [];
            });
            total.value = res.total;
            return res;
        })
        .catch(() => {
            tableLoading.value = false;
        });
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  ledgerListPage({ ...rest, ...page })
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.records;
      tableData.value.map((item) => {
        item.children = [];
      });
      total.value = res.total;
    })
    .catch(() => {
      tableLoading.value = false;
    });
};
// èŽ·å–äº§å“å¤§ç±»tree数据
const getProductOptions = () => {
    // è¿”回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then((res) => {
        productOptions.value = convertIdToValue(res);
        return productOptions.value;
    });
  // è¿”回 Promise,便于在编辑产品时等待加载完成
  return productTreeList().then((res) => {
    // å…¼å®¹æŽ¥å£è¿”回 { data: [] } æˆ–直接返回数组
    const list = Array.isArray(res) ? res : (res?.data ?? []);
    productOptions.value = convertIdToValue(list);
    return productOptions.value;
  });
};
const formattedNumber = (row, column, cellValue) => {
    if (cellValue === undefined || cellValue === null || cellValue === "") {
        return "0.00";
    }
    return parseFloat(cellValue).toFixed(2);
  return parseFloat(cellValue).toFixed(2);
};
const findLedgerRecordByRow = (row) => {
    if (!row) return null;
    if (
        row.maintainer !== undefined ||
        row.maintainerName !== undefined ||
        row.entryPerson !== undefined ||
        row.entryPersonName !== undefined
    ) {
        return row;
    }
    if (row.salesLedgerId !== undefined && row.salesLedgerId !== null) {
        return tableData.value.find((item) => String(item.id) === String(row.salesLedgerId)) || null;
    }
    return null;
// è®¢å•审批状态显示文本
const approvalStatusText = {
  1: "待审核",
  2: "审批中",
  3: "审批通过",
  4: "审批失败",
};
const isCurrentUserMaintainer = (row) => {
    const ledgerRecord = findLedgerRecordByRow(row);
    if (!ledgerRecord) return true;
    const currentUserId = String(userStore.id ?? "");
    const currentNickName = String(userStore.nickName ?? "").trim();
    const maintainerId = ledgerRecord.maintainerId ?? ledgerRecord.entryPerson;
    const maintainerName =
        ledgerRecord.maintainerName ?? ledgerRecord.maintainer ?? ledgerRecord.entryPersonName;
    if (maintainerId !== undefined && maintainerId !== null && String(maintainerId) !== "") {
        return String(maintainerId) === currentUserId;
    }
    if (maintainerName !== undefined && maintainerName !== null && String(maintainerName).trim() !== "") {
        return String(maintainerName).trim() === currentNickName;
    }
    return true;
};
const canEditLedger = (row) => isCurrentUserMaintainer(row);
const canDeleteLedger = (row) => isCurrentUserMaintainer(row);
const sensitiveAmountFormatter = (row, column, cellValue) => {
    if (!isCurrentUserMaintainer(row)) {
        return "*****";
    }
    return formattedNumber(row, column, cellValue);
// èŽ·å–å®¡æ‰¹çŠ¶æ€æ ‡ç­¾ç±»åž‹
const getApprovalStatusType = (status) => {
  const typeMap = {
    1: "info",      // å¾…审核 - ç°è‰²
    2: "warning",   // å®¡æ‰¹ä¸­ - æ©™è‰²
    3: "success",   // å®¡æ‰¹é€šè¿‡ - ç»¿è‰²
    4: "danger",    // å®¡æ‰¹å¤±è´¥ - çº¢è‰²
  };
  return typeMap[status] || "info";
};
// èŽ·å–tree子数据
const getModels = (value) => {
    productForm.value.productCategory = findNodeById(productOptions.value, value);
    modelList({ id: value }).then((res) => {
        modelOptions.value = res;
    });
  productForm.value.productCategory = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  });
};
const getProductModel = (value) => {
    const index = modelOptions.value.findIndex((item) => item.id === value);
    if (index !== -1) {
        productForm.value.specificationModel = modelOptions.value[index].model;
        productForm.value.unit = modelOptions.value[index].unit;
    } else {
        productForm.value.specificationModel = null;
        productForm.value.unit = null;
    }
  console.log("value", value);
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
    fetchQuotationPrice();
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
  }
};
const findNodeById = (nodes, productId) => {
    for (let i = 0; i < nodes.length; i++) {
        if (nodes[i].value === productId) {
            return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
        }
        if (nodes[i].children && nodes[i].children.length > 0) {
            const foundNode = findNodeById(nodes[i].children, productId);
            if (foundNode) {
                return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
            }
        }
    }
    return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
// æ ¹æ®æŠ¥ä»·æŽ¥å£å›žå¡«å•ä»·
const fetchQuotationPrice = async () => {
  // éœ€è¦å®¢æˆ·ç±»åž‹ã€äº§å“åç§°ã€è§„æ ¼
  const customer = customerOption.value.find((c) => c.id === form.value.customerId);
  const customerType = customer?.customerType || customer?.type;
  const productName = productForm.value.productCategory;
  const specification = productForm.value.specificationModel;
  try {
    const { data } = await getQuotationDetail({
      type: customerType,
      productName,
      specification,
    });
    const price = data;
    if (price !== null && price !== undefined) {
      productForm.value.taxInclusiveUnitPrice = Number(price);
      mathNum(); // é‡æ–°è®¡ç®—总价
    }
  } catch (error) {
    console.error("获取报价单价失败", error);
  }
};
function convertIdToValue(data) {
    return data.map((item) => {
        const { id, children, ...rest } = item;
        const newItem = {
            ...rest,
            value: id, // å°† id æ”¹ä¸º value
        };
        if (children && children.length > 0) {
            newItem.children = convertIdToValue(children);
        }
        return newItem;
    });
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// æ ¹æ®åç§°åæŸ¥äº§å“å¤§ç±» id,便于仅存名称时的反显
function findNodeIdByLabel(nodes, label) {
    if (!label) return null;
    for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
        if (node.label === label) return node.value;
        if (node.children && node.children.length > 0) {
            const found = findNodeIdByLabel(node.children, label);
            if (found !== null && found !== undefined) return found;
        }
    }
    return null;
  if (!label) return null;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    if (node.label === label) return node.value;
    if (node.children && node.children.length > 0) {
      const found = findNodeIdByLabel(node.children, label);
      if (found !== null && found !== undefined) return found;
    }
  }
  return null;
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
    // è¿‡æ»¤æŽ‰å­æ•°æ®
    selectedRows.value = selection.filter((item) => item.children !== undefined);
    console.log("selection", selectedRows.value);
  // è¿‡æ»¤æŽ‰å­æ•°æ®
  selectedRows.value = selection.filter((item) => item.children !== undefined);
  console.log("selection", selectedRows.value);
};
const productSelected = (selectedRows) => {
    productSelectedRows.value = selectedRows;
  productSelectedRows.value = selectedRows;
};
const expandedRowKeys = ref([]);
// å±•开行
const expandChange = (row, expandedRows) => {
    if (expandedRows.length > 0) {
        expandedRowKeys.value = [];
        try {
            productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
                const index = tableData.value.findIndex((item) => item.id === row.id);
                if (index > -1) {
                    tableData.value[index].children = res.data;
                }
                expandedRowKeys.value.push(row.id);
            });
        } catch (error) {
            console.log(error);
        }
    } else {
        expandedRowKeys.value = [];
    }
};
// æ·»åŠ è¡¨è¡Œç±»åæ–¹æ³•
const tableRowClassName = ({ row }) => {
  if (!row.deliveryDate) return '';
  if (row.isFh) return '';
  const diff = row.deliveryDaysDiff;
  if (diff === 15) {
    return 'yellow';
  } else if (diff === 10) {
    return 'pink';
  } else if (diff === 2) {
    return 'purple';
  } else if (diff < 2) {
    return 'red';
  if (expandedRows.length > 0) {
    expandedRowKeys.value = [];
    try {
      productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
        const index = tableData.value.findIndex((item) => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
        expandedRowKeys.value.push(row.id);
      });
    } catch (error) {
      console.log(error);
    }
  } else {
    expandedRowKeys.value = [];
  }
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
    return proxy.summarizeTable(param, [
        "contractAmount",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
    ]);
  return proxy.summarizeTable(param, [
    "contractAmount",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param, parentRow) => {
    if (!isCurrentUserMaintainer(parentRow)) {
        const { columns } = param;
        return columns.map((column, index) => {
            if (index === 0) {
                return "合计";
            }
            if (["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"].includes(column.property)) {
                return "*****";
            }
            return "";
        });
    }
    return proxy.summarizeTable(param, [
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
    ]);
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
  ]);
};
// æ‰“开弹框
const openForm = async (type, row) => {
    if (type === "edit" && row && !canEditLedger(row)) {
        proxy.$modal.msgWarning("当前系统登录人不是维护人,不能编辑数据");
        return;
    }
    operationType.value = type;
    form.value = {};
    productData.value = [];
    selectedQuotation.value = null;
    let userLists = await userListNoPage();
    userList.value = userLists.data;
  operationType.value = type;
  form.value = {};
  productData.value = [];
  let userLists = await userListNoPage();
  userList.value = userLists.data;
    listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
        customerOption.value = res.data.records;
    });
    form.value.entryPerson = userStore.id;
    if (type === "add") {
        // æ–°å¢žæ—¶è®¾ç½®å½•入日期为当天
        form.value.entryDate = getCurrentDate();
        // ç­¾è®¢æ—¥æœŸé»˜è®¤ä¸ºå½“天
        form.value.executionDate = getCurrentDate();
    } else {
        currentId.value = row.id;
        getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
            form.value = { ...res };
            form.value.entryPerson = Number(res.entryPerson);
            productData.value = form.value.productData;
            fileList.value = form.value.salesLedgerFiles;
        });
    }
    // let userAll = await userStore.getInfo()
    // userList.value.forEach(element => {
    //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
    //     form.value.entryPerson = userAll.user.userId // è®¾ç½®é»˜è®¤ä¸šåŠ¡å‘˜ä¸ºå½“å‰ç”¨æˆ·
    //   }
    // });
    form.value.entryDate = getCurrentDate(); // è®¾ç½®é»˜è®¤å½•入日期为当前日期
    dialogFormVisible.value = true;
  form.value.entryPerson = userStore.id;
  if (type === "add") {
    // æ–°å¢žæ—¶è®¾ç½®å½•入日期为当天
    form.value.entryDate = getCurrentDate();
    // ç­¾è®¢æ—¥æœŸé»˜è®¤ä¸ºå½“天
    form.value.executionDate = getCurrentDate();
    // æ–°å¢žæ—¶é‡ç½®å®¡æ‰¹äººèŠ‚ç‚¹
    approverNodes.value = [{ id: 1, userId: null }];
    nextApproverId = 2;
  } else {
    currentId.value = row.id;
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
      form.value = { ...res };
      form.value.entryPerson = Number(res.entryPerson);
      productData.value = form.value.productData;
      fileList.value = form.value.salesLedgerFiles;
      // å›žæ˜¾å®¡æ‰¹äººèŠ‚ç‚¹
      if (res.approveUserIds) {
        const userIds = res.approveUserIds.split(",").filter(id => id);
        approverNodes.value = userIds.map((userId, index) => ({
          id: index + 1,
          userId: Number(userId)
        }));
        nextApproverId = userIds.length + 1;
      } else {
        approverNodes.value = [{ id: 1, userId: null }];
        nextApproverId = 2;
      }
    });
  }
  // let userAll = await userStore.getInfo()
  // userList.value.forEach(element => {
  //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
  //     form.value.entryPerson = userAll.user.userId // è®¾ç½®é»˜è®¤ä¸šåŠ¡å‘˜ä¸ºå½“å‰ç”¨æˆ·
  //   }
  // });
  form.value.entryDate = getCurrentDate(); // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  dialogFormVisible.value = true;
};
// æ‰“开报价单选择弹窗(仅审批通过)
@@ -1227,301 +1184,287 @@
    quotationDialogVisible.value = false;
};
function changs(val) {
    console.log(val);
  console.log(val);
}
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
    // æ ¡æ£€æ–‡ä»¶å¤§å°
    // if (file.size > 1024 * 1024 * 10) {
    //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
    //   return false;
    // }
    proxy.$modal.loading("正在上传文件,请稍候...");
    return true;
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  // if (file.size > 1024 * 1024 * 10) {
  //   proxy.$modal.msgError("上传文件大小不能超过10MB!");
  //   return false;
  // }
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
// ä¸Šä¼ å¤±è´¥
function handleUploadError(err) {
    proxy.$modal.msgError("上传文件失败");
    proxy.$modal.closeLoading();
  proxy.$modal.msgError("上传文件失败");
  proxy.$modal.closeLoading();
}
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
    proxy.$modal.closeLoading();
    if (res.code === 200) {
        file.tempId = res.data.tempId;
        proxy.$modal.msgSuccess("上传成功");
    } else {
        proxy.$modal.msgError(res.msg);
        proxy.$refs.fileUpload.handleRemove(file);
    }
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
    if (operationType.value === "edit") {
        let ids = [];
        ids.push(file.id);
        delLedgerFile(ids).then((res) => {
            proxy.$modal.msgSuccess("删除成功");
        });
    }
  if (operationType.value === "edit") {
    let ids = [];
    ids.push(file.id);
    delLedgerFile(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
    });
  }
}
// å‘货图片上传前校检
function handleDeliveryBeforeUpload(file) {
  // æ ¡æ£€æ–‡ä»¶ç±»åž‹
  const isImage = file.type === 'image/png' || file.type === 'image/jpeg' || file.type === 'image/jpg';
  if (!isImage) {
    proxy.$modal.msgError("只能上传 jpg、jpeg、png æ ¼å¼çš„图片!");
    return false;
  }
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) {
    proxy.$modal.msgError("上传图片大小不能超过 10MB!");
    return false;
  }
  proxy.$modal.loading("正在上传图片,请稍候...");
  return true;
}
// å‘货图片上传失败
function handleDeliveryUploadError(err) {
  proxy.$modal.msgError("上传图片失败");
  proxy.$modal.closeLoading();
}
// å‘货图片上传成功回调
function handleDeliveryUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  if (res.code === 200) {
    file.tempId = res.data.tempId;
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.deliveryFileUpload.handleRemove(file);
  }
}
// ç§»é™¤å‘货图片
function handleDeliveryRemove(file) {
  // ä»Žæ–‡ä»¶åˆ—表中移除
  const index = deliveryFileList.value.findIndex(item => item.uid === file.uid);
  if (index > -1) {
    deliveryFileList.value.splice(index, 1);
  }
}
// æäº¤è¡¨å•
const submitForm = () => {
    proxy.$refs["formRef"].validate((valid) => {
        if (valid) {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
            console.log('productData.value--', productData.value)
            if (productData.value !== null && productData.value.length > 0) {
                form.value.productData = proxy.HaveJson(productData.value);
            } else {
                proxy.$modal.msgWarning("请添加产品信息");
                return;
            }
            let tempFileIds = [];
            if (fileList.value !== null && fileList.value.length > 0) {
                tempFileIds = fileList.value.map((item) => item.tempId);
            }
            form.value.tempFileIds = tempFileIds;
            form.value.type = 1;
            addOrUpdateSalesLedger(form.value).then((res) => {
                proxy.$modal.msgSuccess("提交成功");
                closeDia();
                expandedRowKeys.value = [];
                getList();
            });
        }
    });
      if (productData.value !== null && productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
        proxy.$modal.msgWarning("请添加产品信息");
        return;
      }
      // æ ¡éªŒå®¡æ‰¹äººæ˜¯å¦å·²é€‰æ‹©
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
      if (hasEmptyApprover) {
        proxy.$modal.msgWarning("请选择审批人");
        return;
      }
      let tempFileIds = [];
      if (fileList.value !== null && fileList.value.length > 0) {
        tempFileIds = fileList.value.map((item) => item.tempId);
      }
      form.value.tempFileIds = tempFileIds;
      form.value.type = 1;
      // å°†å®¡æ‰¹äººèŠ‚ç‚¹è½¬æ¢ä¸ºé€—å·åˆ†éš”çš„å­—ç¬¦ä¸²
      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
      form.value.approveUserIds = approveUserIds;
      addOrUpdateSalesLedger(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
      });
    }
  });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  // é‡ç½®å®¡æ‰¹äººèŠ‚ç‚¹
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
};
const productIndex = ref(0);
// æ‰“开产品弹框
const openProductForm = async (type, row, index) => {
    // ç¼–辑时检查产品是否已发货或审核通过
    if (type === "edit" && isProductShipped(row)) {
        proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑");
        return;
    }
    productOperationType.value = type;
    productForm.value = {};
    proxy.resetForm("productFormRef");
    if (type === "edit") {
        productForm.value = { ...row };
        productIndex.value = index;
        // ç¼–辑时根据产品大类名称反查 tree èŠ‚ç‚¹ id,并加载规格型号列表
        try {
            const options = productOptions.value && productOptions.value.length > 0
                ? productOptions.value
                : await getProductOptions();
            const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
            if (categoryId) {
                const models = await modelList({ id: categoryId });
                modelOptions.value = models || [];
                // æ ¹æ®å½“前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
                const currentModel = (modelOptions.value || []).find(
                    (m) => m.model === productForm.value.specificationModel
                );
                if (currentModel) {
                    productForm.value.productModelId = currentModel.id;
                }
            }
        } catch (e) {
            // åŠ è½½å¤±è´¥æ—¶ä¿æŒå¯ç¼–è¾‘ï¼Œä¸ä¸­æ–­å¼¹çª—
            console.error("加载产品规格型号失败", e);
        }
    } else {
        getProductOptions()
    }
    productFormVisible.value = true;
  productOperationType.value = type;
  productForm.value = {};
  proxy.resetForm("productFormRef");
  // æ–°å¢žã€ç¼–辑都需先加载产品树,否则 el-tree-select æ— æ•°æ®
  try {
    await getProductOptions();
  } catch (e) {
    console.error("加载产品树失败", e);
  }
  if (type === "edit") {
    productForm.value = { ...row };
    productIndex.value = index;
    // ç¼–辑时根据产品大类名称反查 tree èŠ‚ç‚¹ id,并加载规格型号列表
    try {
      const options = productOptions.value && productOptions.value.length > 0
        ? productOptions.value
        : await getProductOptions();
      const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
      if (categoryId) {
        const models = await modelList({ id: categoryId });
        modelOptions.value = models || [];
        // æ ¹æ®å½“前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
        const currentModel = (modelOptions.value || []).find(
          (m) => m.model === productForm.value.specificationModel
        );
        if (currentModel) {
          productForm.value.productModelId = currentModel.id;
        }
      }
    } catch (e) {
      // åŠ è½½å¤±è´¥æ—¶ä¿æŒå¯ç¼–è¾‘ï¼Œä¸ä¸­æ–­å¼¹çª—
      console.error("加载产品规格型号失败", e);
    }
  }
  productFormVisible.value = true;
  getProductOptions();
};
// æäº¤äº§å“è¡¨å•
const submitProduct = () => {
    proxy.$refs["productFormRef"].validate((valid) => {
        if (valid) {
            if (operationType.value === "edit") {
                submitProductEdit();
            } else {
                if(productOperationType.value === "add"){
                    productData.value.push({ ...productForm.value });
                }else{
                    productData.value[productIndex.value] = { ...productForm.value }
                }
                closeProductDia();
            }
        }
    });
  proxy.$refs["productFormRef"].validate((valid) => {
    if (valid) {
      if (operationType.value === "edit") {
        submitProductEdit();
      } else {
        if(productOperationType.value === "add"){
          productData.value.push({ ...productForm.value });
        }else{
          productData.value[productIndex.value] = { ...productForm.value }
        }
        closeProductDia();
      }
    }
  });
};
const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 1
    addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeProductDia();
        getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
            productData.value = res.productData;
        });
    });
  productForm.value.salesLedgerId = currentId.value;
  productForm.value.type = 1
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
    proxy.$modal.msgSuccess("提交成功");
    closeProductDia();
    getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
      productData.value = res.productData;
    });
  });
};
// åˆ é™¤äº§å“
const deleteProduct = () => {
    if (productSelectedRows.value.length === 0) {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    // æ£€æŸ¥æ˜¯å¦æœ‰å·²å‘货或审核通过的产品
    const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
    if (shippedProducts.length > 0) {
        proxy.$modal.msgWarning("已发货或审核通过的产品不能删除");
        return;
    }
    if (operationType.value === "add") {
        productSelectedRows.value.forEach((selectedRow) => {
            const index = productData.value.findIndex(
                (product) => product.id === selectedRow.id
            );
            if (index !== -1) {
                productData.value.splice(index, 1);
            }
        });
    } else {
        let ids = [];
        if (productSelectedRows.value.length > 0) {
            ids = productSelectedRows.value.map((item) => item.id);
        }
        ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
            confirmButtonText: "确认",
            cancelButtonText: "取消",
            type: "warning",
        })
            .then(() => {
                delProduct(ids).then((res) => {
                    proxy.$modal.msgSuccess("删除成功");
                    closeProductDia();
                    getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
                        (res) => {
                            productData.value = res.productData;
                        }
                    );
                });
            })
            .catch(() => {
                proxy.$modal.msg("已取消");
            });
    }
  if (productSelectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach((selectedRow) => {
      const index = productData.value.findIndex(
        (product) => product.id === selectedRow.id
      );
      if (index !== -1) {
        productData.value.splice(index, 1);
      }
    });
  } else {
    let ids = [];
    if (productSelectedRows.value.length > 0) {
      ids = productSelectedRows.value.map((item) => item.id);
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delProduct(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
          getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
            (res) => {
              productData.value = res.productData;
            }
          );
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  }
};
// å…³é—­äº§å“å¼¹æ¡†
const closeProductDia = () => {
    proxy.resetForm("productFormRef");
    productFormVisible.value = false;
  proxy.resetForm("productFormRef");
  productFormVisible.value = false;
};
// å¯¼å…¥
const handleImport = () => {
    importUpload.title = "导入销售台账";
    importUpload.open = true;
    if (importUploadRef.value) {
        importUploadRef.value.clearFiles();
    }
};
// ä¸‹è½½å¯¼å…¥æ¨¡æ¿
const downloadTemplate = () => {
    proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx");
};
// æäº¤å¯¼å…¥æ–‡ä»¶
const submitImportFile = () => {
    importUpload.isUploading = true;
    proxy.$refs["importUploadRef"].submit();
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/sales/ledger/export", {}, "销售台账.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/sales/ledger/export", {}, "销售台账.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
/** åˆ¤æ–­å•个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
const isProductShipped = (product) => {
    if (!product) return false;
    const status = String(product.shippingStatus || "").trim();
    // å¦‚果发货状态是"已发货"或"审核通过",则不可编辑和删除
    return status === "已发货" || status === "审核通过";
};
/** åˆ¤æ–­é”€å”®è®¢å•下是否存在已发货/发货完成的产品(不可删除) */
const hasShippedProducts = (products) => {
    if (!products || !products.length) return false;
    return products.some((p) => {
        const status = String(p.shippingStatus || "").trim();
        // æœ‰å‘货日期或车牌号视为已发货
        if (p.shippingDate || p.shippingCarNumber) return true;
        // å·²è¿›è¡Œå‘货、发货完成、已发货 å‡ä¸å¯åˆ é™¤
        return status === "已进行发货" || status === "发货完成" || status === "已发货";
    });
};
// åˆ é™¤
const handleDelete = async () => {
    if (selectedRows.value.length === 0) {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    const unauthorizedRows = selectedRows.value.filter((row) => !canDeleteLedger(row));
    if (unauthorizedRows.length > 0) {
        proxy.$modal.msgWarning("当前登录用户不是录入人,不能删除该数据");
        return;
    }
    const ids = selectedRows.value.map((item) => item.id);
    // æ£€æŸ¥æ˜¯å¦æœ‰å·²è¿›è¡Œå‘货或发货完成的销售订单,若有则不允许删除
    const cannotDeleteNames = [];
    for (const row of selectedRows.value) {
        let products = row.children && row.children.length > 0 ? row.children : null;
        if (!products) {
            try {
                const res = await productList({ salesLedgerId: row.id, type: 1 });
                products = res.data || [];
            } catch {
                products = [];
            }
        }
        if (hasShippedProducts(products)) {
            cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
        }
    }
    if (cannotDeleteNames.length > 0) {
        proxy.$modal.msgWarning("已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、"));
        return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            delLedger(ids).then((res) => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  // æ£€æŸ¥æ˜¯å¦æœ‰å®¡æ ¸é€šè¿‡ä¸”维护人不是当前登录用户的数据
  const cannotDeleteRows = selectedRows.value.filter(
    (item) => item.approvalStatus === 3 && item.entryPerson !== userStore.id
  );
  if (cannotDeleteRows.length > 0) {
    proxy.$modal.msgWarning("审核通过且维护人不是当前登录用户的数据不可删除");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      delLedger(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// æ‰“印功能
@@ -1530,7 +1473,7 @@
        proxy.$modal.msgWarning("请选择要打印的数据");
        return;
    }
    // æ˜¾ç¤ºåŠ è½½çŠ¶æ€
    proxy.$modal.loading("正在获取产品数据,请稍候...");
    
@@ -1721,6 +1664,7 @@
      <div class="print-page">
        <div class="delivery-note">
          <div class="header">
            <div class="company-name">青海湟水峡农业发展有限公司</div>
            <div class="document-title">零售发货单</div>
          </div>
          
@@ -1765,8 +1709,8 @@
                      <td>${product.taxInclusiveTotalPrice || '0'}</td>
                    </tr>
                  `).join('') :
            '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
        }
                  '<tr><td colspan="6" style="text-align: center; color: #999;">暂无产品数据</td></tr>'
                }
              </tbody>
              <tfoot>
                <tr>
@@ -1884,6 +1828,20 @@
    return total.toFixed(2);
};
// å‘货类型校验:货车时要求车牌,快递时要求快递公司
const validateShippingCarNumber = (value, callback) => {
  if (deliveryForm.value.type === "货车") {
    if (!value) return callback(new Error("请输入发货车牌号"));
  }
  callback();
};
const validateExpressCompany = (value, callback) => {
  if (deliveryForm.value.type === "快递") {
    if (!value) return callback(new Error("请输入快递公司"));
  }
  callback();
};
const mathNum = () => {
    console.log("productForm.value", productForm.value);
    if (!productForm.value.taxInclusiveUnitPrice) {
@@ -1910,267 +1868,178 @@
// æ ¹æ®å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
const calculateFromTotalPrice = () => {
    if (isCalculating.value) return;
    const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
    const quantity = parseFloat(productForm.value.quantity);
    if (!totalPrice || !quantity || quantity <= 0) {
        return;
    }
    isCalculating.value = true;
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                totalPrice,
                productForm.value.taxRate
            );
    }
    isCalculating.value = false;
  if (isCalculating.value) return;
  const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
  const quantity = parseFloat(productForm.value.quantity);
  const taxRate = Number(productForm.value.taxRate) || 0;
  if (!totalPrice || !quantity || quantity <= 0) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
  productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
  // å¦‚果有税率,计算不含税总价
  productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
          totalPrice,
          taxRate
      );
  isCalculating.value = false;
};
// æ ¹æ®ä¸å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
const calculateFromExclusiveTotalPrice = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
    if (isCalculating.value) return;
    const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
    const quantity = parseFloat(productForm.value.quantity);
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
        return;
    }
    isCalculating.value = true;
    // å…ˆè®¡ç®—含税总价 = ä¸å«ç¨Žæ€»ä»· / (1 - ç¨Žçއ/100)
    const taxRateDecimal = taxRate / 100;
    const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
    productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
    isCalculating.value = false;
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
  if (isCalculating.value) return;
  const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
  const quantity = parseFloat(productForm.value.quantity);
  const taxRate = Number(productForm.value.taxRate) || 0;
  // if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
  //   return;
  // }
  if (!exclusiveTotalPrice || !quantity || quantity <= 0) {
    return;
  }
  isCalculating.value = true;
  // å…ˆè®¡ç®—含税总价 = ä¸å«ç¨Žæ€»ä»· / (1 - ç¨Žçއ/100)
  const taxRateDecimal = taxRate / 100;
  const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
  productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
  // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
  productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
  isCalculating.value = false;
};
// æ ¹æ®æ•°é‡å˜åŒ–计算总价
const calculateFromQuantity = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
        return;
    }
    isCalculating.value = true;
    // è®¡ç®—含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate
            );
    }
    isCalculating.value = false;
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  const taxRate = Number(productForm.value.taxRate) || 0;
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // å¦‚果有税率,计算不含税总价
  productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          taxRate
      );
  isCalculating.value = false;
};
// æ ¹æ®å«ç¨Žå•价变化计算总价
const calculateFromUnitPrice = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
    if (isCalculating.value) return;
    const quantity = parseFloat(productForm.value.quantity);
    const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
        return;
    }
    isCalculating.value = true;
    // è®¡ç®—含税总价
    productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productForm.value.taxRate) {
        productForm.value.taxExclusiveTotalPrice =
            proxy.calculateTaxExclusiveTotalPrice(
                productForm.value.taxInclusiveTotalPrice,
                productForm.value.taxRate
            );
    }
    isCalculating.value = false;
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
  if (isCalculating.value) return;
  const quantity = parseFloat(productForm.value.quantity);
  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
  const taxRate = Number(productForm.value.taxRate) || 0;
  if (!quantity || quantity <= 0 || !unitPrice) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—含税总价
  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
  // å¦‚果有税率,计算不含税总价
  productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
          productForm.value.taxInclusiveTotalPrice,
          taxRate
      );
  isCalculating.value = false;
};
// æ ¹æ®ç¨ŽçŽ‡å˜åŒ–è®¡ç®—ä¸å«ç¨Žæ€»ä»·
const calculateFromTaxRate = () => {
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
    if (isCalculating.value) return;
    const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
    const taxRate = parseFloat(productForm.value.taxRate);
    if (!inclusiveTotalPrice || !taxRate) {
        return;
    }
    isCalculating.value = true;
    // è®¡ç®—不含税总价
    productForm.value.taxExclusiveTotalPrice =
        proxy.calculateTaxExclusiveTotalPrice(
            inclusiveTotalPrice,
            taxRate
        );
    isCalculating.value = false;
};
/**
 * èŽ·å–å‘è´§çŠ¶æ€æ–‡æœ¬
 * @param row è¡Œæ•°æ®
 */
const getShippingStatusText = (row) => {
    // å¦‚果已发货(有发货日期或车牌号),显示"已发货"
    if (row.shippingDate || row.shippingCarNumber) {
        return '已发货';
    }
    // èŽ·å–å‘è´§çŠ¶æ€å­—æ®µ
    const status = row.shippingStatus;
    // å¦‚果状态为空或未定义,默认为"待发货"
    if (status === null || status === undefined || status === '') {
        return '待发货';
    }
    // çŠ¶æ€æ˜¯å­—ç¬¦ä¸²
    const statusStr = String(status).trim();
    const statusTextMap = {
        '待发货': '待发货',
        '待审核': '待审核',
        '审核中': '审核中',
        '审核拒绝': '审核拒绝',
        '审核通过': '审核通过',
        '已发货': '已发货'
    };
    return statusTextMap[statusStr] || '待发货';
};
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
  if (isCalculating.value) return;
/**
 * èŽ·å–å‘è´§çŠ¶æ€æ ‡ç­¾ç±»åž‹ï¼ˆé¢œè‰²ï¼‰
 * @param row è¡Œæ•°æ®
 */
const getShippingStatusType = (row) => {
    // å¦‚果已发货(有发货日期或车牌号),显示绿色
    if (row.shippingDate || row.shippingCarNumber) {
        return 'success';
    }
    // èŽ·å–å‘è´§çŠ¶æ€å­—æ®µ
    const status = row.shippingStatus;
    // å¦‚果状态为空或未定义,默认为灰色(待发货)
    if (status === null || status === undefined || status === '') {
        return 'info';
    }
    // çŠ¶æ€æ˜¯å­—ç¬¦ä¸²
    const statusStr = String(status).trim();
    const typeTextMap = {
        '待发货': 'info',
        '待审核': 'info',
        '审核中': 'warning',
        '审核拒绝': 'danger',
        '审核通过': 'success',
        '已发货': 'success'
    };
    return typeTextMap[statusStr] || 'info';
};
  const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
  const taxRate = Number(productForm.value.taxRate) || 0;
/**
 * åˆ¤æ–­æ˜¯å¦å¯ä»¥å‘è´§
 * åªæœ‰åœ¨äº§å“çŠ¶æ€æ˜¯å……è¶³ï¼Œå‘è´§çŠ¶æ€æ˜¯å¾…å‘è´§å’Œå®¡æ ¸æ‹’ç»çš„æ—¶å€™æ‰å¯ä»¥å‘è´§
 * @param row è¡Œæ•°æ®
 */
const canShip = (row) => {
    // äº§å“çŠ¶æ€å¿…é¡»æ˜¯å……è¶³ï¼ˆapproveStatus === 1)
    if (row.approveStatus !== 1) {
        return false;
    }
    // èŽ·å–å‘è´§çŠ¶æ€
    const shippingStatus = row.shippingStatus;
    // å¦‚果已发货(有发货日期或车牌号),不能再次发货
    if (row.shippingDate || row.shippingCarNumber) {
        return false;
    }
    // å‘货状态必须是"待发货"或"审核拒绝"
    const statusStr = shippingStatus ? String(shippingStatus).trim() : '';
    return statusStr === '待发货' || statusStr === '审核拒绝';
};
  // if (!inclusiveTotalPrice || !taxRate) {
  //   return;
  // }
  if (!inclusiveTotalPrice) {
    return;
  }
  isCalculating.value = true;
  // è®¡ç®—不含税总价
  productForm.value.taxExclusiveTotalPrice =
    proxy.calculateTaxExclusiveTotalPrice(
      inclusiveTotalPrice,
      taxRate
    );
  isCalculating.value = false;
};
/**
 * ä¸‹è½½æ–‡ä»¶
 *
 * @param row ä¸‹è½½æ–‡ä»¶çš„相关信息对象
 */
const fileListRef = ref(null)
const fileListDialogVisible = ref(false)
const downLoadFile = (row) => {
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
        if (fileListRef.value) {
            fileListRef.value.open(res.salesLedgerFiles)
        }
    });
  getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
    fileListRef.value.open(res.salesLedgerFiles)
  });
}
// æ‰“开发货弹框
const openDeliveryForm = (row) => {
    // æ£€æŸ¥æ˜¯å¦å¯ä»¥å‘è´§
    if (!canShip(row)) {
        proxy.$modal.msgWarning("只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货");
        return;
    }
    currentDeliveryRow.value = row;
  deliveryForm.value = {
    type: "货车",
    shippingDate: getCurrentDate(),
    shippingCarNumber: "",
    expressCompany: "",
    expressNumber: "", // åˆå§‹åŒ–快递单号为空
    shippingImages: "", // åˆå§‹åŒ–图片为空
  };
  // é‡ç½®å®¡æ‰¹äººèŠ‚ç‚¹ï¼ˆé»˜è®¤ä¸€ä¸ªç©ºèŠ‚ç‚¹ï¼‰
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
  deliveryFileList.value = []; // åˆå§‹åŒ–文件列表为空
    deliveryFormVisible.value = true;
};
@@ -2178,45 +2047,27 @@
const submitDelivery = () => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
    if (valid) {
      // å®¡æ‰¹äººå¿…填校验(所有节点都要选人)
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
      if (hasEmptyApprover) {
        proxy.$modal.msgError("请为所有审批节点选择审批人!");
        return;
      let tempFileIds = [];
      if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) {
        tempFileIds = deliveryFileList.value.map((item) => item.tempId);
      }
      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
      // ä¿å­˜å½“前展开的行ID,以便发货后重新加载子表格数据
      const currentExpandedKeys = [...expandedRowKeys.value];
      const salesLedgerId = currentDeliveryRow.value.salesLedgerId;
      addShippingInfo({
        salesLedgerId: salesLedgerId,
        salesLedgerProductId: currentDeliveryRow.value.id,
        salesLedgerId: currentDeliveryRow.value.id,
        type: deliveryForm.value.type,
                approveUserIds,
        shippingDate: deliveryForm.value.shippingDate,
        shippingCarNumber: deliveryForm.value.type === "货车" ? deliveryForm.value.shippingCarNumber : "",
        expressCompany: deliveryForm.value.type === "快递" ? deliveryForm.value.expressCompany : "",
        expressNumber: deliveryForm.value.type === "快递" ? deliveryForm.value.expressNumber : "",
        tempFileIds: tempFileIds,
      })
        .then(() => {
          proxy.$modal.msgSuccess("发货成功");
          closeDeliveryDia();
          // åˆ·æ–°ä¸»è¡¨æ•°æ®
          getList().then(() => {
            // å¦‚果之前有展开的行,重新加载这些行的子表格数据
            if (currentExpandedKeys.length > 0) {
              // ä½¿ç”¨ Promise.all å¹¶è¡ŒåŠ è½½æ‰€æœ‰å±•å¼€è¡Œçš„å­è¡¨æ ¼æ•°æ®
              const loadPromises = currentExpandedKeys.map(ledgerId => {
                return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => {
                  const index = tableData.value.findIndex((item) => item.id === ledgerId);
                  if (index > -1) {
                    tableData.value[index].children = res.data;
                  }
                });
              });
              Promise.all(loadPromises).then(() => {
                // æ¢å¤å±•开状态
                expandedRowKeys.value = currentExpandedKeys;
              });
            }
          });
          getList();
        })
        .catch(() => {
          proxy.$modal.msgError("发货失败,请重试");
        });
    }
  });
};
@@ -2224,20 +2075,25 @@
// å…³é—­å‘货弹框
const closeDeliveryDia = () => {
  proxy.resetForm("deliveryFormRef");
  deliveryFileList.value = []; // æ¸…空文件列表
  deliveryForm.value.shippingImages = ""; // æ¸…空图片
  deliveryForm.value.expressNumber = ""; // æ¸…空快递单号
  deliveryFormVisible.value = false;
  currentDeliveryRow.value = null;
};
const currentFactoryName = ref("");
const getCurrentFactoryName = async () => {
    let res = await userStore.getInfo();
    currentFactoryName.value = res.user.currentFactoryName;
// å‘货类型切换时清空对应字段
const handleShippingTypeChange = (val) => {
  if (val === "货车") {
    deliveryForm.value.expressCompany = "";
    deliveryForm.value.expressNumber = "";
  } else {
    deliveryForm.value.shippingCarNumber = "";
  }
};
onMounted(() => {
    getList();
    userListNoPage().then(res => {
        userList.value = res.data;
    })
    getCurrentFactoryName();
});
</script>
@@ -2437,4 +2293,62 @@
        page-break-after: avoid;
    }
}
// éšè—å›¾ç‰‡ä¸Šä¼ ç»„件的预览按钮(放大镜)
:deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
  .el-upload-list__item-preview {
    display: none;
  }
}
// å®¡æ‰¹äººèŠ‚ç‚¹æ ·å¼
.approver-nodes-container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 16px;
  background-color: #f8f9fa;
  border-radius: 4px;
  border: 1px solid #e4e7ed;
}
.approver-node-item {
  flex: 0 0 calc(33.333% - 12px);
  min-width: 200px;
  padding: 12px;
  background-color: #fff;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
  transition: all 0.3s;
  &:hover {
    border-color: #409eff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
  }
}
.approver-node-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.approver-node-label {
  font-size: 13px;
  font-weight: 500;
  color: #606266;
}
@media (max-width: 1200px) {
  .approver-node-item {
    flex: 0 0 calc(50% - 8px);
  }
}
@media (max-width: 768px) {
  .approver-node-item {
    flex: 0 0 100%;
  }
}
</style>
src/views/salesManagement/salesQuotation/index.vue
@@ -15,15 +15,6 @@
            </template>
          </el-input>
        </el-col>
        <el-col :span="8">
          <el-select v-model="searchForm.customer" placeholder="请选择客户" clearable>
                        <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
                            {{
                                item.customerName + "——" + item.taxpayerIdentificationNumber
                            }}
                        </el-option>
          </el-select>
        </el-col>
<!--        <el-col :span="6">-->
<!--          <el-select v-model="searchForm.status" placeholder="请选择报价状态" clearable>-->
<!--            <el-option label="草稿" value="草稿"></el-option>-->
@@ -52,26 +43,12 @@
      >
                <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column prop="quotationNo" label="报价单号" />
        <el-table-column prop="customer" label="客户名称" />
        <el-table-column prop="salesperson" label="业务员" width="100" />
        <el-table-column prop="quotationDate" label="报价日期" width="120" />
        <el-table-column prop="validDate" label="有效期至" width="120" />
        <el-table-column prop="status" label="审批状态" width="120" align="center">
          <template #default="{ row }">
            <el-tag :type="getStatusType(row.status)" disable-transitions>
              {{ row.status || '--' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="totalAmount" label="报价金额" width="120">
          <template #default="scope">
            Â¥{{ scope.row.totalAmount.toFixed(2) }}
          </template>
        </el-table-column>
        <el-table-column prop="salesperson" label="业务员" />
        <el-table-column prop="quotationDate" label="报价日期" />
        <el-table-column label="操作" width="200" fixed="right" align="center">
          <template #default="scope">
            <el-button link type="primary" @click="handleView(scope.row)">查看</el-button>
            <el-button link type="primary" @click="handleEdit(scope.row)" :disabled="!['待审批','拒绝'].includes(scope.row.status)">编辑</el-button>
            <el-button link type="primary" @click="handleView(scope.row)" style="color: #67C23A">查看</el-button>
            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
@@ -91,7 +68,7 @@
    <FormDialog v-model="dialogVisible" :title="dialogTitle" width="85%" :close-on-click-modal="false" @close="dialogVisible = false" @confirm="handleSubmit" @cancel="dialogVisible = false">
      <div class="quotation-form-container">
        <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="quotation-form">
        <!-- åŸºæœ¬ä¿¡æ¯ -->
        <!-- åŸºæœ¬ä¿¡æ¯ï¼ˆä»…保留业务员和报价日期) -->
        <el-card class="form-card" shadow="hover">
          <template #header>
            <div class="card-header-wrapper">
@@ -102,22 +79,17 @@
          <div class="form-content">
            <el-row :gutter="24">
              <el-col :span="12">
                <el-form-item label="客户名称" prop="customer">
                  <el-select v-model="form.customer" placeholder="请选择客户" style="width: 100%" clearable filterable>
                    <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"></el-option>
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="业务员" prop="salesperson">
                  <el-select v-model="form.salesperson" placeholder="请选择业务员" style="width: 100%" clearable filterable>
                    <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                      :value="item.nickName" />
                  <el-select v-model="form.salesperson" placeholder="请选择业务员" style="width: 100%" clearable>
                    <el-option
                      v-for="item in userList"
                      :key="item.nickName"
                      :label="item.nickName"
                      :value="item.nickName"
                    />
                  </el-select>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="24">
              <el-col :span="12">
                <el-form-item label="报价日期" prop="quotationDate">
                  <el-date-picker
@@ -129,83 +101,6 @@
                    value-format="YYYY-MM-DD"
                    clearable
                  />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="有效期至" prop="validDate">
                  <el-date-picker
                    v-model="form.validDate"
                    type="date"
                    placeholder="选择有效期"
                    style="width: 100%"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                    clearable
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="24">
              <el-col :span="12">
                <el-form-item label="付款方式" prop="paymentMethod">
                  <el-input v-model="form.paymentMethod" placeholder="请输入付款方式" clearable />
                </el-form-item>
              </el-col>
            </el-row>
          </div>
        </el-card>
        <!-- å®¡æ‰¹äººä¿¡æ¯ -->
        <el-card class="form-card" shadow="hover">
          <template #header>
            <div class="card-header-wrapper">
              <el-icon class="card-icon"><UserFilled /></el-icon>
              <span class="card-title">审批人选择</span>
              <el-button type="primary" size="small" @click="addApproverNode" class="header-btn">
                <el-icon><Plus /></el-icon>
                æ–°å¢žèŠ‚ç‚¹
              </el-button>
            </div>
          </template>
          <div class="form-content">
            <el-row>
              <el-col :span="24">
                <el-form-item>
                  <div class="approver-nodes-container">
                    <div
                      v-for="(node, index) in approverNodes"
                      :key="node.id"
                      class="approver-node-item"
                    >
                      <div class="approver-node-label">
                        <span class="node-step">{{ index + 1 }}</span>
                        <span class="node-text">审批人</span>
                        <el-icon class="arrow-icon"><ArrowRight /></el-icon>
                      </div>
                      <el-select
                        v-model="node.userId"
                        placeholder="选择人员"
                        class="approver-select"
                                                filterable
                        clearable
                      >
                        <el-option
                          v-for="user in userList"
                          :key="user.userId"
                          :label="user.nickName"
                          :value="user.userId"
                        />
                      </el-select>
                      <el-button
                        type="danger"
                        size="small"
                        :icon="Delete"
                        @click="removeApproverNode(index)"
                        v-if="approverNodes.length > 1"
                        class="remove-btn"
                      >删除</el-button>
                    </div>
                  </div>
                </el-form-item>
              </el-col>
            </el-row>
@@ -228,52 +123,53 @@
            <el-table :data="form.products" border style="width: 100%" class="product-table" v-if="form.products.length > 0">
            <el-table-column prop="product" label="产品名称" width="200">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.productId`" class="product-table-form-item">
                  <el-tree-select
                    v-model="scope.row.productId"
                    placeholder="请选择"
                    clearable
                    check-strictly
                    @change="getModels($event, scope.row)"
                    :data="productOptions"
                    :render-after-expand="false"
                    style="width: 100%"
                  />
                </el-form-item>
                                <el-tree-select
                                    v-model="scope.row.productId"
                                    placeholder="请选择"
                                    clearable
                                    check-strictly
                                    @change="getModels($event, scope.row)"
                                    :data="productOptions"
                                    :render-after-expand="false"
                                    style="width: 100%"
                                />
              </template>
            </el-table-column>
            <el-table-column prop="specification" label="规格型号" width="200">
            <el-table-column prop="specification" label="规格型号" width="150">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.specificationId`" class="product-table-form-item">
                  <el-select
                    v-model="scope.row.specificationId"
                    placeholder="请选择"
                    clearable
                    @change="getProductModel($event, scope.row)"
                    style="width: 100%"
                  >
                    <el-option
                      v-for="item in scope.row.modelOptions || []"
                      :key="item.id"
                      :label="item.model"
                      :value="item.id"
                    />
                  </el-select>
                </el-form-item>
                                <el-select
                                    v-model="scope.row.specificationId"
                                    placeholder="请选择"
                                    clearable
                                    @change="getProductModel($event, scope.row)"
                                >
                                    <el-option
                                        v-for="item in modelOptions"
                                        :key="item.id"
                                        :label="item.model"
                                        :value="item.id"
                                    />
                                </el-select>
              </template>
            </el-table-column>
            <el-table-column prop="unit" label="单位">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.unit`" class="product-table-form-item">
                  <el-input v-model="scope.row.unit" placeholder="单位" clearable/>
                </el-form-item>
                <el-input v-model="scope.row.unit" placeholder="单位" />
              </template>
            </el-table-column>
            <el-table-column prop="unitPrice" label="单价">
            <el-table-column prop="unitPrice" label="一批商单价">
              <template #default="scope">
                <el-form-item :prop="`products.${scope.$index}.unitPrice`" class="product-table-form-item">
                  <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
                </el-form-item>
                <el-input-number v-model="scope.row.unitPrice" :min="0" :precision="2" style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column prop="unitPriceTwo" label="终端商单价">
              <template #default="scope">
                <el-input-number v-model="scope.row.unitPriceTwo" :min="0" :precision="2" style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column prop="unitPriceThree" label="单价">
              <template #default="scope">
                <el-input-number v-model="scope.row.unitPriceThree" :min="0" :precision="2" style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="80" align="center">
@@ -315,28 +211,29 @@
    <el-dialog v-model="viewDialogVisible" title="报价详情" width="800px">
      <el-descriptions :column="2" border>
        <el-descriptions-item label="报价单号">{{ currentQuotation.quotationNo }}</el-descriptions-item>
        <el-descriptions-item label="客户名称">{{ currentQuotation.customer }}</el-descriptions-item>
        <el-descriptions-item label="业务员">{{ currentQuotation.salesperson }}</el-descriptions-item>
        <el-descriptions-item label="报价日期">{{ currentQuotation.quotationDate }}</el-descriptions-item>
        <el-descriptions-item label="有效期至">{{ currentQuotation.validDate }}</el-descriptions-item>
        <el-descriptions-item label="付款方式">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
<!--        <el-descriptions-item label="报价状态">-->
<!--          <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
<!--        </el-descriptions-item>-->
        <el-descriptions-item label="报价总额" :span="2">
          <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">Â¥{{ currentQuotation.totalAmount?.toFixed(2) }}</span>
        </el-descriptions-item>
      </el-descriptions>
      
      <div style="margin: 20px 0;">
      <div style="margin-top: 20px;">
        <h4>产品明细</h4>
        <el-table :data="currentQuotation.products" border style="width: 100%">
          <el-table-column prop="product" label="产品名称" />
          <el-table-column prop="specification" label="规格型号" />
          <el-table-column prop="unit" label="单位" />
          <el-table-column prop="unitPrice" label="单价">
          <el-table-column prop="unitPrice" label="一批商单价">
            <template #default="scope">
              Â¥{{ scope.row.unitPrice.toFixed(2) }}
            </template>
          </el-table-column>
          <el-table-column prop="unitPriceTwo" label="终端商单价">
            <template #default="scope">
              Â¥{{ scope.row.unitPriceTwo?.toFixed(2) }}
            </template>
          </el-table-column>
          <el-table-column prop="unitPriceThree" label="单价">
            <template #default="scope">
              Â¥{{ scope.row.unitPriceThree?.toFixed(2) }}
            </template>
          </el-table-column>
        </el-table>
@@ -353,20 +250,18 @@
<script setup>
import { ref, reactive, computed, onMounted, markRaw, shallowRef } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue'
import { Search, Document, Box, EditPen, Plus } from '@element-plus/icons-vue'
import Pagination from '@/components/PIMTable/Pagination.vue'
import FormDialog from '@/components/Dialog/FormDialog.vue'
import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js'
import {userListNoPage} from "@/api/system/user.js";
import {customerList} from "@/api/salesManagement/salesLedger.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
import {listCustomerPrivatePool} from "@/api/basicData/customerFile.js";
// å“åº”式数据
const loading = ref(false)
const searchForm = reactive({
  quotationNo: '',
  customer: '',
  status: ''
})
@@ -400,57 +295,27 @@
  totalAmount: 0
})
const baseRules = {
  customer: [{ required: true, message: '请选择客户', trigger: 'change' }],
const rules = {
  salesperson: [{ required: true, message: '请选择业务员', trigger: 'change' }],
  quotationDate: [{ required: true, message: '请选择报价日期', trigger: 'change' }],
  validDate: [{ required: true, message: '请选择有效期', trigger: 'change' }],
  paymentMethod: [{ required: true, message: '请输入付款方式', trigger: 'blur' }]
  quotationDate: [{ required: true, message: '请选择报价日期', trigger: 'change' }]
}
const productRowRules = {
  productId: [{ required: true, message: '请选择产品名称', trigger: 'change' }],
  specificationId: [{ required: true, message: '请选择规格型号', trigger: 'change' }],
  unit: [{ required: true, message: '请填写单位', trigger: 'blur' }],
  unitPrice: [{ required: true, message: '请填写单价', trigger: 'change' }]
}
const rules = computed(() => {
  const r = { ...baseRules }
  ;(form.products || []).forEach((_, i) => {
    r[`products.${i}.productId`] = productRowRules.productId
    r[`products.${i}.specificationId`] = productRowRules.specificationId
    r[`products.${i}.unit`] = productRowRules.unit
    r[`products.${i}.unitPrice`] = productRowRules.unitPrice
  })
  return r
})
const userList = ref([]);
const customerOption = ref([]);
// å®¡æ‰¹äººèŠ‚ç‚¹ç›¸å…³
const approverNodes = ref([
  { id: 1, userId: null }
])
let nextApproverId = 2
const isEdit = ref(false)
const editId = ref(null)
const currentQuotation = ref({})
const formRef = ref()
// æ·»åŠ å®¡æ‰¹äººèŠ‚ç‚¹
function addApproverNode() {
  approverNodes.value.push({ id: nextApproverId++, userId: null })
}
// åˆ é™¤å®¡æ‰¹äººèŠ‚ç‚¹
function removeApproverNode(index) {
  approverNodes.value.splice(index, 1)
}
// è®¡ç®—属性
const filteredList = computed(() => {
  let list = quotationList.value
  if (searchForm.quotationNo) {
    list = list.filter(item => item.quotationNo.includes(searchForm.quotationNo))
  }
  if (searchForm.status) {
    list = list.filter(item => item.status === searchForm.status)
  }
  return list
})
@@ -467,20 +332,13 @@
const resetSearch = () => {
  searchForm.quotationNo = ''
  searchForm.customer = ''
  searchForm.status = ''
  // é‡ç½®åˆ°ç¬¬ä¸€é¡µå¹¶é‡æ–°æŸ¥è¯¢
  pagination.currentPage = 1
  handleSearch()
}
const handleAdd = async () => {
  dialogTitle.value = '新增报价'
  isEdit.value = false
  resetForm()
  // é‡ç½®å®¡æ‰¹äººèŠ‚ç‚¹
  approverNodes.value = [{ id: 1, userId: null }]
  nextApproverId = 2
  dialogVisible.value = true
    let userLists = await userListNoPage();
    // åªå¤åˆ¶éœ€è¦çš„字段,避免将组件引用放入响应式对象
@@ -490,8 +348,13 @@
    userName: item.userName || ''
  }));
    getProductOptions();
    listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
        customerOption.value = res.data.records;
    customerList().then((res) => {
        // åªå¤åˆ¶éœ€è¦çš„字段,避免将组件引用放入响应式对象
        customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
      id: item.id,
      customerName: item.customerName || '',
      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
    }))
    });
}
const getProductOptions = () => {
@@ -534,7 +397,7 @@
    if (!value) {
        row.productId = '';
        row.product = '';
        row.modelOptions = [];
        modelOptions.value = [];
        row.specificationId = '';
        row.specification = '';
        row.unit = '';
@@ -547,9 +410,9 @@
    if (label) {
        row.product = label;
    }
    // èŽ·å–è§„æ ¼åž‹å·åˆ—è¡¨ï¼Œè®¾ç½®åˆ°å½“å‰è¡Œçš„ modelOptions
    // èŽ·å–è§„æ ¼åž‹å·åˆ—è¡¨
    modelList({ id: value }).then((res) => {
        row.modelOptions = res || [];
        modelOptions.value = res || [];
    });
};
const getProductModel = (value, row) => {
@@ -563,11 +426,10 @@
    }
    // æ›´æ–° specificationId(v-model å·²ç»è‡ªåŠ¨æ›´æ–°ï¼Œè¿™é‡Œç¡®ä¿ä¸€è‡´æ€§ï¼‰
    row.specificationId = value;
    const modelOptions = row.modelOptions || [];
    const index = modelOptions.findIndex((item) => item.id === value);
    const index = modelOptions.value.findIndex((item) => item.id === value);
    if (index !== -1) {
        row.specification = modelOptions[index].model;
        row.unit = modelOptions[index].unit;
        row.specification = modelOptions.value[index].model;
        row.unit = modelOptions.value[index].unit;
    } else {
        row.specification = '';
        row.unit = '';
@@ -605,7 +467,9 @@
      specification: product.specification || '',
      quantity: product.quantity || 0,
      unit: product.unit || '',
      unitPrice: product.unitPrice || 0,
      unitPrice: product.unitPrice || 0, // ä¸€æ‰¹å•†å•ä»·
      unitPriceTwo: product.unitPriceTwo || product.dealerUnitPrice || 0, // ç»ˆç«¯å•†å•ä»·
      unitPriceThree: product.unitPriceThree || 0, // å•ä»·
      amount: product.amount || 0
    })) : [],
    totalAmount: row.totalAmount || 0
@@ -630,65 +494,31 @@
  form.paymentMethod = row.paymentMethod || ''
  form.status = row.status || '草稿'
  form.remark = row.remark || ''
  form.products = row.products ? await Promise.all(row.products.map(async (product) => {
  form.products = row.products ? row.products.map(product => {
    const productName = product.product || product.productName || ''
    // ä¼˜å…ˆç”¨ productId;如果只有名称,尝试反查 id ä»¥ä¾¿æ ‘选择器反显
    const resolvedProductId = product.productId
    const resolvedId = product.productId
      ? Number(product.productId)
      : findNodeIdByLabel(productOptions.value, productName) || ''
    // å¦‚果有产品ID,加载对应的规格型号列表
    let modelOptions = [];
    let resolvedSpecificationId = product.specificationId || '';
    if (resolvedProductId) {
      try {
        const res = await modelList({ id: resolvedProductId });
        modelOptions = res || [];
        // å¦‚果返回的数据没有 specificationId,但有 specification åç§°ï¼Œæ ¹æ®åç§°æŸ¥æ‰¾ ID
        if (!resolvedSpecificationId && product.specification) {
          const foundModel = modelOptions.find(item => item.model === product.specification);
          if (foundModel) {
            resolvedSpecificationId = foundModel.id;
          }
        }
      } catch (error) {
        console.error('加载规格型号失败:', error);
      }
    }
    return {
      productId: resolvedProductId,
      productId: resolvedId,
      product: productName,
      specificationId: resolvedSpecificationId,
      specificationId: product.specificationId || '',
      specification: product.specification || '',
      quantity: product.quantity || 0,
      unit: product.unit || '',
      unitPrice: product.unitPrice || 0,
      amount: product.amount || 0,
      modelOptions: modelOptions // ä¸ºæ¯è¡Œæ·»åŠ ç‹¬ç«‹çš„è§„æ ¼åž‹å·åˆ—è¡¨
      unitPriceTwo: product.unitPriceTwo || product.dealerUnitPrice || 0,
      unitPriceThree: product.unitPriceThree || 0,
      amount: product.amount || 0
    }
  })) : []
  }) : []
  form.subtotal = row.subtotal || 0
  form.freight = row.freight || 0
  form.otherFee = row.otherFee || 0
  form.discountRate = row.discountRate || 0
  form.discountAmount = row.discountAmount || 0
  form.totalAmount = row.totalAmount || 0
  // åæ˜¾å®¡æ‰¹äºº
  if (row.approveUserIds) {
    const userIds = row.approveUserIds.split(',')
    approverNodes.value = userIds.map((userId, idx) => ({
      id: idx + 1,
      userId: parseInt(userId.trim())
    }))
    nextApproverId = userIds.length + 1
  } else {
    approverNodes.value = [{ id: 1, userId: null }]
    nextApproverId = 2
  }
  
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨
  let userLists = await userListNoPage();
@@ -750,9 +580,10 @@
    specification: '',
    quantity: 1,
    unit: '',
    unitPriceTwo: 0,
    unitPriceThree: 0,
    unitPrice: 0,
    amount: 0,
    modelOptions: [] // ä¸ºæ¯è¡Œæ·»åŠ ç‹¬ç«‹çš„è§„æ ¼åž‹å·åˆ—è¡¨
    amount: 0
  })
}
@@ -776,6 +607,10 @@
  form.totalAmount = form.subtotal + form.freight + form.otherFee - form.discountAmount
}
const handleCustomerChange = () => {
  // å¯ä»¥æ ¹æ®å®¢æˆ·ä¿¡æ¯è‡ªåŠ¨å¡«å……ä¸€äº›é»˜è®¤å€¼
}
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    if (valid) {
@@ -783,16 +618,6 @@
        ElMessage.warning('请至少添加一个产品')
        return
      }
      // å®¡æ‰¹äººå¿…填校验
      const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
      if (hasEmptyApprover) {
        ElMessage.error('请为所有审批节点选择审批人!')
        return
      }
      // æ”¶é›†æ‰€æœ‰èŠ‚ç‚¹çš„å®¡æ‰¹äººid
      form.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
      
      // è®¡ç®—所有产品的单价总和
      form.totalAmount = form.products.reduce((sum, product) => {
@@ -831,14 +656,10 @@
const handleCurrentChange = (val) => {
  pagination.currentPage = val.page
  pagination.pageSize = val.limit
  // åˆ†é¡µå˜åŒ–时重新查询列表
  handleSearch()
}
const handleSearch = ()=>{
  const params = {
    // åŽç«¯åˆ†é¡µå‚数:current / size
    current: pagination.currentPage,
    size: pagination.pageSize,
    ...pagination,
    ...searchForm
  }
  getQuotationList(params).then(res=>{
@@ -854,8 +675,6 @@
        validDate: item.validDate || '',
        paymentMethod: item.paymentMethod || '',
        status: item.status || '草稿',
        // å®¡æ‰¹äººï¼ˆç”¨äºŽç¼–辑时反显)
        approveUserIds: item.approveUserIds || '',
        remark: item.remark || '',
        products: item.products ? item.products.map(product => ({
          productId: product.productId || '',
@@ -865,6 +684,8 @@
          quantity: product.quantity || 0,
          unit: product.unit || '',
          unitPrice: product.unitPrice || 0,
          unitPriceTwo: product.unitPriceTwo || product.dealerUnitPrice || 0,
          unitPriceThree: product.unitPriceThree || 0,
          amount: product.amount || 0
        })) : [],
        subtotal: item.subtotal || 0,
@@ -878,7 +699,12 @@
    }
  })
    customerList().then((res) => {
        customerOption.value = res;
        // åªå¤åˆ¶éœ€è¦çš„字段,避免将组件引用放入响应式对象
        customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
      id: item.id,
      customerName: item.customerName || '',
      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
    }))
    });
}
@@ -962,82 +788,6 @@
.form-content {
  padding: 8px 0;
}
.product-table-form-item {
  margin-bottom: 0;
  :deep(.el-form-item__content) {
    margin-left: 0 !important;
  }
  :deep(.el-form-item__label) {
    width: auto;
    min-width: auto;
  }
}
.approver-nodes-container {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  padding: 12px 0;
}
.approver-node-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 16px;
  background: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  transition: all 0.3s ease;
  min-width: 180px;
  &:hover {
    border-color: #409eff;
    background: #f0f7ff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
  }
}
.approver-node-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #606266;
  .node-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    background: #409eff;
    color: #fff;
    border-radius: 50%;
    font-size: 12px;
    font-weight: 600;
  }
  .node-text {
    font-weight: 500;
  }
  .arrow-icon {
    color: #909399;
    font-size: 14px;
  }
}
.approver-select {
  width: 100%;
  min-width: 150px;
}
.remove-btn {
  margin-top: 4px;
}
.product-table {