buhuazhen
2 天以前 862562319ab34582c2fe26c4c9c03b4ba7871caf
feat: 德益科技项目功能优化与配置

包含以下更新:
- 统一表单创建时间和编号生成逻辑
- 原材料、成品、出厂检验列表添加合格率统计字段
- 入库记录管理添加源单号列显示
- 采购合同号按录入日期生成
- 产品规格改为规格型号
- 修复框架 tags-view 显示问题
- 质量管理检测页面增加查询条件
- 审批列表筛选条件修复
- OA审批附件列表显示修复
- 人事审批附件展示功能
- 华强建材/八维商贸配置文件

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
已添加4个文件
已修改37个文件
4213 ■■■■■ 文件已修改
multiple/assets/favicon/BWSMfavicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HQJCfavicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/BWSMLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HQJCLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementLedger.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/index.vue 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/parameterMaintenance/index.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/purchaseApproval/index.vue 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/assets/fixedAssets.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/assets/intangibleAssets.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/paymentApply.vue 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/payable/purchaseIn.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/invoiceApply.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/receipt.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/receivable/salesOut.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/Record.vue 165 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/Record.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/Record.vue 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js 126 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/officeProcessAutomation/HrManage/staff-contract/components/formDia.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/components/formDia.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementPlan/index.vue 623 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/purchaseOrder/index.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/qualityInspection/index.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productManagement/productIdentifier/index.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionTraceability/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrderManagement/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionPlan/productionPlan/index.vue 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/index.vue 318 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nearExpiryReturn/index.vue 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/index.vue 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/index.vue 274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/dangerInvestigation/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/safetyTrainingAssessment/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 1127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/BWSMfavicon.ico
multiple/assets/favicon/HQJCfavicon.ico
multiple/assets/logo/BWSMLogo.png
multiple/assets/logo/HQJCLogo.png
src/api/procurementManagement/procurementLedger.js
@@ -66,10 +66,11 @@
  });
}
export function createPurchaseNo() {
export function createPurchaseNo(entryDate) {
  return request({
    url: "/purchase/ledger/createPurchaseNo",
    method: "get",
    params: { entryDate },
  });
}
export function updateApprovalStatus(query) {
src/layout/index.vue
@@ -11,7 +11,7 @@
         class="main-container main-layout">
      <div :class="{ 'fixed-header': fixedHeader, 'with-tags': showTagsView }">
        <navbar @setLayout="setLayout" />
        <tags-view v-if="showTagsView" />
        <tags-view />
      </div>
      <app-main />
      <settings ref="settingRef" />
@@ -42,11 +42,15 @@
  const sidebar = computed(() => useAppStore().sidebar);
  const device = computed(() => useAppStore().device);
  const needTagsView = computed(() => settingsStore.tagsView);
  const showTagsView = computed(() => needTagsView.value && tagsViewStore.visitedViews.length > 1);
  const showTagsView = computed(
    () => needTagsView.value && tagsViewStore.visitedViews.length > 1
  );
  const fixedHeader = computed(() => settingsStore.fixedHeader);
  const aiEnabled = computed(() => Number(userStore.aiEnabled) === 1);
  const showGlobalAiChat = computed(() => {
    const isIndustrialBrainRoute = String(route.path || "").startsWith("/ai-industrial-brain");
    const isIndustrialBrainRoute = String(route.path || "").startsWith(
      "/ai-industrial-brain"
    );
    return !isIndustrialBrainRoute && aiEnabled.value;
  });
@@ -96,9 +100,16 @@
    position: relative;
    min-height: 100%;
    width: 100%;
    background:
      radial-gradient(circle at 14% -8%, rgba(59, 130, 246, 0.14), transparent 36%),
      radial-gradient(circle at 88% -12%, rgba(56, 189, 248, 0.1), transparent 30%),
    background: radial-gradient(
        circle at 14% -8%,
        rgba(59, 130, 246, 0.14),
        transparent 36%
      ),
      radial-gradient(
        circle at 88% -12%,
        rgba(56, 189, 248, 0.1),
        transparent 30%
      ),
      linear-gradient(165deg, #f3f7fc 0%, #eef5ff 56%, #f8fbff 100%);
    &.mobile.openSidebar {
src/views/basicData/parameterMaintenance/index.vue
@@ -50,7 +50,7 @@
                      prop="paramCode">
          <el-input v-model="formData.paramCode"
                    disabled
                    placeholder="自动生成" />
                    placeholder="保存后自动生成" />
        </el-form-item>
        <el-form-item label="参数名称"
                      prop="paramName">
@@ -82,6 +82,14 @@
                       value="2" />
          </el-select>
        </el-form-item> -->
        <el-form-item label="创建时间"
                      prop="createTime">
          <el-date-picker v-model="formData.createTime"
                          type="date"
                          placeholder="选择日期"
                          value-format="YYYY-MM-DD"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="单位"
                      prop="unit">
          <el-input v-model="formData.unit"
@@ -341,6 +349,7 @@
    remark: "",
    isRequired: 0,
    paramFormat: "",
    createTime: "",
  });
  const rules = reactive({
    paramName: [{ required: true, message: "请输入参数名称", trigger: "blur" }],
@@ -519,6 +528,7 @@
    formData.unit = "";
    formData.remark = "";
    formData.isRequired = 0;
    formData.createTime = new Date().toISOString().split("T")[0];
    dialogVisible.value = true;
  };
src/views/collaborativeApproval/purchaseApproval/index.vue
@@ -278,7 +278,7 @@
    },
    rules: {
      purchaseContractNumber: [
        { required: true, message: "请输入", trigger: "blur" },
        { required: false, message: "请输入", trigger: "blur" },
      ],
      projectName: [{ required: true, message: "请输入", trigger: "blur" }],
      supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
@@ -448,9 +448,7 @@
    productData.value = [];
    fileList.value = [];
    if (operationType.value == "add") {
      createPurchaseNo().then(res => {
        form.value.purchaseContractNumber = res.data;
      });
      form.value.purchaseContractNumber = "";
    }
    userListNoPage().then(res => {
      userList.value = res.data;
@@ -521,7 +519,7 @@
  }
  // 提交表单
  const submitForm = n => {
    proxy.$refs["formRef"].validate(valid => {
    proxy.$refs["formRef"].validate(async valid => {
      if (valid) {
        if (productData.value.length > 0) {
          form.value.productData = proxy.HaveJson(productData.value);
@@ -536,6 +534,21 @@
        form.value.tempFileIds = tempFileIds;
        form.value.type = 2;
        form.value.approvalStatus = n;
        // 如果采购合同号为空,则根据录入日期自动生成
        if (!form.value.purchaseContractNumber) {
          try {
            const purchaseNoRes = await createPurchaseNo(form.value.entryDate);
            if (purchaseNoRes?.data) {
              form.value.purchaseContractNumber = purchaseNoRes.data;
            }
          } catch (error) {
            console.error("生成采购合同号失败:", error);
            proxy.$modal.msgWarning("生成采购合同号失败");
            return;
          }
        }
        addOrEditPurchase(form.value).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
src/views/financialManagement/assets/fixedAssets.vue
@@ -82,7 +82,7 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="资产编号" prop="assetCode">
              <el-input v-model="form.assetCode" placeholder="系统自动生成" disabled />
              <el-input v-model="form.assetCode" placeholder="保存后自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -175,6 +175,13 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建时间" prop="createTime">
              <el-date-picker v-model="form.createTime" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
@@ -258,6 +265,7 @@
  keeper: "",
  status: "in_use",
  remark: "",
  createTime: "",
});
const form = reactive({
@@ -356,16 +364,14 @@
  getTableData();
};
const buildAssetCode = () => `GD${Date.now().toString().slice(-10)}`;
const add = () => {
  isEdit.value = false;
  isView.value = false;
  currentId.value = null;
  dialogTitle.value = "新增固定资产";
  Object.assign(form, createDefaultForm(), {
    assetCode: buildAssetCode(),
    purchaseDate: new Date().toISOString().split('T')[0],
    createTime: new Date().toISOString().split('T')[0],
  });
  dialogVisible.value = true;
};
src/views/financialManagement/assets/intangibleAssets.vue
@@ -83,7 +83,7 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="资产编号" prop="assetCode">
              <el-input v-model="form.assetCode" placeholder="系统自动生成" disabled />
              <el-input v-model="form.assetCode" placeholder="保存后自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -162,6 +162,13 @@
                <el-option label="闲置" value="idle" />
                <el-option label="已摊销完毕" value="amortized" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建时间" prop="createTime">
              <el-date-picker v-model="form.createTime" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" style="width: 100%;" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -250,6 +257,7 @@
  status: "in_use",
  description: "",
  remark: "",
  createTime: "",
});
const form = reactive({
@@ -354,16 +362,14 @@
  getTableData();
};
const buildAssetCode = () => `WX${Date.now().toString().slice(-10)}`;
const add = () => {
  isEdit.value = false;
  isView.value = false;
  currentId.value = null;
  dialogTitle.value = "新增无形资产";
  Object.assign(form, createDefaultForm(), {
    assetCode: buildAssetCode(),
    acquisitionDate: new Date().toISOString().split('T')[0],
    createTime: new Date().toISOString().split('T')[0],
  });
  dialogVisible.value = true;
};
src/views/financialManagement/payable/paymentApply.vue
@@ -97,7 +97,7 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="申请单号" prop="invoiceApplicationNo">
              <el-input v-model="form.invoiceApplicationNo" placeholder="系统自动生成" disabled />
              <el-input v-model="form.invoiceApplicationNo" placeholder="保存后自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -158,6 +158,18 @@
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建时间" prop="createTime">
              <el-date-picker
                v-model="form.createTime"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
                :disabled="isView"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款金额" prop="paymentAmount">
              <el-input-number
                v-model="form.paymentAmount"
@@ -217,7 +229,7 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款单号" prop="paymentNumber">
              <el-input v-model="paymentForm.paymentNumber" placeholder="系统自动生成" disabled />
              <el-input v-model="paymentForm.paymentNumber" placeholder="保存后自动生成" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -245,6 +257,17 @@
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建时间" prop="createTime">
              <el-date-picker
                v-model="paymentForm.createTime"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款金额" prop="paymentAmount">
              <el-input-number
@@ -407,6 +430,7 @@
  bankAccount: "",
  bankName: "",
  remark: "",
  createTime: "",
});
const paymentRules = {
@@ -429,6 +453,7 @@
  stockInRecordIds: [],
  inboundBatches: "",
  status: 0,
  createTime: "",
});
const rules = {
@@ -563,6 +588,7 @@
    stockInRecordIds,
    inboundBatches: formatInboundBatches(row.inboundBatches),
    status: normalizeStatus(row.status),
    createTime: row.createTime ?? "",
  });
};
@@ -580,6 +606,7 @@
  remark: row.remark ?? "",
  status: statusOverride !== undefined ? statusOverride : normalizeStatus(row.status),
  paymentAmount: Number(row.paymentAmount ?? row.amount ?? 0),
  createTime: row.createTime,
});
const buildSubmitPayload = (forUpdate = false) => {
@@ -703,6 +730,7 @@
    stockInRecordIds: [],
    inboundBatches: "",
    status: 0,
    createTime: new Date().toISOString().split("T")[0],
  });
  inboundBatchList.value = [];
  inboundBatchOptions.value = [];
@@ -781,6 +809,7 @@
    bankAccount: row.bankAccountNum ?? row.bankAccount ?? "",
    bankName: row.bankAccountName ?? row.bankName ?? "",
    remark: "",
    createTime: new Date().toISOString().split("T")[0],
  });
  paymentDialogVisible.value = true;
  nextTick(() => {
@@ -800,6 +829,7 @@
      paymentAmount: paymentForm.paymentAmount,
      paymentNumber: paymentForm.paymentNumber || "",
      remark: paymentForm.remark || "",
      createTime: paymentForm.createTime,
    })
      .then((res) => {
        if (res.code === 200) {
src/views/financialManagement/payable/purchaseIn.vue
@@ -98,7 +98,7 @@
      slot: "inboundDate",
    },
    { label: "产品名称", prop: "productName", minWidth: "140" },
    { label: "产品规格", prop: "specificationModel", minWidth: "140" },
    { label: "规格型号", prop: "specificationModel", minWidth: "140" },
    {
      label: "金额",
      prop: "inboundAmount",
src/views/financialManagement/receivable/invoiceApply.vue
@@ -98,7 +98,7 @@
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="申请单号" prop="applyCode">
              <el-input v-model="form.applyCode" placeholder="系统自动生成" disabled />
              <el-input v-model="form.applyCode" placeholder="保存后自动生成" disabled />
            </el-form-item>
          </el-col>
        </el-row>
@@ -180,6 +180,20 @@
            <el-form-item label="申请日期" prop="applyDate">
              <el-date-picker
                v-model="form.applyDate"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
                style="width: 100%;"
                :disabled="isView"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建时间" prop="createTime">
              <el-date-picker
                v-model="form.createTime"
                type="date"
                placeholder="选择日期"
                value-format="YYYY-MM-DD"
@@ -524,6 +538,7 @@
  applyDate: "",
  content: "",
  remark: "",
  createTime: "",
});
const rules = {
@@ -714,7 +729,7 @@
  isView.value = false;
  dialogTitle.value = "新增开票申请";
  Object.assign(form, {
    applyCode: "KP" + Date.now().toString().slice(-8),
    applyCode: "",
    customerId: "",
    outboundBatchNos: [],
    outboundBatches: "",
@@ -724,6 +739,7 @@
    applyDate: new Date().toISOString().split("T")[0],
    content: "",
    remark: "",
    createTime: new Date().toISOString().split("T")[0],
  });
  outboundBatchList.value = [];
  outboundBatchOptions.value = [];
@@ -752,6 +768,7 @@
    invoiceAmount: form.amount,
    taxRate: form.taxRate,
    status: 0,
    createTime: form.createTime,
  };
  if (forUpdate) {
    payload.id = currentId.value;
src/views/financialManagement/receivable/receipt.vue
@@ -111,7 +111,7 @@
            <el-form-item label="收款单号"
                          prop="receiptCode">
              <el-input v-model="form.receiptCode"
                        placeholder="系统自动生成"
                        placeholder="保存后自动生成"
                        disabled />
            </el-form-item>
          </el-col>
@@ -191,6 +191,17 @@
                           :label="item.label"
                           :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="创建时间"
                          prop="createTime">
              <el-date-picker v-model="form.createTime"
                              type="date"
                              placeholder="选择日期"
                              value-format="YYYY-MM-DD"
                              style="width: 100%;"
                              :disabled="isView" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -379,6 +390,7 @@
    stockOutRecordIds: [],
    outboundBatches: "",
    remark: "",
    createTime: "",
  });
  const rules = {
@@ -658,6 +670,7 @@
      collectionNumber: form.receiptCode || "",
      remark: form.remark || "",
      stockOutRecordIds: (form.stockOutRecordIds || []).join(","),
      createTime: form.createTime,
    };
    if (forUpdate) {
      payload.id = currentId.value;
@@ -746,7 +759,7 @@
    isView.value = false;
    dialogTitle.value = "新增收款";
    Object.assign(form, {
      receiptCode: "SK" + Date.now().toString().slice(-8),
      receiptCode: "",
      customerId: "",
      receiptDate: new Date().toISOString().split("T")[0],
      amount: 0,
@@ -754,6 +767,7 @@
      stockOutRecordIds: [],
      outboundBatches: "",
      remark: "",
      createTime: new Date().toISOString().split("T")[0],
    });
    outboundBatchList.value = [];
    outboundBatchOptions.value = [];
src/views/financialManagement/receivable/salesOut.vue
@@ -1,27 +1,33 @@
<template>
<!-- 销售出库 -->
  <div class="app-container">
    <el-form :model="filters" :inline="true">
    <el-form :model="filters"
             :inline="true">
      <el-form-item label="出库单号:">
        <el-input v-model="filters.outboundBatches" placeholder="请输入出库单号" clearable style="width: 200px;" />
        <el-input v-model="filters.outboundBatches"
                  placeholder="请输入出库单号"
                  clearable
                  style="width: 200px;" />
      </el-form-item>
      <el-form-item label="客户名称:">
        <el-input v-model="filters.customerName" placeholder="请输入客户名称" clearable style="width: 200px;" />
        <el-input v-model="filters.customerName"
                  placeholder="请输入客户名称"
                  clearable
                  style="width: 200px;" />
      </el-form-item>
      <el-form-item label="出库日期:">
        <el-date-picker
          v-model="filters.dateRange"
        <el-date-picker v-model="filters.dateRange"
          value-format="YYYY-MM-DD"
          format="YYYY-MM-DD"
          type="daterange"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          clearable
        />
                        clearable />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button type="primary"
                   @click="onSearch">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
@@ -29,11 +35,11 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button @click="handleOut" icon="Download">导出</el-button>
          <el-button @click="handleOut"
                     icon="Download">导出</el-button>
        </div>
      </div>
      <PIMTable
        rowKey="id"
      <PIMTable rowKey="id"
        :column="columns"
        :tableData="dataList"
        :tableLoading="tableLoading"
@@ -42,8 +48,7 @@
          size: pagination.pageSize,
          total: pagination.total,
        }"
        @pagination="changePage"
      />
                @pagination="changePage" />
    </div>
  </div>
</template>
@@ -76,13 +81,19 @@
  { label: "客户名称", prop: "customerName", minWidth: "180" },
  { label: "出库日期", prop: "shippingDate", width: "170" },
  { label: "产品名称", prop: "productName", minWidth: "140" },
  { label: "产品规格", prop: "specificationModel", minWidth: "140" },
    { label: "规格型号", prop: "specificationModel", minWidth: "140" },
  {
    label: "金额",
    prop: "outboundAmount",
    minWidth: "120",
    align: "right",
    formatData: (val) => (val === null || val === undefined || val === "" ? "" : Number(val).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 })),
      formatData: val =>
        val === null || val === undefined || val === ""
          ? ""
          : Number(val).toLocaleString("zh-CN", {
              minimumFractionDigits: 2,
              maximumFractionDigits: 2,
            }),
  },
  { label: "发货编号", prop: "shippingNo", minWidth: "140" },
  { label: "销售订单号", prop: "salesContractNo", minWidth: "150" },
@@ -115,7 +126,7 @@
    current: pagination.currentPage,
    size: pagination.pageSize,
  })
    .then((res) => {
      .then(res => {
      const ok = res.code === 200 || res.code === 0;
      if (ok && res.data) {
        pagination.total = res.data.total ?? 0;
src/views/inventoryManagement/dispatchLog/Record.vue
@@ -1,14 +1,14 @@
<template>
  <div>
    <div class="search_form" style="margin-bottom: 10px">
      <el-form
          ref="searchFormRef"
    <div class="search_form"
         style="margin-bottom: 10px">
      <el-form ref="searchFormRef"
          :model="searchForm"
          class="demo-form-inline"
      >
               class="demo-form-inline">
        <el-row :gutter="20">
          <el-col :span="4">
            <el-form-item label="出库日期" prop="timeStr">
            <el-form-item label="出库日期"
                          prop="timeStr">
              <el-date-picker v-model="searchForm.timeStr"
                              type="date"
                              placeholder="请选择日期"
@@ -18,16 +18,17 @@
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="产品大类" prop="productName">
            <el-form-item label="产品大类"
                          prop="productName">
              <el-input v-model="searchForm.productName"
                        style="width: 240px"
                        placeholder="请输入"
                        clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="产品规格" prop="model">
            <el-form-item label="规格型号"
                          prop="model">
              <el-input v-model="searchForm.model"
                        style="width: 240px"
                        placeholder="请输入"
@@ -35,7 +36,8 @@
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="批号" prop="batchNo">
            <el-form-item label="批号"
                          prop="batchNo">
              <el-input v-model="searchForm.batchNo"
                        style="width: 240px"
                        placeholder="请输入"
@@ -43,29 +45,26 @@
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="来源" prop="recordType">
              <el-select
                  v-model="searchForm.recordType"
            <el-form-item label="来源"
                          prop="recordType">
              <el-select v-model="searchForm.recordType"
                  style="width: 240px"
                  placeholder="请选择"
                  clearable
              >
                <el-option
                    v-for="item in stockRecordTypeOptions"
                         clearable>
                <el-option v-for="item in stockRecordTypeOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                />
                           :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
          <!-- 按钮 -->
          <el-col :span="4">
            <el-form-item>
              <el-button type="primary" @click="getList">
              <el-button type="primary"
                         @click="getList">
                搜索
              </el-button>
              <el-button @click="resetSearch">
                重置
              </el-button>
@@ -75,77 +74,81 @@
      </el-form>
    </div>
    <div class="actions">
      <el-button type="primary" @click="handleBatchApprove">审批</el-button>
      <el-button type="primary"
                 @click="handleBatchApprove">审批</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>
      <el-button type="danger"
                 plain
                 @click="handleDelete">删除</el-button>
      <el-button type="primary"
                 plain
                 @click="handlePrint">打印</el-button>
    </div>
    <div class="table_list">
      <el-table
        :data="tableData"
      <el-table :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys"
        :row-key="(row) => row.id"
        style="width: 100%"
        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="出库批次"
                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="outboundBatches"
          min-width="100"
          show-overflow-tooltip
        />
        <el-table-column
          label="出库日期"
                         show-overflow-tooltip />
        <el-table-column label="出库日期"
          prop="createTime"
          show-overflow-tooltip
        />
        <el-table-column
          label="产品大类"
                         show-overflow-tooltip />
        <el-table-column label="产品大类"
          prop="productName"
          show-overflow-tooltip
        />
        <el-table-column label="规格型号" prop="model" show-overflow-tooltip />
        <el-table-column label="批号" prop="batchNo" show-overflow-tooltip />
        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
        <el-table-column
          label="出库数量"
                         show-overflow-tooltip />
        <el-table-column label="规格型号"
                         prop="model"
                         show-overflow-tooltip />
        <el-table-column label="批号"
                         prop="batchNo"
                         show-overflow-tooltip />
        <el-table-column label="单位"
                         prop="unit"
                         show-overflow-tooltip />
        <el-table-column label="出库数量"
          prop="stockOutNum"
          show-overflow-tooltip
        />
        <el-table-column label="出库人" prop="createBy" show-overflow-tooltip />
        <el-table-column label="来源" prop="recordType" show-overflow-tooltip>
                         show-overflow-tooltip />
        <el-table-column label="出库人"
                         prop="createBy"
                         show-overflow-tooltip />
        <el-table-column label="来源"
                         prop="recordType"
                         show-overflow-tooltip>
          <template #default="scope">
            {{ getRecordType(scope.row.recordType) }}
          </template>
        </el-table-column>
        <el-table-column
          label="审批状态"
        <el-table-column label="审批状态"
          prop="approvalStatus"
          show-overflow-tooltip
        >
                         show-overflow-tooltip>
          <template #default="scope">
            <el-tag
              :type="getApprovalStatusTagType(scope.row.approvalStatus)"
              size="small"
            >
            <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)"
                    size="small">
              {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
            </el-tag>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
      <pagination v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
                  @pagination="paginationChange" />
    </div>
  </div>
</template>
@@ -211,9 +214,9 @@
  searchFormRef.value?.resetFields();
  page.current = 1;
  getList();
}
  };
const paginationChange = (obj) => {
  const paginationChange = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
@@ -225,10 +228,10 @@
    ...page,
    topParentProductId: props.topParentProductId,
  })
    .then((res) => {
      .then(res => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      tableData.value.map((item) => {
        tableData.value.map(item => {
        item.children = [];
      });
      total.value = res.data.total;
@@ -238,9 +241,9 @@
    });
};
const getRecordType = (recordType) => {
  const getRecordType = recordType => {
  return (
    stockRecordTypeOptions.value.find((item) => item.value === recordType)
      stockRecordTypeOptions.value.find(item => item.value === recordType)
      ?.label || ""
  );
};
@@ -258,7 +261,7 @@
  REJECTED: "驳回",
};
const getApprovalStatusLabel = (status) => {
  const getApprovalStatusLabel = status => {
  if (status === null || status === undefined || status === "") {
    return "待审批";
  }
@@ -266,7 +269,7 @@
};
// 通过/驳回固定色;其余(含待审批、空值、未映射但文案为待审批)统一用 warning 预警色
const getApprovalStatusTagType = (status) => {
  const getApprovalStatusTagType = status => {
  if (
    status === 1 ||
    status === "1" ||
@@ -287,20 +290,20 @@
// 获取来源类型选项
const fetchStockRecordTypeOptions = () => {
  if (props.type === "0") {
    findAllQualifiedStockOutRecordTypeOptions().then((res) => {
      findAllQualifiedStockOutRecordTypeOptions().then(res => {
      stockRecordTypeOptions.value = res.data;
    });
    return;
  }
  findAllUnQualifiedStockOutRecordTypeOptions().then((res) => {
    findAllUnQualifiedStockOutRecordTypeOptions().then(res => {
    stockRecordTypeOptions.value = res.data;
  });
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  // 过滤掉子数据
  selectedRows.value = selection.filter((item) => item.id);
    selectedRows.value = selection.filter(item => item.id);
  console.log("selection", selectedRows.value);
};
const expandedRowKeys = ref([]);
@@ -310,7 +313,7 @@
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  const ids = selectedRows.value.map((item) => item.id);
    const ids = selectedRows.value.map(item => item.id);
  ElMessageBox.confirm("请选择审批结果", "审批", {
    confirmButtonText: "通过",
    cancelButtonText: "驳回",
@@ -327,7 +330,7 @@
          proxy.$modal.msgError("审批通过失败");
        });
    })
    .catch((action) => {
      .catch(action => {
      if (action === "cancel") {
        batchApproveStockOutRecords({ ids, approvalStatus: 2 })
          .then(() => {
@@ -366,7 +369,7 @@
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -377,7 +380,7 @@
    type: "warning",
  })
    .then(() => {
      delPendingStockOut(ids).then((res) => {
        delPendingStockOut(ids).then(res => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
@@ -656,7 +659,7 @@
};
// 格式化日期
const formatDate = (dateString) => {
  const formatDate = dateString => {
  if (!dateString) return getCurrentDate();
  const date = new Date(dateString);
  const year = date.getFullYear();
@@ -666,7 +669,7 @@
};
// 格式化日期时间
const formatDateTime = (date) => {
  const formatDateTime = date => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
src/views/inventoryManagement/receiptManagement/Record.vue
@@ -1,14 +1,14 @@
<template>
  <div>
    <div class="search_form" style="margin-bottom: 10px;">
      <el-form
          ref="searchFormRef"
    <div class="search_form"
         style="margin-bottom: 10px;">
      <el-form ref="searchFormRef"
          :model="searchForm"
          class="demo-form-inline"
      >
               class="demo-form-inline">
        <el-row :gutter="20">
          <el-col :span="4">
            <el-form-item label="入库日期" prop="timeStr">
            <el-form-item label="入库日期"
                          prop="timeStr">
              <el-date-picker v-model="searchForm.timeStr"
                              type="date"
                              placeholder="请选择日期"
@@ -18,16 +18,17 @@
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="产品大类" prop="productName">
            <el-form-item label="产品大类"
                          prop="productName">
              <el-input v-model="searchForm.productName"
                        style="width: 240px"
                        placeholder="请输入"
                        clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="产品规格" prop="model">
            <el-form-item label="规格型号"
                          prop="model">
              <el-input v-model="searchForm.model"
                        style="width: 240px"
                        placeholder="请输入"
@@ -35,7 +36,8 @@
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="批号" prop="batchNo">
            <el-form-item label="批号"
                          prop="batchNo">
              <el-input v-model="searchForm.batchNo"
                        style="width: 240px"
                        placeholder="请输入"
@@ -43,7 +45,8 @@
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="来源" prop="recordType">
            <el-form-item label="来源"
                          prop="recordType">
              <el-select v-model="searchForm.recordType"
                         style="width: 240px"
                         placeholder="请选择"
@@ -58,10 +61,10 @@
          <!-- 按钮 -->
          <el-col :span="4">
            <el-form-item>
              <el-button type="primary" @click="getList">
              <el-button type="primary"
                         @click="getList">
                搜索
              </el-button>
              <el-button @click="resetSearch">
                重置
              </el-button>
@@ -132,8 +135,7 @@
            {{ getRecordType(scope.row.recordType) }}
          </template>
        </el-table-column>
        <el-table-column
            v-if="showSourceOrderNoColumn"
        <el-table-column v-if="showSourceOrderNoColumn"
            label="源单号"
            width="150"
            prop="sourceOrderNo"
@@ -146,7 +148,8 @@
                         prop="approvalStatus"
                         show-overflow-tooltip>
          <template #default="scope">
            <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
            <el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)"
                    size="small">
              {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
            </el-tag>
          </template>
@@ -190,13 +193,13 @@
  type: {
    type: String,
    required: true,
    default: '0'
      default: "0",
  },
  topParentProductId: {
    type: [String, Number],
    default: undefined
  }
})
      default: undefined,
    },
  });
const tableData = ref([]);
const selectedRows = ref([]);
@@ -225,11 +228,14 @@
  searchFormRef.value?.resetFields();
  page.current = 1;
  getList();
}
  };
const getRecordType = (recordType) => {
  return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
}
  const getRecordType = recordType => {
    return (
      stockRecordTypeOptions.value.find(item => item.value === recordType)
        ?.label || ""
    );
  };
const approvalStatusLabelMap = {
  0: "待审批",
@@ -244,7 +250,7 @@
};
approvalStatusLabelMap[3] = "待确认";
const getApprovalStatusLabel = (status) => {
  const getApprovalStatusLabel = status => {
  if (status === null || status === undefined || status === "") {
    return "待审批";
  }
@@ -252,32 +258,64 @@
};
// 通过/驳回固定色;其余(含待审批、空值、未映射但文案为待审批)统一用 warning 预警色
const getApprovalStatusTagType = (status) => {
  if (status === 1 || status === "1" || status === "approved" || status === "APPROVED") return "success";
  if (status === 2 || status === "2" || status === "rejected" || status === "REJECTED") return "danger";
  const getApprovalStatusTagType = status => {
    if (
      status === 1 ||
      status === "1" ||
      status === "approved" ||
      status === "APPROVED"
    )
      return "success";
    if (
      status === 2 ||
      status === "2" ||
      status === "rejected" ||
      status === "REJECTED"
    )
      return "danger";
  return "warning";
};
const isPendingApproval = status => {
  return status === 0 || status === "0" || status === "pending" || status === "PENDING" || status === null || status === undefined || status === "";
    return (
      status === 0 ||
      status === "0" ||
      status === "pending" ||
      status === "PENDING" ||
      status === null ||
      status === undefined ||
      status === ""
    );
};
const isRejectedApproval = status => {
  return status === 2 || status === "2" || status === "rejected" || status === "REJECTED";
    return (
      status === 2 ||
      status === "2" ||
      status === "rejected" ||
      status === "REJECTED"
    );
};
const isRowSelectable = row => {
  return isPendingApproval(row?.approvalStatus) || isRejectedApproval(row?.approvalStatus);
    return (
      isPendingApproval(row?.approvalStatus) ||
      isRejectedApproval(row?.approvalStatus)
    );
};
const canBatchApprove = computed(() => {
  return selectedRows.value.length > 0
      && selectedRows.value.every(row => isPendingApproval(row.approvalStatus));
    return (
      selectedRows.value.length > 0 &&
      selectedRows.value.every(row => isPendingApproval(row.approvalStatus))
    );
});
const canReverseApprove = computed(() => {
  return selectedRows.value.length > 0
      && selectedRows.value.every(row => isRejectedApproval(row.approvalStatus));
    return (
      selectedRows.value.length > 0 &&
      selectedRows.value.every(row => isRejectedApproval(row.approvalStatus))
    );
});
const canDelete = computed(() => canBatchApprove.value);
@@ -286,7 +324,7 @@
  return topParentProductId === 276 || topParentProductId === 278;
});
const formatSourceOrderNo = (value) => {
  const formatSourceOrderNo = value => {
  const text = String(value ?? "").trim();
  return text || "--";
};
@@ -299,33 +337,44 @@
const getList = () => {
  tableLoading.value = true;
  getStockInRecordListPage(Object.assign({}, {...searchForm.value, ...page,  topParentProductId: props.topParentProductId}))
    getStockInRecordListPage(
      Object.assign(
        {},
        {
          ...searchForm.value,
          ...page,
          topParentProductId: props.topParentProductId,
        }
      )
    )
      .then(res => {
        tableData.value = res.data.records;
        total.value = res.data.total || 0;
      }).finally(() => {
    tableLoading.value = false;
  })
      .finally(() => {
        tableLoading.value = false;
      });
};
// 获取来源类型选项
const fetchStockRecordTypeOptions = () => {
  if (props.type === '0') {
    findAllQualifiedStockInRecordTypeOptions()
        .then(res => {
    if (props.type === "0") {
      findAllQualifiedStockInRecordTypeOptions().then(res => {
          stockRecordTypeOptions.value = res.data;
        })
    return
      });
      return;
  }
  // findAllUnQualifiedStockInRecordTypeOptions()
  //     .then(res => {
  //       stockRecordTypeOptions.value = res.data;
  //     })
}
  };
// 表格选择数据
const handleSelectionChange = selection => {
  selectedRows.value = selection.filter(item => item.id && isRowSelectable(item));
    selectedRows.value = selection.filter(
      item => item.id && isRowSelectable(item)
    );
};
const expandedRowKeys = ref([]);
@@ -378,7 +427,7 @@
              proxy.$modal.msgError("审批通过失败");
            });
      })
      .catch((action) => {
      .catch(action => {
        if (action === "cancel") {
          batchApproveStockInRecords({ids, approvalStatus: 2})
              .then(() => {
@@ -403,7 +452,11 @@
  })
      .then(() => {
        // 根据不同的 tab 类型调用不同的导出接口
        proxy.download("/stockInRecord/exportStockInRecord", {type: props.type}, props.type === '0' ? "合格入库.xlsx" : "不合格入库.xlsx");
        proxy.download(
          "/stockInRecord/exportStockInRecord",
          { type: props.type },
          props.type === "0" ? "合格入库.xlsx" : "不合格入库.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
src/views/inventoryManagement/stockManagement/Record.vue
@@ -1,23 +1,22 @@
<template>
  <div>
    <div class="search_form mb10">
      <el-form
          ref="searchFormRef"
      <el-form ref="searchFormRef"
          :model="searchForm"
          class="demo-form-inline"
      >
               class="demo-form-inline">
        <el-row :gutter="20">
          <el-col :span="4">
            <el-form-item label="产品大类" prop="productName">
            <el-form-item label="产品大类"
                          prop="productName">
              <el-input v-model="searchForm.productName"
                        style="width: 240px"
                        placeholder="请输入"
                        clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="产品规格" prop="model">
            <el-form-item label="规格型号"
                          prop="model">
              <el-input v-model="searchForm.model"
                        style="width: 240px"
                        placeholder="请输入"
@@ -25,7 +24,8 @@
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="批号" prop="batchNo">
            <el-form-item label="批号"
                          prop="batchNo">
              <el-input v-model="searchForm.batchNo"
                        style="width: 240px"
                        placeholder="请输入"
@@ -35,10 +35,10 @@
          <!-- 按钮 -->
          <el-col :span="4">
            <el-form-item>
              <el-button type="primary" @click="getList">
              <el-button type="primary"
                         @click="getList">
                搜索
              </el-button>
              <el-button @click="resetSearch">
                重置
              </el-button>
@@ -47,23 +47,19 @@
        </el-row>
      </el-form>
      <div>
        <el-button type="primary" @click="isShowNewModal = true"
          >新增库存</el-button
        >
        <el-button
          type="info"
        <el-button type="primary"
                   @click="isShowNewModal = true">新增库存</el-button>
        <el-button type="info"
          plain
          icon="Upload"
          @click="isShowImportModal = true"
        >
                   @click="isShowImportModal = true">
          导入库存
        </el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <el-table
        :data="tableData"
      <el-table :data="tableData"
        border
        v-loading="tableLoading"
        @selection-change="handleSelectionChange"
@@ -71,112 +67,92 @@
        :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="产品名称"
                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="batchNo" show-overflow-tooltip />
        <el-table-column
          label="合格库存数量"
                         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="batchNo"
                         show-overflow-tooltip />
        <el-table-column label="合格库存数量"
          prop="qualifiedQuantity"
          show-overflow-tooltip
        />
        <el-table-column
          label="不合格库存数量"
                         show-overflow-tooltip />
        <el-table-column label="不合格库存数量"
          prop="unQualifiedQuantity"
          show-overflow-tooltip
        />
        <el-table-column
          label="合格冻结数量"
                         show-overflow-tooltip />
        <el-table-column label="合格冻结数量"
          prop="qualifiedLockedQuantity"
          show-overflow-tooltip
        />
        <el-table-column
          label="不合格冻结数量"
                         show-overflow-tooltip />
        <el-table-column label="不合格冻结数量"
          prop="unQualifiedLockedQuantity"
          show-overflow-tooltip
        />
        <el-table-column
          label="库存预警数量"
                         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="最近更新时间"
                         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"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
          label="操作"
          min-width="80"
          align="center"
        >
                         align="center">
          <template #default="scope">
            <el-button
              link
            <el-button link
              type="primary"
              @click="showDetailModal(scope.row)"
              >详情</el-button
            >
                       @click="showDetailModal(scope.row)">详情</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
      <pagination v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
                  @pagination="paginationChange" />
    </div>
    <batch-no-qty-detail
      v-if="isShowDetailModal"
    <batch-no-qty-detail v-if="isShowDetailModal"
      v-model:visible="isShowDetailModal"
      :record="record"
      @subtract="handleDetailSubtract"
      @frozen="handleDetailFrozen"
      @thaw="handleDetailThaw"
    />
    <new-stock-inventory
      v-if="isShowNewModal"
                         @thaw="handleDetailThaw" />
    <new-stock-inventory v-if="isShowNewModal"
      v-model:visible="isShowNewModal"
      :top-product-parent-id="props.productId"
      @completed="handleQuery"
    />
    <subtract-stock-inventory
      v-if="isShowSubtractModal"
                         @completed="handleQuery" />
    <subtract-stock-inventory v-if="isShowSubtractModal"
      v-model:visible="isShowSubtractModal"
      :record="record"
      :type="record.stockType"
      @completed="handleQuery"
    />
                              @completed="handleQuery" />
    <!-- 导入库存-->
    <import-stock-inventory
      v-if="isShowImportModal"
    <import-stock-inventory v-if="isShowImportModal"
      v-model:visible="isShowImportModal"
      type="qualified"
      @uploadSuccess="handleQuery"
    />
                            @uploadSuccess="handleQuery" />
    <!-- 冻结/解冻库存-->
    <frozen-and-thaw-stock-inventory
      v-if="isShowFrozenAndThawModal"
    <frozen-and-thaw-stock-inventory v-if="isShowFrozenAndThawModal"
      v-model:visible="isShowFrozenAndThawModal"
      :record="record"
      :operation-type="operationType"
      :type="record.stockType"
      @completed="handleQuery"
    />
                                     @completed="handleQuery" />
  </div>
</template>
@@ -245,7 +221,7 @@
  searchFormRef.value?.resetFields();
  page.current = 1;
  getList();
}
  };
// 查询列表
/** 搜索按钮操作 */
@@ -253,7 +229,7 @@
  page.current = 1;
  getList();
};
const paginationChange = (obj) => {
  const paginationChange = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
@@ -261,7 +237,7 @@
const getList = () => {
  tableLoading.value = true;
  getStockInventoryListPageCombined({ ...searchForm.value, ...page })
    .then((res) => {
      .then(res => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      total.value = res.data.total;
@@ -273,7 +249,7 @@
    });
};
const handleFileSuccess = (response) => {
  const handleFileSuccess = response => {
  const { code, msg } = response;
  if (code == 200) {
    ElMessage({ message: "导入成功", type: "success" });
@@ -285,13 +261,13 @@
};
// 点击领用
const showSubtractModal = (row) => {
  const showSubtractModal = row => {
  record.value = row;
  isShowSubtractModal.value = true;
};
// 点击详情
const showDetailModal = (row) => {
  const showDetailModal = row => {
  if (!row?.productId || !row?.productModelId) {
    proxy.$modal.msgError("当前数据缺少产品ID或规格型号ID");
    return;
@@ -300,39 +276,39 @@
  isShowDetailModal.value = true;
};
const handleDetailSubtract = (row) => {
  const handleDetailSubtract = row => {
  isShowDetailModal.value = false;
  showSubtractModal(row);
};
const handleDetailFrozen = (row) => {
  const handleDetailFrozen = row => {
  isShowDetailModal.value = false;
  showFrozenModal(row);
};
const handleDetailThaw = (row) => {
  const handleDetailThaw = row => {
  isShowDetailModal.value = false;
  showThawModal(row);
};
// 点击冻结
const showFrozenModal = (row) => {
  const showFrozenModal = row => {
  record.value = row;
  isShowFrozenAndThawModal.value = true;
  operationType.value = "frozen";
};
// 点击解冻
const showThawModal = (row) => {
  const showThawModal = row => {
  record.value = row;
  isShowFrozenAndThawModal.value = true;
  operationType.value = "thaw";
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  // 过滤掉子数据
  selectedRows.value = selection.filter((item) => item.id);
    selectedRows.value = selection.filter(item => item.id);
  console.log("selection", selectedRows.value);
};
const expandedRowKeys = ref([]);
src/views/officeProcessAutomation/ApproveManage/approve-list/approveListConstants.js
@@ -1,21 +1,7 @@
import {
  createEmptyNode,
  formatDisplayTime,
  mapNodesFromApi,
  mapSignModeFromApi,
  mapSignModeToApi,
  normalizeFlowNodes,
  nodeSignModeLabel,
} from "../approve-template/approveTemplateConstants.js";
import { createEmptyNode, formatDisplayTime, mapNodesFromApi, mapSignModeFromApi, mapSignModeToApi, normalizeFlowNodes, nodeSignModeLabel } from "../approve-template/approveTemplateConstants.js";
import { buildFormPayloadFromFields, parseFormConfigToData } from "../approve-template/formConfigUtils.js";
import {
  isDynamicOptionSource,
  resolveSelectDisplayLabel,
} from "../approve-template/selectOptionSource.js";
import {
  appendDotNotationQuery,
  buildApprovalInstanceSearchDto,
} from "../approve-shared/approvalInstanceListSearch.js";
import { isDynamicOptionSource, resolveSelectDisplayLabel } from "../approve-template/selectOptionSource.js";
import { appendDotNotationQuery, buildApprovalInstanceSearchDto } from "../approve-shared/approvalInstanceListSearch.js";
/** 审批类型(与后端字段 approvalType 对齐,后期可同步) */
export const APPROVAL_TYPE_OPTIONS = [
@@ -80,24 +66,11 @@
  if (upper === "APPROVED" || upper === "APPROVE" || upper === "PASS" || upper === "AGREE") {
    return "approved";
  }
  if (
    upper === "REJECTED" ||
    upper === "REJECT" ||
    upper === "REFUSE" ||
    upper === "REFUSED" ||
    upper === "DENIED"
  ) {
  if (upper === "REJECTED" || upper === "REJECT" || upper === "REFUSE" || upper === "REFUSED" || upper === "DENIED") {
    return "rejected";
  }
  if (upper === "CANCELLED" || upper === "CANCEL" || upper === "REVOKED") return "cancelled";
  if (
    upper === "PENDING" ||
    upper === "IN_PROGRESS" ||
    upper === "PROCESSING" ||
    upper === "RUNNING" ||
    upper === "WAIT" ||
    upper === "WAITING"
  ) {
  if (upper === "PENDING" || upper === "IN_PROGRESS" || upper === "PROCESSING" || upper === "RUNNING" || upper === "WAIT" || upper === "WAITING") {
    return "pending";
  }
  if (s.includes("草稿")) return "draft";
@@ -134,13 +107,9 @@
  }
  const tasks = row.tasks;
  if (Array.isArray(tasks) && tasks.length) {
    const rejected = tasks.some((t) =>
      normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "rejected"
    );
    const rejected = tasks.some(t => normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "rejected");
    if (rejected) return "REJECTED";
    const allApproved = tasks.every((t) =>
      normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "approved"
    );
    const allApproved = tasks.every(t => normalizeApprovalStatusKey(t?.status ?? t?.taskStatus) === "approved");
    if (allApproved) return "APPROVED";
  }
  return "";
@@ -175,7 +144,7 @@
/** 后端 records → 时间线展示结构 */
export function mapRecordsFromApi(records) {
  const list = Array.isArray(records) ? records : [];
  return list.map((r) => ({
  return list.map(r => ({
    id: r.id,
    operatorName: r.approverName || r.operatorName || r.createUserName || "",
    result: mapRecordResultFromApi(r.approveAction ?? r.action ?? r.status),
@@ -198,7 +167,7 @@
  const list = Array.isArray(tasks) ? tasks : [];
  if (!list.length) return [];
  const byLevel = new Map();
  list.forEach((t) => {
  list.forEach(t => {
    const level = Number(t.levelNo ?? t.taskLevel ?? t.nodeOrder ?? 1);
    if (!byLevel.has(level)) {
      byLevel.set(level, {
@@ -226,16 +195,14 @@
      node.signMode = mapSignModeFromApi(t.approveType);
    }
  });
  return [...byLevel.entries()]
    .sort(([a], [b]) => a - b)
    .map(([, node]) => node);
  return [...byLevel.entries()].sort(([a], [b]) => a - b).map(([, node]) => node);
}
/** 页面 flowNodes → 后端 tasks */
export function mapFlowNodesToTasks(flowNodes, { instanceId, templateId } = {}) {
  const nodes = normalizeFlowNodes(flowNodes);
  const tasks = [];
  nodes.forEach((n) => {
  nodes.forEach(n => {
    const levelNo = n.nodeOrder ?? 1;
    const approveType = mapSignModeToApi(n.signMode);
    n.approvers.forEach((a, idx) => {
@@ -278,7 +245,7 @@
    return String(val);
  }
  if (field?.type === "select" && field.options?.length) {
    const hit = field.options.find((o) => String(o.value) === String(val));
    const hit = field.options.find(o => String(o.value) === String(val));
    return hit?.label || String(val);
  }
  if (Array.isArray(val)) return val.join(" 至 ");
@@ -298,8 +265,8 @@
  };
  if (!fields.length && Object.keys(formPayload).length) {
    fields = Object.keys(formPayload)
      .filter((k) => k && k !== "summary")
      .map((k) => ({
      .filter(k => k && k !== "summary")
      .map(k => ({
        key: k,
        label: k,
        type: guessFieldTypeFromValue(formPayload[k]),
@@ -366,11 +333,7 @@
export function buildInstanceDto({ submitForm, activeTemplate, userStore, flowNodes, existingRow }) {
  const payload = submitForm?.formPayload || {};
  const tpl = activeTemplate || {};
  const title =
    String(payload.summary || payload.title || "").trim() ||
    tpl.label ||
    submitForm?.templateName ||
    "审批申请";
  const title = String(payload.summary || payload.title || "").trim() || tpl.label || submitForm?.templateName || "审批申请";
  const templateId = submitForm?.templateId || tpl.templateId;
  const instanceId = existingRow?.id ?? submitForm?.instanceId;
  const taskList = mapFlowNodesToTasks(flowNodes || submitForm?.flowNodes, {
@@ -388,20 +351,13 @@
    tasks: taskList,
  };
  const attachments =
    (Array.isArray(submitForm?.storageBlobDTOs) && submitForm.storageBlobDTOs.length
      ? submitForm.storageBlobDTOs
      : null) || tpl.storageBlobDTOs;
  const attachments = (Array.isArray(submitForm?.storageBlobDTOs) && submitForm.storageBlobDTOs.length ? submitForm.storageBlobDTOs : null) || tpl.storageBlobDTOs;
  if (attachments?.length) dto.storageBlobDTOs = attachments;
  if (isUpdate) {
    dto.id = existingRow?.id ?? submitForm?.instanceId;
    dto.instanceNo = existingRow?.instanceNo ?? submitForm?.instanceNo ?? "";
    dto.status =
      submitForm?.saveStatusApi ||
      existingRow?.statusRaw ||
      mapInstanceStatusToApi(existingRow?.approvalStatus) ||
      "PENDING";
    dto.status = submitForm?.saveStatusApi || existingRow?.statusRaw || mapInstanceStatusToApi(existingRow?.approvalStatus) || "PENDING";
    dto.currentLevel = existingRow?.currentLevel ?? submitForm?.currentLevel ?? 1;
    dto.applicantId = existingRow?.applicantId ?? existingRow?.applicantNo;
    dto.applicantName = existingRow?.applicantName || "";
@@ -440,7 +396,7 @@
/** 页面 approvalStatus → 后端 status */
export function mapInstanceStatusToApi(approvalStatus) {
  const key = normalizeApprovalStatusKey(approvalStatus);
  const hit = APPROVAL_STATUS_OPTIONS.find((x) => x.value === key);
  const hit = APPROVAL_STATUS_OPTIONS.find(x => x.value === key);
  return hit?.api || "PENDING";
}
@@ -463,9 +419,7 @@
  const resolved = resolveInstanceFormFields(row);
  const { fields, formPayload, templateSnapshot } = resolved;
  const tasks = Array.isArray(row.tasks) ? row.tasks : [];
  const flowNodes = tasks.length
    ? mapTasksToFlowNodes(tasks)
    : mapNodesFromApi(row.nodes || row.flowNodes);
  const flowNodes = tasks.length ? mapTasksToFlowNodes(tasks) : mapNodesFromApi(row.nodes || row.flowNodes);
  const approvalRecords = mapRecordsFromApi(row.records);
  return {
    id: row.id,
@@ -496,12 +450,13 @@
    templateSnapshot,
    tasks,
    records: Array.isArray(row.records) ? row.records : [],
    storageBlobVOList: row.storageBlobVOList || [],
    storageBlobDTOs: row.storageBlobVOList || row.storageBlobDTOs || [],
    flowNodes,
    approvalFlowNodes: [],
    currentNodeIndex: 0,
    approvalRecords,
    rejectReason:
      approvalRecords.find((r) => r.result === "rejected")?.opinion || "",
    rejectReason: approvalRecords.find(r => r.result === "rejected")?.opinion || "",
  };
}
@@ -524,12 +479,7 @@
  };
}
export function buildApprovalInstanceListParams({
  page,
  searchForm,
  businessType,
  extraParams,
}) {
export function buildApprovalInstanceListParams({ page, searchForm, businessType, extraParams }) {
  const dto = buildApprovalInstanceSearchDto(searchForm, extraParams);
  const bizType = businessType ?? searchForm?.businessType;
  if (bizType != null && bizType !== "") {
@@ -548,11 +498,11 @@
}
export function approvalTypeLabel(v) {
  return APPROVAL_TYPE_OPTIONS.find((x) => x.value === v)?.label || v || "—";
  return APPROVAL_TYPE_OPTIONS.find(x => x.value === v)?.label || v || "—";
}
export function approvalTypeStyle(v) {
  const hit = APPROVAL_TYPE_OPTIONS.find((x) => x.value === v);
  const hit = APPROVAL_TYPE_OPTIONS.find(x => x.value === v);
  if (!hit) return {};
  return {
    backgroundColor: hit.cellBg,
@@ -563,7 +513,7 @@
export function approvalStatusLabel(v) {
  const key = normalizeApprovalStatusKey(v);
  return APPROVAL_STATUS_OPTIONS.find((x) => x.value === key)?.label || "—";
  return APPROVAL_STATUS_OPTIONS.find(x => x.value === key)?.label || "—";
}
/** 业务申请页状态文案:PENDING→进行中 APPROVED→已完成 REJECTED→已驳回 */
@@ -582,9 +532,7 @@
 * 进行中(PENDING)、已完成(APPROVED) 不可修改;已驳回、已撤销等可修改
 */
export function canEditBusinessInstanceRow(row) {
  const key = normalizeApprovalStatusKey(
    row?.approvalStatus ?? row?.statusRaw ?? row?.status
  );
  const key = normalizeApprovalStatusKey(row?.approvalStatus ?? row?.statusRaw ?? row?.status);
  return key !== "pending" && key !== "approved";
}
@@ -609,12 +557,8 @@
/** 列表行 → 编辑表单(仅用行数据回显) */
export function buildEditFormFromInstanceRow(row) {
  const { fields, formPayload, templateSnapshot } = resolveInstanceFormFields(row);
  const normalized = normalizeFlowNodes(
    row?.flowNodes?.length ? row.flowNodes : mapTasksToFlowNodes(row?.tasks)
  );
  const flowNodes = normalized.length
    ? JSON.parse(JSON.stringify(normalized))
    : [createEmptyNode(1)];
  const normalized = normalizeFlowNodes(row?.flowNodes?.length ? row.flowNodes : mapTasksToFlowNodes(row?.tasks));
  const flowNodes = normalized.length ? JSON.parse(JSON.stringify(normalized)) : [createEmptyNode(1)];
  return {
    templateKey: String(row?.templateId || ""),
@@ -631,9 +575,7 @@
    formPayload,
    flowNodes,
    templateAttachments: initTemplateAttachmentsFromSnapshot(templateSnapshot),
    storageBlobDTOs: row?.storageBlobDTOs?.length
      ? JSON.parse(JSON.stringify(row.storageBlobDTOs))
      : [],
    storageBlobDTOs: (row?.storageBlobDTOs?.length ? row.storageBlobDTOs : row?.storageBlobVOList || []).map(f => JSON.parse(JSON.stringify(f))),
  };
}
@@ -641,9 +583,7 @@
  const tpl = templateOverride || null;
  const payload = tpl?.fields?.length ? buildFormPayloadFromFields(tpl.fields) : { summary: "" };
  const normalized = normalizeFlowNodes(flowNodesOverride);
  const flowNodes = normalized.length
    ? JSON.parse(JSON.stringify(normalized))
    : [createEmptyNode(1)];
  const flowNodes = normalized.length ? JSON.parse(JSON.stringify(normalized)) : [createEmptyNode(1)];
  return {
    templateKey: templateKey || "",
    templateId: tpl?.templateId || "",
@@ -658,9 +598,7 @@
    formFieldDefs: tpl?.fields || [],
    formPayload: payload,
    flowNodes,
    templateAttachments: tpl?.storageBlobDTOs
      ? JSON.parse(JSON.stringify(tpl.storageBlobDTOs))
      : [],
    templateAttachments: tpl?.storageBlobDTOs ? JSON.parse(JSON.stringify(tpl.storageBlobDTOs)) : [],
    storageBlobDTOs: [],
  };
}
src/views/officeProcessAutomation/ApproveManage/approve-list/components/ApproveDetailPanel.vue
@@ -3,43 +3,77 @@
  <div class="approve-detail-panel">
    <div class="detail-block">
      <div class="detail-block-title">基本信息</div>
      <el-descriptions :column="2" border>
      <el-descriptions :column="2"
                       border>
        <el-descriptions-item label="业务单号">{{ row.bizId || row.id || "—" }}</el-descriptions-item>
        <el-descriptions-item label="审批状态">
          <el-tag :type="approvalStatusTagType(row.approvalStatus)" size="small" effect="plain">
          <el-tag :type="approvalStatusTagType(row.approvalStatus)"
                  size="small"
                  effect="plain">
            {{ approvalStatusLabel(row.approvalStatus) }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="审批类型">
          <span class="approve-type-cell" :style="approvalTypeStyle(row.approvalType)">
          <span class="approve-type-cell"
                :style="approvalTypeStyle(row.approvalType)">
            {{ approvalTypeLabel(row.approvalType) }}
          </span>
        </el-descriptions-item>
        <el-descriptions-item label="申请人编号">{{ row.applicantNo || "—" }}</el-descriptions-item>
        <el-descriptions-item label="申请人名称">{{ row.applicantName || "—" }}</el-descriptions-item>
        <el-descriptions-item label="申请摘要">{{ row.summary || "—" }}</el-descriptions-item>
        <el-descriptions-item v-if="row.rejectReason" label="驳回原因" :span="2">
        <el-descriptions-item v-if="row.rejectReason"
                              label="驳回原因"
                              :span="2">
          <span class="reject-text">{{ row.rejectReason }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="创建时间" :span="2">
        <el-descriptions-item label="创建时间"
                              :span="2">
          {{ formatDisplayTime(row.createTime) }}
        </el-descriptions-item>
      </el-descriptions>
    </div>
    <div class="detail-block">
      <div class="detail-block-title">填报内容</div>
      <FormPayloadFields
        :fields="formResolved.fields"
      <FormPayloadFields :fields="formResolved.fields"
        :form-payload="formResolved.formPayload"
        readonly
      />
                         readonly />
    </div>
    <div v-if="attachmentList.length"
         class="detail-block">
      <div class="detail-block-title">附件列表</div>
      <div class="attachment-list">
        <div v-for="file in attachmentList"
             :key="file.id"
             class="attachment-item">
          <el-icon class="file-icon">
            <Paperclip />
          </el-icon>
          <span class="file-name"
                :title="file.name || file.originalFilename">
            {{ file.name || file.originalFilename }}
          </span>
          <div class="file-actions">
            <el-link v-if="file.previewURL || file.url"
                     type="primary"
                     :underline="false"
                     @click="openFile(file.previewURL || file.url)">预览</el-link>
            <el-divider v-if="(file.previewURL || file.url) && file.downloadURL"
                        direction="vertical" />
            <el-link v-if="file.downloadURL"
                     type="primary"
                     :underline="false"
                     @click="openFile(file.downloadURL)">下载</el-link>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { computed } from "vue";
  import { Paperclip } from "@element-plus/icons-vue";
import { formatDisplayTime } from "../../approve-template/approveTemplateConstants.js";
import {
  approvalTypeLabel,
@@ -55,6 +89,16 @@
});
const formResolved = computed(() => resolveInstanceFormFields(props.row));
  const attachmentList = computed(() => {
    const list = props.row.storageBlobVOList || props.row.storageBlobDTOs || [];
    return Array.isArray(list) ? list : [];
  });
  function openFile(url) {
    if (!url) return;
    window.open(url, "_blank");
  }
</script>
<style scoped>
@@ -82,4 +126,42 @@
.reject-text {
  color: var(--el-color-danger);
}
  .attachment-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
  }
  .attachment-item {
    display: flex;
    align-items: center;
    padding: 10px 12px;
    background-color: var(--el-fill-color-light);
    border-radius: 6px;
    border: 1px solid var(--el-border-color-lighter);
    transition: all 0.3s;
  }
  .attachment-item:hover {
    border-color: var(--el-color-primary-light-5);
    background-color: var(--el-color-primary-light-9);
  }
  .file-icon {
    font-size: 18px;
    color: var(--el-text-color-secondary);
    margin-right: 10px;
  }
  .file-name {
    flex: 1;
    font-size: 13px;
    color: var(--el-text-color-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-right: 12px;
  }
  .file-actions {
    display: flex;
    align-items: center;
    flex-shrink: 0;
  }
</style>
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
@@ -1,25 +1,9 @@
import { computed } from "vue";
import {
  businessApprovalStatusLabel,
  businessApprovalStatusTagType,
  formatFieldDisplayValue,
  resolveInstanceFormFields,
} from "../approve-list/approveListConstants.js";
import {
  INSTANCE_NO_SEARCH_MODULE_KEYS,
  INSTANCE_NO_TABLE_COLUMN,
} from "./approvalInstanceListSearch.js";
import { businessApprovalStatusLabel, businessApprovalStatusTagType, formatFieldDisplayValue, resolveInstanceFormFields } from "../approve-list/approveListConstants.js";
import { INSTANCE_NO_SEARCH_MODULE_KEYS, INSTANCE_NO_TABLE_COLUMN } from "./approvalInstanceListSearch.js";
/** 列表/详情不回显为独立列的填报项 key(避免覆盖实例系统字段) */
const DEFAULT_EXCLUDE_KEYS = new Set([
  "summary",
  "status",
  "approvalStatus",
  "approvalstatus",
  "instanceStatus",
  "publishStatus",
  "newsStatus",
]);
const DEFAULT_EXCLUDE_KEYS = new Set(["summary", "status", "approvalStatus", "approvalstatus", "instanceStatus", "publishStatus", "newsStatus"]);
/** enrich 后必须保留的实例字段(不被 formConfig 铺平覆盖) */
const PRESERVE_INSTANCE_FIELDS = [
@@ -44,6 +28,8 @@
  "unread",
  "currentLevel",
  "newsStatus",
  "storageBlobVOList",
  "storageBlobDTOs",
];
/**
@@ -64,14 +50,8 @@
    if (!f?.key || DEFAULT_EXCLUDE_KEYS.has(f.key)) continue;
    const val = formPayload[f.key];
    let text = formatFieldDisplayValue(f, val, caches);
    if (
      text === String(val) &&
      row?.applicantName &&
      (f.label === "申请人" || f.key === "applicant" || f.key === "applicantName")
    ) {
      const idMatch =
        String(val) === String(row.applicantId) ||
        String(val) === String(row.applicantNo);
    if (text === String(val) && row?.applicantName && (f.label === "申请人" || f.key === "applicant" || f.key === "applicantName")) {
      const idMatch = String(val) === String(row.applicantId) || String(val) === String(row.applicantNo);
      if (idMatch) text = row.applicantName;
    }
    formDisplay[f.key] = text;
@@ -89,10 +69,8 @@
 * 从列表首行 formConfig 生成主表动态列(label 取自模板字段 label)
 */
export function getFormConfigFieldColumns(firstRow, { excludeKeys = DEFAULT_EXCLUDE_KEYS } = {}) {
  const fields = (firstRow?.formFieldDefs || []).filter(
    (f) => f?.key && !excludeKeys.has(f.key)
  );
  return fields.map((f) => ({
  const fields = (firstRow?.formFieldDefs || []).filter(f => f?.key && !excludeKeys.has(f.key));
  return fields.map(f => ({
    label: f.label || f.key,
    prop: f.key,
    minWidth: f.type === "textarea" ? 200 : f.type === "datetimerange" ? 160 : 120,
@@ -104,19 +82,9 @@
 * 业务申请主表列:固定列 + formConfig 动态列 + 审批状态 + 操作
 */
export function buildInstanceTableColumns(tableDataRef, buildTableActions, options = {}) {
  const {
    moduleKey,
    excludeKeys = DEFAULT_EXCLUDE_KEYS,
    beforeFormColumns = [],
    extraColumns = [],
    afterFormColumns = [],
    actionWidth = 260,
  } = options;
  const { moduleKey, excludeKeys = DEFAULT_EXCLUDE_KEYS, beforeFormColumns = [], extraColumns = [], afterFormColumns = [], actionWidth = 260 } = options;
  const leadingCols =
    moduleKey && INSTANCE_NO_SEARCH_MODULE_KEYS.has(moduleKey)
      ? [INSTANCE_NO_TABLE_COLUMN]
      : [];
  const leadingCols = moduleKey && INSTANCE_NO_SEARCH_MODULE_KEYS.has(moduleKey) ? [INSTANCE_NO_TABLE_COLUMN] : [];
  return computed(() => {
    const formCols = getFormConfigFieldColumns(tableDataRef.value?.[0], { excludeKeys });
@@ -132,8 +100,8 @@
        prop: "approvalStatus",
        width: 110,
        dataType: "tag",
        formatData: (v) => businessApprovalStatusLabel(v),
        formatType: (v) => businessApprovalStatusTagType(v),
        formatData: v => businessApprovalStatusLabel(v),
        formatType: v => businessApprovalStatusTagType(v),
      },
      {
        dataType: "action",
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js
@@ -1,3 +1,4 @@
import dayjs from "dayjs";
import { APPROVAL_MODULE_KEYS } from "./approvalModuleRegistry.js";
/** 支持审批单号查询/主表展示的审批申请模块 */
@@ -48,16 +49,29 @@
  return no ? { instanceNo: no } : {};
}
/** 组装 approvalInstanceDto 查询片段(申请人 + 审批单号) */
/** 组装 approvalInstanceDto 查询片段(申请人 + 审批单号 + 状态 + 时间范围) */
export function buildApprovalInstanceSearchDto(searchForm = {}, extraParams = {}) {
  const dto = {
    ...(extraParams && typeof extraParams === "object" ? extraParams : {}),
  };
  Object.assign(dto, pickApplicantFromSearchForm(searchForm));
  Object.assign(dto, pickInstanceNoFromSearchForm(searchForm));
  // 审批状态
  if (searchForm?.status) {
    dto.status = searchForm.status;
  }
  // 创建时间范围
  const range = searchForm?.createTimeRange;
  if (Array.isArray(range) && range[0]) {
    dto.createTimeStart = range[0] + (range[0].includes(":") ? "" : " 00:00:00");
  }
  if (Array.isArray(range) && range[1]) {
    dto.createTimeEnd = range[1] + (range[1].includes(":") ? "" : " 23:59:59");
  }
  delete dto.createTime;
  delete dto.createTimeStart;
  delete dto.createTimeEnd;
  return dto;
}
@@ -74,26 +88,17 @@
function matchApplicantKeyword(row, keyword) {
  const kw = (keyword || "").trim().toLowerCase();
  if (!kw) return true;
  const parts = [
    row?.applicantName,
    row?.applicantNo,
    row?.applicantId,
    getRowPayloadValue(row, ["applicant", "applicantName", "applicantId"]),
  ]
    .filter((v) => v != null && v !== "")
    .map((v) => String(v).toLowerCase());
  return parts.some((p) => p.includes(kw));
  const parts = [row?.applicantName, row?.applicantNo, row?.applicantId, getRowPayloadValue(row, ["applicant", "applicantName", "applicantId"])]
    .filter(v => v != null && v !== "")
    .map(v => String(v).toLowerCase());
  return parts.some(p => p.includes(kw));
}
function matchApplicantId(row, applicantId) {
  if (applicantId == null || applicantId === "") return true;
  const id = String(applicantId);
  if (row?.applicantId != null && String(row.applicantId) === id) return true;
  const payloadApplicant = getRowPayloadValue(row, [
    "applicant",
    "applicantId",
    "applicantUserId",
  ]);
  const payloadApplicant = getRowPayloadValue(row, ["applicant", "applicantId", "applicantUserId"]);
  return String(payloadApplicant) === id;
}
@@ -106,31 +111,48 @@
function matchInstanceNo(row, instanceNo) {
  const kw = (instanceNo || "").trim().toLowerCase();
  if (!kw) return true;
  const parts = [row?.instanceNo, row?.bizId]
    .filter((v) => v != null && v !== "")
    .map((v) => String(v).toLowerCase());
  return parts.some((p) => p.includes(kw));
  const parts = [row?.instanceNo, row?.bizId].filter(v => v != null && v !== "").map(v => String(v).toLowerCase());
  return parts.some(p => p.includes(kw));
}
/** 是否存在列表筛选条件(申请人 / 审批单号) */
/** 是否存在列表筛选条件(申请人 / 审批单号 / 状态 / 时间范围) */
export function hasActiveModuleSearch(moduleKey, searchForm = {}) {
  const sf = searchForm || {};
  if ((sf.instanceNo || "").trim()) return true;
  if ((sf.applicantKeyword || "").trim()) return true;
  if ((sf.applicantName || "").trim()) return true;
  return sf.applicantId != null && sf.applicantId !== "";
  if (sf.applicantId != null && sf.applicantId !== "") return true;
  if (sf.status) return true;
  if (Array.isArray(sf.createTimeRange) && sf.createTimeRange.length === 2) return true;
  return false;
}
/** 按申请人、审批单号做前端兜底筛选 */
/** 按申请人、审批单号、状态、时间范围做前端兜底筛选 */
export function filterInstanceRowsByModuleSearch(moduleKey, rows, searchForm = {}) {
  const sf = searchForm || {};
  const list = Array.isArray(rows) ? rows : [];
  if (!hasActiveModuleSearch(moduleKey, sf)) return list;
  return list.filter(
    (row) =>
      matchInstanceNo(row, sf.instanceNo) &&
      matchApplicantId(row, sf.applicantId) &&
      matchApplicantKeyword(row, sf.applicantKeyword || sf.applicantName)
  );
  return list.filter(row => {
    // 审批单号
    if (!matchInstanceNo(row, sf.instanceNo)) return false;
    // 申请人
    if (!matchApplicantId(row, sf.applicantId)) return false;
    if (!matchApplicantKeyword(row, sf.applicantKeyword || sf.applicantName)) return false;
    // 状态
    if (sf.status && String(row.statusRaw || row.status).toUpperCase() !== String(sf.status).toUpperCase()) {
      return false;
    }
    // 时间范围
    if (Array.isArray(sf.createTimeRange) && sf.createTimeRange.length === 2) {
      const rowTime = row.createTime || row.applyTime;
      if (rowTime) {
        const t = dayjs(rowTime);
        const start = dayjs(sf.createTimeRange[0] + " 00:00:00");
        const end = dayjs(sf.createTimeRange[1] + " 23:59:59");
        if (t.isBefore(start) || t.isAfter(end)) return false;
      }
    }
    return true;
  });
}
src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js
@@ -1,5 +1,3 @@
import { matchBusinessTypeValue } from "../approve-list/approveListConstants.js";
/**
 * 各业务模块与审批模板类型的映射(配置化入口)
 * businessType 与后端 TypeEnums / listPage 约定一致(写死枚举值)
src/views/officeProcessAutomation/HrManage/staff-contract/components/formDia.vue
@@ -1,37 +1,39 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
    <el-dialog v-model="dialogFormVisible"
        title="详情"
        width="70%"
        @close="closeDia"
    >
      <PIMTable
          rowKey="id"
               @close="closeDia">
      <PIMTable rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          height="600"
      ></PIMTable>
                height="600"></PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <Files ref="filesDia"></Files>
    <FileList v-if="fileDialogVisible"
              v-model:visible="fileDialogVisible"
              :record-type="'staff_contract'"
              :record-id="recordId" />
  </div>
</template>
<script setup>
import {ref} from "vue";
  import { ref, defineAsyncComponent, getCurrentInstance } from "vue";
import {findStaffContractListPage} from "@/api/personnelManagement/staffContract.js";
const Files = defineAsyncComponent(() => import( "@/views/personnelManagement/contractManagement/filesDia.vue"));
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const filesDia = ref()
  const FileList = defineAsyncComponent(() =>
    import("@/components/Dialog/FileList.vue")
  );
  const { proxy } = getCurrentInstance();
  const emit = defineEmits(["close"]);
  const fileDialogVisible = ref(false);
  const recordId = ref(0);
const dialogFormVisible = ref(false);
const operationType = ref('')
  const operationType = ref("");
const tableColumn = ref([
  {
    label: "合同年限",
@@ -49,16 +51,17 @@
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
      fixed: "right",
    width: 120,
    operation: [
      {
        name: "上传附件",
          name: "附件",
        type: "text",
        clickFun: (row) => {
          filesDia.value.openDialog( row,'合同')
          clickFun: row => {
            recordId.value = row.id;
            fileDialogVisible.value = true;
        },
      }
        },
    ],
  },
]);
@@ -69,22 +72,17 @@
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  if (operationType.value === 'edit') {
    if (operationType.value === "edit") {
    findStaffContractListPage({staffOnJobId: row.id}).then(res => {
      tableData.value = res.data.records
    })
        tableData.value = res.data.records;
      });
  }
}
const openUploadFile = (row) => {
  filesDia.value.open = true
  filesDia.value.row = row
}
  };
// 关闭弹框
const closeDia = () => {
  dialogFormVisible.value = false;
  emit('close')
    emit("close");
};
defineExpose({
  openDialog,
@@ -92,5 +90,4 @@
</script>
<style scoped>
</style>
src/views/personnelManagement/contractManagement/components/formDia.vue
@@ -1,37 +1,39 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
    <el-dialog v-model="dialogFormVisible"
        title="详情"
        width="70%"
        @close="closeDia"
    >
      <PIMTable
          rowKey="id"
               @close="closeDia">
      <PIMTable rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          height="600"
      ></PIMTable>
                height="600"></PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <Files ref="filesDia"></Files>
    <FileList v-if="fileDialogVisible"
              v-model:visible="fileDialogVisible"
              :record-type="'staff_contract'"
              :record-id="recordId" />
  </div>
</template>
<script setup>
import {ref} from "vue";
  import { ref, defineAsyncComponent, getCurrentInstance } from "vue";
import {findStaffContractListPage} from "@/api/personnelManagement/staffContract.js";
const Files = defineAsyncComponent(() => import( "@/views/personnelManagement/contractManagement/filesDia.vue"));
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const filesDia = ref()
  const FileList = defineAsyncComponent(() =>
    import("@/components/Dialog/FileList.vue")
  );
  const { proxy } = getCurrentInstance();
  const emit = defineEmits(["close"]);
  const fileDialogVisible = ref(false);
  const recordId = ref(0);
const dialogFormVisible = ref(false);
const operationType = ref('')
  const operationType = ref("");
const tableColumn = ref([
  {
    label: "合同年限",
@@ -49,16 +51,17 @@
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
      fixed: "right",
    width: 120,
    operation: [
      {
        name: "上传附件",
          name: "附件",
        type: "text",
        clickFun: (row) => {
          filesDia.value.openDialog( row,'合同')
          clickFun: row => {
            recordId.value = row.id;
            fileDialogVisible.value = true;
        },
      }
        },
    ],
  },
]);
@@ -69,22 +72,17 @@
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  if (operationType.value === 'edit') {
    if (operationType.value === "edit") {
    findStaffContractListPage({staffOnJobId: row.id}).then(res => {
      tableData.value = res.data.records
    })
        tableData.value = res.data.records;
      });
  }
}
const openUploadFile = (row) => {
  filesDia.value.open = true
  filesDia.value.row = row
}
  };
// 关闭弹框
const closeDia = () => {
  dialogFormVisible.value = false;
  emit('close')
    emit("close");
};
defineExpose({
  openDialog,
@@ -92,5 +90,4 @@
</script>
<style scoped>
</style>
src/views/procurementManagement/procurementLedger/index.vue
@@ -923,7 +923,7 @@
    },
    rules: {
      purchaseContractNumber: [
        { required: true, message: "请输入", trigger: "blur" },
        { required: false, message: "请输入", trigger: "blur" },
      ],
      projectName: [
        { required: true, message: "请输入项目名称", trigger: "blur" },
@@ -1306,18 +1306,7 @@
      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) {
      if (type === "edit" && row?.id) {
        // 编辑时加载数据
        currentId.value = row.id;
        try {
@@ -1398,7 +1387,7 @@
  // 提交表单
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
    proxy.$refs["formRef"].validate(async valid => {
      if (valid) {
        if (productData.value.length > 0) {
          // 新增时,需要从每个产品对象中删除 id 字段
@@ -1428,6 +1417,20 @@
          delete submitData.id;
        }
        // 如果采购合同号为空,则根据录入日期自动生成
        if (!submitData.purchaseContractNumber) {
          try {
            const purchaseNoRes = await createPurchaseNo(submitData.entryDate);
            if (purchaseNoRes?.data) {
              submitData.purchaseContractNumber = purchaseNoRes.data;
            }
          } catch (error) {
            console.error("生成采购合同号失败:", error);
            proxy.$modal.msgWarning("生成采购合同号失败");
            return;
          }
        }
        addOrEditPurchase(submitData).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
src/views/procurementManagement/procurementPlan/index.vue
@@ -1,158 +1,205 @@
<template>
  <div class="app-container">
    <!-- 搜索区域 -->
    <el-card class="search-card" shadow="never">
      <el-form :model="searchForm" :inline="true" class="search-form">
    <el-card class="search-card"
             shadow="never">
      <el-form :model="searchForm"
               :inline="true"
               class="search-form">
        <el-form-item label="计划名称">
          <el-input v-model="searchForm.planName" placeholder="请输入计划名称" clearable />
          <el-input v-model="searchForm.planName"
                    placeholder="请输入计划名称"
                    clearable />
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="searchForm.status" placeholder="请选择状态" clearable style="width: 150px">
            <el-option label="启用" value="active" />
            <el-option label="禁用" value="disabled" />
          <el-select v-model="searchForm.status"
                     placeholder="请选择状态"
                     clearable
                     style="width: 150px">
            <el-option label="启用"
                       value="active" />
            <el-option label="禁用"
                       value="disabled" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">
            <el-icon><Search /></el-icon>
          <el-button type="primary"
                     @click="handleSearch">
            <el-icon>
              <Search />
            </el-icon>
            搜索
          </el-button>
          <el-button @click="handleReset">
            <el-icon><Refresh /></el-icon>
            <el-icon>
              <Refresh />
            </el-icon>
            重置
          </el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <!-- 操作按钮 -->
    <el-card class="table-card" shadow="never">
    <el-card class="table-card"
             shadow="never">
      <div class="table-header">
        <div class="table-title">采购计划列表</div>
        <div class="table-actions">
          <el-button type="primary" @click="handleAdd">
            <el-icon><Plus /></el-icon>
          <el-button type="primary"
                     @click="handleAdd">
            <el-icon>
              <Plus />
            </el-icon>
            新增计划
          </el-button>
          <el-button type="info" @click="handleExport">
            <el-icon><Download /></el-icon>
          <el-button type="info"
                     @click="handleExport">
            <el-icon>
              <Download />
            </el-icon>
            导出
          </el-button>
        </div>
      </div>
      <!-- 数据表格 -->
      <el-table
        v-loading="loading"
      <el-table v-loading="loading"
        :data="tableData"
        stripe
        border
        style="width: 100%"
      >
        <el-table-column prop="planName" label="计划名称" min-width="150" />
        <el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
        <el-table-column prop="formula" label="计算公式" min-width="200" show-overflow-tooltip>
                style="width: 100%">
        <el-table-column prop="planName"
                         label="计划名称"
                         min-width="150" />
        <el-table-column prop="description"
                         label="描述"
                         min-width="200"
                         show-overflow-tooltip />
        <el-table-column prop="formula"
                         label="计算公式"
                         min-width="200"
                         show-overflow-tooltip>
          <template #default="{ row }">
            <el-tag type="info" size="small">{{ row.formula }}</el-tag>
            <el-tag type="info"
                    size="small">{{ row.formula }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="status" label="状态" width="80" align="center">
        <el-table-column prop="status"
                         label="状态"
                         width="80"
                         align="center">
          <template #default="{ row }">
            <el-tag :type="row.status === 'active' ? 'success' : 'info'" size="small">
            <el-tag :type="row.status === 'active' ? 'success' : 'info'"
                    size="small">
              {{ row.status === 'active' ? '启用' : '禁用' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="updateTime" label="最后计算时间" width="160" />
        <el-table-column label="操作" width="200" fixed="right" align="center">
        <el-table-column prop="updateTime"
                         label="最后计算时间"
                         width="160" />
        <el-table-column label="操作"
                         width="200"
                         fixed="right"
                         align="center">
          <template #default="{ row }">
            <el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
            <el-button type="success" link @click="handleCalculate(row)">计算</el-button>
            <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
            <el-button type="primary"
                       link
                       @click="handleEdit(row)">编辑</el-button>
            <el-button type="success"
                       link
                       @click="handleCalculate(row)">计算</el-button>
            <el-button type="danger"
                       link
                       @click="handleDelete(row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页 -->
      <div class="pagination-container">
        <el-pagination
          v-model:current-page="pagination.current"
        <el-pagination v-model:current-page="pagination.current"
          v-model:page-size="pagination.size"
          :page-sizes="[10, 20, 50, 100]"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
                       @current-change="handleCurrentChange" />
      </div>
    </el-card>
    <!-- 新增/编辑对话框 -->
    <FormDialog
      v-model="dialogVisible"
    <FormDialog v-model="dialogVisible"
      :title="dialogType === 'add' ? '新增采购计划' : '编辑采购计划'"
      :width="'1000px'"
      :operation-type="dialogType"
      :close-on-click-modal="false"
      @close="dialogVisible = false"
      @confirm="handleSubmit"
      @cancel="dialogVisible = false"
    >
                @cancel="dialogVisible = false">
      <div class="form-container">
        <!-- 基本信息 -->
        <div class="form-section">
          <div class="section-title">基本信息</div>
          <el-form
            ref="formRef"
          <el-form ref="formRef"
            :model="formData"
            :rules="formRules"
            label-width="120px"
          >
                   label-width="120px">
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="编码" prop="code">
                  <el-input v-model="formData.code" placeholder="系统自动生成" disabled />
                <el-form-item label="编码"
                              prop="code">
                  <el-input v-model="formData.code"
                            placeholder="保存后自动生成"
                            disabled />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="名称" prop="planName" required>
                  <el-input v-model="formData.planName" placeholder="请输入计划名称" />
                <el-form-item label="名称"
                              prop="planName"
                              required>
                  <el-input v-model="formData.planName"
                            placeholder="请输入计划名称" />
                </el-form-item>
              </el-col>
            </el-row>
            <el-form-item label="描述" prop="description">
              <el-input
                v-model="formData.description"
            <el-form-item label="描述"
                          prop="description">
              <el-input v-model="formData.description"
                type="textarea"
                :rows="3"
                placeholder="请输入计划描述"
              />
                        placeholder="请输入计划描述" />
            </el-form-item>
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="状态" prop="status">
                  <el-select v-model="formData.status" placeholder="请选择状态" style="width: 100%">
                    <el-option label="启用" value="active" />
                    <el-option label="禁用" value="disabled" />
                <el-form-item label="状态"
                              prop="status">
                  <el-select v-model="formData.status"
                             placeholder="请选择状态"
                             style="width: 100%">
                    <el-option label="启用"
                               value="active" />
                    <el-option label="禁用"
                               value="disabled" />
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="是否系统预置">
                  <el-checkbox v-model="formData.isSystemPreset">是</el-checkbox>
                <el-form-item label="创建时间" prop="createTime">
                  <el-date-picker v-model="formData.createTime"
                                  type="date"
                                  placeholder="选择日期"
                                  value-format="YYYY-MM-DD"
                                  style="width: 100%" />
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
        </div>
        <!-- 计算参数 -->
        <div class="form-section">
          <div class="section-title">计算参数</div>
          <el-tabs v-model="activeTab" class="param-tabs">
            <el-tab-pane label="需求参数" name="demand">
          <el-tabs v-model="activeTab"
                   class="param-tabs">
            <el-tab-pane label="需求参数"
                         name="demand">
              <div class="checkbox-group">
                <el-checkbox v-model="formData.considerExistingStock">考虑现有库存</el-checkbox>
                <el-checkbox v-model="formData.warehouseControl">仓库运行MRP的控制</el-checkbox>
@@ -163,7 +210,8 @@
                <el-checkbox v-model="formData.negativeStockAsDemand">负库存作为需求</el-checkbox>
              </div>
            </el-tab-pane>
            <el-tab-pane label="计算参数" name="calculation">
            <el-tab-pane label="计算参数"
                         name="calculation">
              <div class="checkbox-group">
                <el-checkbox v-model="formData.considerExistingStock">考虑现有库存</el-checkbox>
                <el-checkbox v-model="formData.warehouseControl">仓库运行MRP的控制</el-checkbox>
@@ -176,7 +224,6 @@
            </el-tab-pane>
          </el-tabs>
        </div>
        <!-- 汇总合并选项 -->
        <div class="form-section">
          <div class="section-title">汇总合并选项</div>
@@ -186,20 +233,20 @@
            <el-checkbox v-model="formData.summaryDemandDate">需求日期</el-checkbox>
          </div>
        </div>
        <!-- 计算公式 -->
        <div class="form-section">
          <div class="section-title">计算公式</div>
          <div class="formula-input-section">
            <el-form-item label="计算公式" prop="formula" required>
              <el-input
                v-model="formData.formula"
            <el-form-item label="计算公式"
                          prop="formula"
                          required>
              <el-input v-model="formData.formula"
                placeholder="例如: 预计出库数量 - 现有库存 + 安全库存 - 预计入库数量"
                @input="validateFormula"
              />
                        @input="validateFormula" />
            </el-form-item>
            <div class="formula-help">
              <el-text type="info" size="small">
              <el-text type="info"
                       size="small">
                支持变量:预计出库数量、现有库存、安全库存、预计入库数量
              </el-text>
            </div>
@@ -207,87 +254,117 @@
        </div>
      </div>
    </FormDialog>
    <!-- 产品选择对话框 -->
    <FormDialog
      v-model="productSelectDialogVisible"
    <FormDialog v-model="productSelectDialogVisible"
      title="选择产品"
      :width="'800px'"
      :close-on-click-modal="false"
      @close="productSelectDialogVisible = false"
      @confirm="handleConfirmProductSelection"
      @cancel="productSelectDialogVisible = false"
    >
                @cancel="productSelectDialogVisible = false">
      <div class="product-select">
        <el-alert
          title="请选择要计算的产品"
        <el-alert title="请选择要计算的产品"
          type="info"
          :closable="false"
          show-icon
        >
                  show-icon>
          <template #default>
            <p>选择产品后,系统将根据当前计算公式和产品库存情况进行计算。</p>
          </template>
        </el-alert>
        <el-table
          v-loading="productLoading"
        <el-table v-loading="productLoading"
          :data="productList"
          @selection-change="handleProductSelectionChange"
          stripe
          border
          style="width: 100%; margin-top: 20px;"
        >
          <el-table-column type="selection" width="55" />
          <el-table-column prop="productCategory" label="产品大类" min-width="150" />
          <el-table-column prop="specificationModel" label="产品规格" width="120" />
          <el-table-column prop="inboundNum0" label="现有库存" width="100" align="right" />
          <el-table-column prop="inboundNum" label="安全库存" width="100" align="right" />
          <el-table-column prop="inboundNum" label="预计出库" width="100" align="right" />
          <el-table-column prop="inboundNum0" label="预计入库" width="100" align="right" />
                  style="width: 100%; margin-top: 20px;">
          <el-table-column type="selection"
                           width="55" />
          <el-table-column prop="productCategory"
                           label="产品大类"
                           min-width="150" />
          <el-table-column prop="specificationModel"
                           label="规格型号"
                           width="120" />
          <el-table-column prop="inboundNum0"
                           label="现有库存"
                           width="100"
                           align="right" />
          <el-table-column prop="inboundNum"
                           label="安全库存"
                           width="100"
                           align="right" />
          <el-table-column prop="inboundNum"
                           label="预计出库"
                           width="100"
                           align="right" />
          <el-table-column prop="inboundNum0"
                           label="预计入库"
                           width="100"
                           align="right" />
        </el-table>
      </div>
    </FormDialog>
    <!-- 计算结果对话框 -->
    <FormDialog
      v-model="calculateDialogVisible"
    <FormDialog v-model="calculateDialogVisible"
      title="采购计算结果"
      :width="'1000px'"
      :close-on-click-modal="false"
      @close="calculateDialogVisible = false"
      @confirm="handleCreatePurchaseOrder"
      @cancel="calculateDialogVisible = false"
    >
                @cancel="calculateDialogVisible = false">
      <div class="calculate-result">
        <el-alert
          title="计算结果"
        <el-alert title="计算结果"
          type="success"
          :closable="false"
          show-icon
        >
                  show-icon>
          <template #default>
            <p>基于当前配置的计算公式和库存情况,系统已计算出各产品的采购需求。</p>
          </template>
        </el-alert>
        <el-table :data="calculateResult" stripe border style="width: 100%; margin-top: 20px;">
          <el-table-column prop="productCategory" label="产品大类" min-width="150" />
          <el-table-column prop="specificationModel" label="产品规格" width="120" />
          <el-table-column prop="inboundNum0" label="现有库存" width="100" align="right" />
          <el-table-column prop="inboundNum" label="安全库存" width="100" align="right" />
          <el-table-column prop="inboundNum" label="预计出库数量" width="120" align="right" />
          <el-table-column prop="inboundNum0" label="预计入库数量" width="120" align="right" />
          <el-table-column prop="weeklyNetDemand" label="按周净需求" width="120" align="right">
        <el-table :data="calculateResult"
                  stripe
                  border
                  style="width: 100%; margin-top: 20px;">
          <el-table-column prop="productCategory"
                           label="产品大类"
                           min-width="150" />
          <el-table-column prop="specificationModel"
                           label="规格型号"
                           width="120" />
          <el-table-column prop="inboundNum0"
                           label="现有库存"
                           width="100"
                           align="right" />
          <el-table-column prop="inboundNum"
                           label="安全库存"
                           width="100"
                           align="right" />
          <el-table-column prop="inboundNum"
                           label="预计出库数量"
                           width="120"
                           align="right" />
          <el-table-column prop="inboundNum0"
                           label="预计入库数量"
                           width="120"
                           align="right" />
          <el-table-column prop="weeklyNetDemand"
                           label="按周净需求"
                           width="120"
                           align="right">
            <template #default="{ row }">
              <el-tag :type="row.weeklyNetDemand > 0 ? 'warning' : 'success'" size="small">
              <el-tag :type="row.weeklyNetDemand > 0 ? 'warning' : 'success'"
                      size="small">
                {{ row.weeklyNetDemand }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="suggestedPurchase" label="建议采购" width="100" align="right">
          <el-table-column prop="suggestedPurchase"
                           label="建议采购"
                           width="100"
                           align="right">
            <template #default="{ row }">
              <el-tag :type="row.suggestedPurchase > 0 ? 'danger' : 'success'" size="small">
              <el-tag :type="row.suggestedPurchase > 0 ? 'danger' : 'success'"
                      size="small">
                {{ row.suggestedPurchase }}
              </el-tag>
            </template>
@@ -299,43 +376,50 @@
</template>
<script setup>
import FormDialog from '@/components/Dialog/FormDialog.vue';
import {ref, reactive, onMounted, getCurrentInstance} from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Refresh, Plus, Download } from '@element-plus/icons-vue'
import {listPage,add,update,del,listPageCopy} from "@/api/procurementManagement/procurementPlan.js"
  import FormDialog from "@/components/Dialog/FormDialog.vue";
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Search, Refresh, Plus, Download } from "@element-plus/icons-vue";
  import {
    listPage,
    add,
    update,
    del,
    listPageCopy,
  } from "@/api/procurementManagement/procurementPlan.js";
// 响应式数据
const loading = ref(false)
const submitLoading = ref(false)
const dialogVisible = ref(false)
const productSelectDialogVisible = ref(false)
const calculateDialogVisible = ref(false)
const dialogType = ref('add')
const productLoading = ref(false)
const selectedProducts = ref([])
const currentPlan = ref(null)
  const loading = ref(false);
  const submitLoading = ref(false);
  const dialogVisible = ref(false);
  const productSelectDialogVisible = ref(false);
  const calculateDialogVisible = ref(false);
  const dialogType = ref("add");
  const productLoading = ref(false);
  const selectedProducts = ref([]);
  const currentPlan = ref(null);
// 搜索表单
const searchForm = reactive({
  planName: '',
  status: ''
})
    planName: "",
    status: "",
  });
// 分页数据
const pagination = reactive({
  current: 1,
  size: 20
})
    size: 20,
  });
// 表单数据
const formData = reactive({
  code: '',
  planName: '',
  description: '',
  status: '',
    code: "",
    planName: "",
    description: "",
    status: "",
  isSystemPreset: false,
  formula: '',
    formula: "",
    createTime: "",
  // 计算参数
  considerExistingStock: false,
  warehouseControl: false,
@@ -347,166 +431,157 @@
  // 汇总合并选项
  summaryMaterial: false,
  summaryAuxAttributes: false,
  summaryDemandDate: false
})
    summaryDemandDate: false,
  });
// 当前激活的标签页
const activeTab = ref('demand')
  const activeTab = ref("demand");
// 表单验证规则
const formRules = {
  planName: [
    { required: true, message: '请输入计划名称', trigger: 'blur' }
  ],
  status: [
    { required: true, message: '请选择状态', trigger: 'change' }
  ],
  formula: [
    { required: true, message: '请输入计算公式', trigger: 'blur' }
  ]
}
    planName: [{ required: true, message: "请输入计划名称", trigger: "blur" }],
    status: [{ required: true, message: "请选择状态", trigger: "change" }],
    formula: [{ required: true, message: "请输入计算公式", trigger: "blur" }],
  };
// 表格数据
const tableData = ref([])
  const tableData = ref([]);
// 产品列表数据
const productList = ref([
  {
    id: 4,
    productName: '产品D',
    productCode: 'PD004',
      productName: "产品D",
      productCode: "PD004",
    existingStock: 90,
    safetyStock: 40,
    expectedOutbound: 160,
    expectedInbound: 35
  }
])
      expectedInbound: 35,
    },
  ]);
// 计算结果数据
const calculateResult = ref([
  {
    productName: '产品A',
      productName: "产品A",
    existingStock: 100,
    safetyStock: 50,
    expectedOutbound: 200,
    expectedInbound: 30,
    weeklyNetDemand: 120,
    suggestedPurchase: 150
      suggestedPurchase: 150,
  },
  {
    productName: '产品B',
      productName: "产品B",
    existingStock: 80,
    safetyStock: 30,
    expectedOutbound: 150,
    expectedInbound: 20,
    weeklyNetDemand: 100,
    suggestedPurchase: 120
  }
])
const total = ref(0)
      suggestedPurchase: 120,
    },
  ]);
  const total = ref(0);
// 方法
const handleSearch = () => {
  pagination.current = 1
  loadData()
}
    pagination.current = 1;
    loadData();
  };
const handleReset = () => {
  Object.assign(searchForm, {
    planName: '',
    status: ''
  })
  handleSearch()
}
      planName: "",
      status: "",
    });
    handleSearch();
  };
const loadData = () => {
  loading.value = true
    loading.value = true;
  listPage({...searchForm,...pagination}).then(res => {
    if(res.code === 200){
      tableData.value = res.data.records
      total.value = res.data.total
      loading.value = false
        tableData.value = res.data.records;
        total.value = res.data.total;
        loading.value = false;
    }
  })
}
    });
  };
const handleAdd = () => {
  dialogType.value = 'add'
  resetForm()
  // 自动生成编码
  formData.code = 'CGJH' + String(Date.now()).slice(-4)
  dialogVisible.value = true
}
    dialogType.value = "add";
    resetForm();
    formData.createTime = new Date().toISOString().split("T")[0];
    dialogVisible.value = true;
  };
const handleEdit = (row) => {
  dialogType.value = 'edit'
  Object.assign(formData, row)
  dialogVisible.value = true
}
  const handleEdit = row => {
    dialogType.value = "edit";
    Object.assign(formData, row);
    dialogVisible.value = true;
  };
const handleDelete = async (row) => {
  const handleDelete = async row => {
  try {
    await ElMessageBox.confirm('确定要删除这个采购计划吗?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
    let ids = [row.id]
      await ElMessageBox.confirm("确定要删除这个采购计划吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      });
      let ids = [row.id];
    del(ids).then(res =>{
     if(res.code === 200){
      ElMessage.success('删除成功')
      loadData()
          ElMessage.success("删除成功");
          loadData();
    }
   })
      });
  } catch {
    // 用户取消删除
  }
}
  };
const handleSubmit = async () => {
  try {
    // 表单验证
    if (!formData.planName || !formData.formula) {
      ElMessage.error('请填写必填项')
      return
        ElMessage.error("请填写必填项");
        return;
    }
    submitLoading.value = true
      submitLoading.value = true;
    if (dialogType.value === 'add') {
      if (dialogType.value === "add") {
      add(formData).then(res => {
        if(res.code === 200){
          ElMessage.success('新增成功')
          dialogVisible.value = false
          loadData()
            ElMessage.success("新增成功");
            dialogVisible.value = false;
            loadData();
        }
      })
        });
    } else {
      // 编辑
      update(formData).then(res => {
        if(res.code === 200){
          ElMessage.success('编辑成功')
          dialogVisible.value = false
          loadData()
            ElMessage.success("编辑成功");
            dialogVisible.value = false;
            loadData();
        }
      })
        });
    }
  } catch (error) {
    ElMessage.error('操作失败')
      ElMessage.error("操作失败");
  } finally {
    submitLoading.value = false
      submitLoading.value = false;
  }
}
  };
const resetForm = () => {
  Object.assign(formData, {
    code: '',
    planName: '',
    description: '',
    status: '',
      code: "",
      planName: "",
      description: "",
      status: "",
    isSystemPreset: false,
    formula: '预计出库数量 - 现有库存 + 安全库存 - 预计入库数量',
      formula: "预计出库数量 - 现有库存 + 安全库存 - 预计入库数量",
    // 计算参数
    considerExistingStock: false,
    warehouseControl: false,
@@ -518,52 +593,52 @@
    // 汇总合并选项
    summaryMaterial: false,
    summaryAuxAttributes: false,
    summaryDemandDate: false
  })
  activeTab.value = 'demand'
}
      summaryDemandDate: false,
    });
    activeTab.value = "demand";
  };
const validateFormula = () => {
  // 简单的公式验证
  const formula = formData.formula
    const formula = formData.formula;
  if (formula && !/^[a-zA-Z\u4e00-\u9fa5\s\*\+\-\/\(\)\d\.]+$/.test(formula)) {
    ElMessage.warning('公式格式可能不正确,请检查')
      ElMessage.warning("公式格式可能不正确,请检查");
  }
}
  };
const handleCalculate = (row) => {
  currentPlan.value = row
  productSelectDialogVisible.value = true
  loadProductList()
}
  const handleCalculate = row => {
    currentPlan.value = row;
    productSelectDialogVisible.value = true;
    loadProductList();
  };
const loadProductList = () => {
  productLoading.value = true
    productLoading.value = true;
  // 模拟加载产品数据
  listPageCopy({size:-1}).then(res => {
    if(res.code === 200){
      productList.value = res.data.records
      productLoading.value = false
        productList.value = res.data.records;
        productLoading.value = false;
    }
  })
}
    });
  };
const handleProductSelectionChange = (selection) => {
  selectedProducts.value = selection
}
  const handleProductSelectionChange = selection => {
    selectedProducts.value = selection;
  };
const handleConfirmProductSelection = () => {
  if (selectedProducts.value.length === 0) {
    ElMessage.warning('请选择要计算的产品')
    return
      ElMessage.warning("请选择要计算的产品");
      return;
  }
  
  ElMessage.success(`正在计算 ${currentPlan.value.planName} 的采购需求...`)
  productSelectDialogVisible.value = false
    ElMessage.success(`正在计算 ${currentPlan.value.planName} 的采购需求...`);
    productSelectDialogVisible.value = false;
  
  // 根据选择的产品和计算公式进行计算
  calculateWithSelectedProducts()
}
    calculateWithSelectedProducts();
  };
const calculateWithSelectedProducts = () => {
  // 模拟计算过程
@@ -571,8 +646,12 @@
  const result = selectedProducts.value.map(product => {
    // 这里应该根据实际的计算公式进行计算
    // 示例:预计出库数量 - 现有库存 + 安全库存 - 预计入库数量
    const weeklyNetDemand = product.inboundNum - product.inboundNum0 + product.inboundNum - product.inboundNum0
    const suggestedPurchase = Math.max(0, weeklyNetDemand)
      const weeklyNetDemand =
        product.inboundNum -
        product.inboundNum0 +
        product.inboundNum -
        product.inboundNum0;
      const suggestedPurchase = Math.max(0, weeklyNetDemand);
    return {
      productCategory: product.productCategory,
@@ -580,18 +659,17 @@
      inboundNum0: product.inboundNum0,
      inboundNum: product.inboundNum,
      weeklyNetDemand: weeklyNetDemand,
      suggestedPurchase: suggestedPurchase
    }
  })
        suggestedPurchase: suggestedPurchase,
      };
    });
  calculateResult.value = result
  calculateDialogVisible.value = true
}
    calculateResult.value = result;
    calculateDialogVisible.value = true;
  };
const handleCreatePurchaseOrder = () => {
  calculateDialogVisible.value = false
}
    calculateDialogVisible.value = false;
  };
const { proxy } = getCurrentInstance();
const handleExport = () => {
  ElMessageBox.confirm("内容将被导出,是否确认导出?", "导出", {
@@ -605,23 +683,22 @@
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
}
  };
  const handleSizeChange = size => {
    pagination.size = size;
    loadData();
  };
const handleSizeChange = (size) => {
  pagination.size = size
  loadData()
}
const handleCurrentChange = (current) => {
  pagination.current = current
  loadData()
}
  const handleCurrentChange = current => {
    pagination.current = current;
    loadData();
  };
// 生命周期
onMounted(() => {
  loadData()
})
    loadData();
  });
</script>
<style scoped>
src/views/procurementManagement/purchaseOrder/index.vue
@@ -56,6 +56,13 @@
            <el-option label="供应商B" value="供应商B" />
          </el-select>
        </el-form-item>
        <el-form-item label="创建时间">
          <el-date-picker v-model="formData.createTime"
                          type="date"
                          placeholder="选择日期"
                          value-format="YYYY-MM-DD"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
        </el-form-item>
@@ -81,7 +88,8 @@
const formData = reactive({
  supplierName: '',
  remark: ''
  remark: '',
  createTime: ''
})
const mockData = [
@@ -124,7 +132,7 @@
  if (type === 'edit' && row.id) {
    Object.assign(formData, { supplierName: row.supplierName, remark: row.remark })
  } else {
    Object.assign(formData, { supplierName: '', remark: '' })
    Object.assign(formData, { supplierName: '', remark: '', createTime: new Date().toISOString().split('T')[0] })
  }
  dialogVisible.value = true
}
@@ -133,11 +141,11 @@
  if (dialogType.value === 'add') {
    const newOrder = {
      id: Date.now(),
      orderNo: `PO${Date.now()}`,
      orderNo: '',
      supplierName: formData.supplierName,
      status: 'draft',
      totalAmount: 0,
      createTime: new Date().toLocaleString(),
      createTime: formData.createTime,
      remark: formData.remark
    }
    tableData.value.unshift(newOrder)
src/views/procurementManagement/qualityInspection/index.vue
@@ -116,6 +116,18 @@
          <el-input v-model="formData.inspector" placeholder="请输入质检员姓名" />
        </el-form-item>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建时间">
              <el-date-picker v-model="formData.createTime"
                              type="date"
                              placeholder="选择日期"
                              value-format="YYYY-MM-DD"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注">
          <el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
        </el-form-item>
@@ -144,7 +156,8 @@
  supplierName: '',
  products: [],
  inspector: '',
  remark: ''
  remark: '',
  createTime: ''
})
const mockData = [
@@ -198,7 +211,8 @@
      supplierName: '', 
      products: [],
      inspector: '', 
      remark: ''
      remark: '',
      createTime: new Date().toISOString().split('T')[0]
    })
  }
  dialogVisible.value = true
@@ -228,13 +242,13 @@
  if (dialogType.value === 'add') {
    const newInspection = {
      id: Date.now(),
      inspectionNo: `QI${Date.now()}`,
      inspectionNo: '',
      arrivalNo: formData.arrivalNo,
      supplierName: formData.supplierName,
      status: 'pending',
      qualifiedQuantity: totalQualified,
      unqualifiedQuantity: totalUnqualified,
      inspectionTime: new Date().toLocaleString(),
      inspectionTime: formData.createTime,
      inspector: formData.inspector,
      remark: formData.remark
    }
src/views/productManagement/productIdentifier/index.vue
@@ -239,6 +239,13 @@
        <el-form-item label="标识类型">
          <span>{{ currentProduct.identifierType }}</span>
        </el-form-item>
        <el-form-item label="创建时间">
          <el-date-picker v-model="createTime"
                          type="date"
                          placeholder="选择日期"
                          value-format="YYYY-MM-DD"
                          style="width: 100%"></el-date-picker>
        </el-form-item>
        <el-form-item label="生成数量"
                      prop="generateQuantity">
          <el-input-number v-model="generateQuantity"
@@ -442,6 +449,7 @@
  const generateQuantity = ref(1);
  const codeRule = ref("");
  const customPrefix = ref("");
  const createTime = ref(new Date().toISOString().split('T')[0]);
  const newBatchNo = ref("");
  const reassignReason = ref("");
  const formRef = ref();
@@ -659,9 +667,9 @@
          currentProduct.value.batchNo
        }_${String(i).padStart(3, "0")}`;
      } else if (codeRule.value === "时间戳+随机数") {
        identifierCode = `TS_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
        identifierCode = "";
      } else if (codeRule.value === "自定义规则") {
        identifierCode = `${customPrefix.value || "CUSTOM"}_${Date.now()}_${i}`;
        identifierCode = "";
      }
      newIdentifiers.push({
@@ -672,7 +680,7 @@
        identifierType: currentProduct.value.identifierType,
        identifierCode: identifierCode,
        status: "已生成",
        generateTime: new Date().toLocaleString(),
        generateTime: createTime.value,
        remark: "批量生成",
      });
    }
src/views/productionManagement/productionTraceability/index.vue
@@ -35,7 +35,7 @@
                         border>
          <el-descriptions-item label="生产订单号">{{ rowData.productionOrderDto?.npsNo || '-' }}</el-descriptions-item>
          <el-descriptions-item label="产品名称">{{ rowData.productionOrderDto?.productName || '-' }}</el-descriptions-item>
          <el-descriptions-item label="产品规格">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item>
          <el-descriptions-item label="规格型号">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item>
          <!-- <el-descriptions-item label="物料编码">{{ rowData.productionOrderDto?.materialCode || '-' }}</el-descriptions-item> -->
          <el-descriptions-item label="计划数量">{{ rowData.productionOrderDto?.quantity || 0 }} <span class="unit">{{ rowData.productionOrderDto?.unit || '-' }}</span></el-descriptions-item>
          <el-descriptions-item label="当前状态">
src/views/productionManagement/workOrder/index.vue
@@ -93,7 +93,7 @@
              <span class="info-value">{{ transferCardRowData.productName }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">产品规格</span>
              <span class="info-label">规格型号</span>
              <span class="info-value">{{ transferCardRowData.model }}</span>
            </div>
            <!-- <div class="info-item">
src/views/productionManagement/workOrderManagement/index.vue
@@ -57,7 +57,7 @@
              <span class="info-value">{{ transferCardRowData.productName }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">产品规格</span>
              <span class="info-label">规格型号</span>
              <span class="info-value">{{ transferCardRowData.model }}</span>
            </div>
            <div class="info-item">
src/views/productionPlan/productionPlan/index.vue
@@ -56,7 +56,7 @@
                    style="width: 160px;"
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item label="产品规格:"
        <el-form-item label="规格型号:"
                      prop="model">
          <el-input v-model="searchForm.model"
                    placeholder="请输入"
@@ -121,7 +121,7 @@
        </el-row>
        <el-row :gutter="20">
          <el-col>
            <el-form-item label="产品规格">
            <el-form-item label="规格型号">
              <div class="info-display">{{ mergeForm.model || '-' }}</div>
            </el-form-item>
          </el-col>
@@ -137,6 +137,12 @@
                           :min="0"
                           :max="sumAssignedQuantity"
                           @change="onBlur"
                           style="width: 100%" />
        </el-form-item>
        <el-form-item label="创建时间">
          <el-date-picker v-model="mergeForm.createTime"
                          type="date"
                          value-format="YYYY-MM-DD"
                           style="width: 100%" />
        </el-form-item>
      </el-form>
@@ -173,7 +179,7 @@
                      prop="mpsNo">
          <el-input v-model="form.mpsNo"
                    disabled
                    placeholder="新增后自动生成" />
                    placeholder="保存后自动生成" />
        </el-form-item>
        <el-form-item label="产品名称"
                      prop="productId">
@@ -186,7 +192,7 @@
                          @change="handleProductChange"
                          style="width: 100%" />
        </el-form-item>
        <el-form-item label="产品规格"
        <el-form-item label="规格型号"
                      prop="productModelId">
          <el-select v-model="form.productModelId"
                     @change="handleChangeSpecification"
@@ -226,6 +232,14 @@
                          value-format="YYYY-MM-DD"
                          style="width: 100%"
                          placeholder="请选择承诺日期" />
        </el-form-item>
        <el-form-item label="创建时间"
                      prop="createTime">
          <el-date-picker v-model="form.createTime"
                          type="date"
                          value-format="YYYY-MM-DD"
                          style="width: 100%"
                          placeholder="请选择创建时间" />
        </el-form-item>
        <el-form-item label="备注"
                      prop="remark">
@@ -321,7 +335,7 @@
      },
    },
    {
      label: "产品规格",
      label: "规格型号",
      prop: "model",
      width: "150px",
      className: "spec-cell",
@@ -430,6 +444,7 @@
              Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
            mergeForm.planCompleteTime = row.requiredDate || "";
            mergeForm.productId = row.productId || "";
            mergeForm.createTime = new Date().toISOString().split("T")[0];
            mergeForm.ids = [row.id];
            sumAssignedQuantity.value =
              Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
@@ -473,6 +488,7 @@
    totalAssignedQuantity: 0,
    planCompleteTime: "",
    productId: "",
    createTime: "",
  });
  // 导入相关
@@ -503,11 +519,12 @@
    requiredDate: "",
    promisedDeliveryDate: "",
    remark: "",
    createTime: "",
  });
  const rules = reactive({
    productId: [{ required: true, message: "请选择产品", trigger: "change" }],
    productModelId: [
      { required: true, message: "请选择产品规格", trigger: "change" },
      { required: true, message: "请选择规格型号", trigger: "change" },
    ],
    qtyRequired: [{ required: true, message: "请输入数量", trigger: "blur" }],
    requiredDate: [
@@ -686,17 +703,17 @@
      });
  };
  // 选中的产品规格ID
  // 选中的规格型号ID
  const selectedProductModelId = ref("");
  // 表格选择数据
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
    // 如果有选中的行,记录第一个选中行的产品规格ID
    // 如果有选中的行,记录第一个选中行的规格型号ID
    if (selection.length > 0) {
      selectedProductModelId.value = selection[0].productModelId;
    } else {
      // 如果没有选中的行,清空产品规格ID
      // 如果没有选中的行,清空规格型号ID
      selectedProductModelId.value = "";
    }
  };
@@ -717,7 +734,7 @@
    if (!selectedProductModelId.value) {
      return true;
    }
    // 如果有选中的行,只有产品规格ID相同的行才可选择
    // 如果有选中的行,只有规格型号ID相同的行才可选择
    return row.productModelId === selectedProductModelId.value;
  };
  // 拉取数据按钮操作
@@ -889,6 +906,7 @@
      requiredDate: "",
      promisedDeliveryDate: "",
      remark: "",
      createTime: new Date().toISOString().split("T")[0],
    });
    dialogVisible.value = true;
    fetchProductOptions();
src/views/qualityManagement/finalInspection/index.vue
@@ -1,33 +1,71 @@
<template>
  <div class="app-container">
    <div class="search_form mb20">
      <div>
        <span class="search_title">产品名称:</span>
        <el-input
            v-model="searchForm.productName"
      <el-form ref="searchFormRef"
               :model="searchForm"
               class="demo-form-inline">
        <el-row :gutter="20">
          <el-col :span="4">
            <el-form-item label="产品名称"
                          prop="productName">
              <el-input v-model="searchForm.productName"
            style="width: 240px"
            placeholder="请输入产品名称搜索"
            @change="handleQuery"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="6">
            <el-form-item label="检测日期"
                          prop="entryDate">
              <el-date-picker v-model="searchForm.entryDate"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="daterange"
                              placeholder="请选择"
            clearable
            :prefix-icon="Search"
        />
        <span style="margin-left: 10px" class="search_title">检测日期:</span>
        <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                        placeholder="请选择" clearable @change="changeDaterange"/>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索
        </el-button
        >
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')">新增</el-button>
                              @change="changeDaterange" />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="销售单号"
                          prop="salesContractNo">
              <el-input v-model="searchForm.salesContractNo"
                        style="width: 240px"
                        placeholder="请输入销售单号搜索"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="4">
            <el-form-item label="生产工单号"
                          prop="workOrderNo">
              <el-input v-model="searchForm.workOrderNo"
                        style="width: 240px"
                        placeholder="请输入生产工单号搜索"
                        clearable />
            </el-form-item>
          </el-col>
          <!-- 按钮 -->
          <el-col :span="4">
            <el-form-item>
              <el-button type="primary"
                         @click="getList">
                搜索
              </el-button>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div class="actions">
        <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>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
      <PIMTable rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
@@ -35,25 +73,39 @@
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
                :total="page.total"></PIMTable>
    </div>
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <files-dia ref="filesDia" @close="handleQuery"></files-dia>
    <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
    <InspectionFormDia ref="inspectionFormDia"
                       @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia"
             @close="handleQuery"></FormDia>
    <files-dia ref="filesDia"
               @close="handleQuery"></files-dia>
    <el-dialog v-model="dialogFormVisible"
               title="编辑检验员"
               width="30%"
               @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="检验员:" prop="checkName">
          <el-select v-model="form.checkName" placeholder="请选择" clearable>
            <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-form-item label="检验员:"
                      prop="checkName">
          <el-select v-model="form.checkName"
                     placeholder="请选择"
                     clearable>
            <el-option v-for="item in userList"
                       :key="item.nickName"
                       :label="item.nickName"
                       :value="item.nickName"/>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
@@ -63,15 +115,23 @@
<script setup>
import {Search} from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
  import {
    onMounted,
    ref,
    reactive,
    toRefs,
    getCurrentInstance,
    nextTick,
  } from "vue";
import InspectionFormDia from "@/views/qualityManagement/finalInspection/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/finalInspection/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {
  downloadQualityInspect,
  qualityInspectDel,
  qualityInspectListPage, qualityInspectUpdate,
  submitQualityInspect
    qualityInspectListPage,
    qualityInspectUpdate,
    submitQualityInspect,
} from "@/api/qualityManagement/rawMaterialInspection.js";
import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue";
import dayjs from "dayjs";
@@ -81,6 +141,8 @@
const data = reactive({
  searchForm: {
    productName: "",
      salesContractNo: "",
      workOrderNo: "",
    entryDate: undefined, // 录入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
@@ -94,17 +156,17 @@
  {
    label: "检测日期",
    prop: "checkTime",
    width: 120
      width: 120,
  },
  {
    label: "销售单号",
    prop: "salesContractNo",
    width: 120
      width: 120,
  },
  {
    label: "生产工单号",
    prop: "workOrderNo",
    width: 120
      width: 120,
  },
  {
    label: "检验员",
@@ -125,41 +187,58 @@
  {
    label: "总数量",
    prop: "quantity",
    width: 100
      width: 100,
  },
  {
    label: "合格数量",
    prop: "qualifiedQuantity",
    width: 100
      width: 100,
  },
  {
    label: "不合格数量",
    prop: "unqualifiedQuantity",
    width: 100
      width: 100,
    },
    {
      label: "合格率",
      prop: "passRate",
      width: 100,
      dataType: "tag",
      formatType: params => {
        if (!params) return "";
        const rate = parseFloat(params);
        if (rate < 90) {
          return "danger";
        } else if (rate === 100) {
          return "success";
        } else {
          return "warning";
        }
      },
  },
  {
    label: "检测单位",
    prop: "checkCompany",
    width: 120
      width: 120,
  },
  {
    label: "检测结果",
    prop: "checkResult",
    dataType: "tag",
    formatType: (params) => {
      if (params == '不合格') {
      formatType: params => {
        if (params == "不合格") {
        return "danger";
      } else if (params == '合格') {
        } else if (params == "合格") {
        return "success";
      } else {
        return 'danger';
          return "danger";
      }
    },
  },
  {
    label: "提交状态",
    prop: "inspectState",
    formatData: (params) => {
      formatData: params => {
      if (params) {
        return "已提交";
      } else {
@@ -177,10 +256,10 @@
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openForm("edit", row);
        },
        disabled: (row) => {
          disabled: row => {
          // 已提交则禁用
          if (row.inspectState == 1) return true;
          // 如果检验员有值,只有当前登录用户能编辑
@@ -188,29 +267,29 @@
            return row.checkName !== userStore.nickName;
          }
          return false;
        }
          },
      },
      {
        name: "查看",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openForm("view", row);
        },
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openFilesFormDia(row);
        },
      },
      {
        name: "提交",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          submit(row.id);
        },
        disabled: (row) => {
          disabled: row => {
          // 已提交则禁用
          if (row.inspectState == 1) return true;
          // 如果检验员有值,只有当前登录用户能提交
@@ -218,26 +297,26 @@
            return row.checkName !== userStore.nickName;
          }
          return false;
        }
          },
      },
      {
        name: "分配检验员",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          if (!row.checkName) {
            open(row)
              open(row);
          } else {
            proxy.$modal.msgError("检验员已存在");
          }
        },
        disabled: (row) => {
          disabled: row => {
          return row.inspectState == 1 || row.checkName;
        }
          },
      },
      {
        name: "下载",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          downLoadFile(row);
        },
      },
@@ -247,24 +326,24 @@
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const currentRow = ref(null)
  const currentRow = ref(null);
const page = reactive({
  current: 1,
  size: 100,
  total: 0
    total: 0,
});
const formDia = ref()
const filesDia = ref()
const inspectionFormDia = ref()
const {proxy} = getCurrentInstance()
const userStore = useUserStore()
  const formDia = ref();
  const filesDia = ref();
  const inspectionFormDia = ref();
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
const userList = ref([]);
const form = ref({
  checkName: ""
    checkName: "",
});
const dialogFormVisible = ref(false);
const changeDaterange = (value) => {
  const changeDaterange = value => {
  searchForm.value.entryDateStart = undefined;
  searchForm.value.entryDateEnd = undefined;
  if (value) {
@@ -279,7 +358,7 @@
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
@@ -287,44 +366,57 @@
const getList = () => {
  tableLoading.value = true;
  const params = {...searchForm.value, ...page};
  params.entryDate = undefined
  qualityInspectListPage({...params, inspectType: 2}).then(res => {
    params.entryDate = undefined;
    qualityInspectListPage({ ...params, inspectType: 2 })
      .then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
        tableData.value = res.data.records.map(item => {
          const quantity = parseFloat(item.quantity);
          const qualifiedQuantity = parseFloat(item.qualifiedQuantity);
          let passRate = null;
          if (!isNaN(quantity) && !isNaN(qualifiedQuantity) && quantity > 0) {
            passRate = ((qualifiedQuantity / quantity) * 100).toFixed(2) + "%";
          }
          return {
            ...item,
            passRate: passRate,
          };
        });
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
      .catch(err => {
        tableLoading.value = false;
      });
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
// 打开弹框
const openForm = (type, row) => {
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
      formDia.value?.openDialog(type, row);
    });
};
// 打开新增检验弹框
const openInspectionForm = (type, row) => {
  nextTick(() => {
    inspectionFormDia.value?.openDialog(type, row)
  })
      inspectionFormDia.value?.openDialog(type, row);
    });
};
// 打开附件弹框
const openFilesFormDia = (type, row) => {
  nextTick(() => {
    filesDia.value?.openDialog(type, row)
  })
      filesDia.value?.openDialog(type, row);
    });
};
// 删除
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -335,7 +427,7 @@
    type: "warning",
  })
      .then(() => {
        qualityInspectDel(ids).then((res) => {
        qualityInspectDel(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
@@ -352,7 +444,11 @@
    type: "warning",
  })
      .then(() => {
        proxy.download("/quality/qualityInspect/export", {inspectType: 2}, "出厂检验.xlsx");
        proxy.download(
          "/quality/qualityInspect/export",
          { inspectType: 2 },
          "出厂检验.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
@@ -360,13 +456,13 @@
};
// 提价
const submit = async (id) => {
  const res = await submitQualityInspect({id: id})
  const submit = async id => {
    const res = await submitQualityInspect({ id: id });
  if (res.code === 200) {
    proxy.$modal.msgSuccess("提交成功");
    getList();
  }
}
  };
// 关闭弹框
const closeDia = () => {
@@ -378,43 +474,49 @@
  if (currentRow.value) {
    const data = {
      ...form.value,
      id: currentRow.value.id
    }
        id: currentRow.value.id,
      };
    qualityInspectUpdate(data).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    })
      });
  }
};
const open = async (row) => {
  const open = async row => {
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  currentRow.value = row
  dialogFormVisible.value = true
}
    currentRow.value = row;
    dialogFormVisible.value = true;
  };
const downLoadFile = (row) => {
  downloadQualityInspect({id: row.id}).then((blobData) => {
  const downLoadFile = row => {
    downloadQualityInspect({ id: row.id }).then(blobData => {
    const blob = new Blob([blobData], {
      type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    })
    const downloadUrl = window.URL.createObjectURL(blob)
        type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      });
      const downloadUrl = window.URL.createObjectURL(blob);
    const link = document.createElement('a')
    link.href = downloadUrl
    link.download = '原材料检验报告.docx'
    document.body.appendChild(link)
    link.click()
      const link = document.createElement("a");
      link.href = downloadUrl;
      link.download = "原材料检验报告.docx";
      document.body.appendChild(link);
      link.click();
    document.body.removeChild(link)
    window.URL.revokeObjectURL(downloadUrl)
  })
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
    });
};
onMounted(() => {
  getList();
});
</script>
<style scoped></style>
<style scoped lang="scss">
  .actions {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 10px;
  }
</style>
src/views/qualityManagement/nearExpiryReturn/index.vue
@@ -1,25 +1,27 @@
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="产品名称" prop="productName">
        <el-input
          v-model="queryParams.productName"
    <el-form :model="queryParams"
             ref="queryForm"
             :inline="true"
             v-show="showSearch"
             label-width="68px">
      <el-form-item label="产品名称"
                    prop="productName">
        <el-input v-model="queryParams.productName"
          placeholder="请输入产品名称"
          clearable
          @keyup.enter.native="handleQuery"
        />
                  @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="批次号" prop="batchNumber">
        <el-input
          v-model="queryParams.batchNumber"
      <el-form-item label="批次号"
                    prop="batchNumber">
        <el-input v-model="queryParams.batchNumber"
          placeholder="请输入批次号"
          clearable
          @keyup.enter.native="handleQuery"
        />
                  @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="退回日期" prop="returnDate">
        <el-date-picker
          clearable
      <el-form-item label="退回日期"
                    prop="returnDate">
        <el-date-picker clearable
          v-model="queryParams.returnDate"
          type="date"
          value-format="YYYY-MM-DD"
@@ -27,125 +29,160 @@
        </el-date-picker>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
        <el-button type="primary"
                   icon="Search"
                   @click="handleQuery">搜索</el-button>
        <el-button icon="Refresh"
                   @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <el-row :gutter="10" class="mb8">
    <el-row :gutter="10"
            class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
        <el-button type="primary"
          plain
          icon="Plus"
          @click="handleAdd"
        >新增</el-button>
                   @click="handleAdd">新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
        <el-button type="success"
          plain
          icon="Edit"
          :disabled="single"
          @click="handleUpdate"
        >修改</el-button>
                   @click="handleUpdate">修改</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
        <el-button type="danger"
          plain
          icon="Delete"
          :disabled="multiple"
          @click="handleDelete"
        >删除</el-button>
                   @click="handleDelete">删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
        <el-button type="warning"
          plain
          icon="Download"
          @click="handleExport"
        >导出</el-button>
                   @click="handleExport">导出</el-button>
      </el-col>
      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
      <right-toolbar v-model:showSearch="showSearch"
                     @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table v-loading="loading" :data="nearExpiryReturnList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="序号" type="index" width="50" align="center" />
      <el-table-column label="产品名称" prop="productName" />
      <el-table-column label="产品规格" prop="productSpec" />
      <el-table-column label="批次号" prop="batchNumber" />
      <el-table-column label="生产日期" prop="productionDate" align="center">
    <el-table v-loading="loading"
              :data="nearExpiryReturnList"
              @selection-change="handleSelectionChange">
      <el-table-column type="selection"
                       width="55"
                       align="center" />
      <el-table-column label="序号"
                       type="index"
                       width="50"
                       align="center" />
      <el-table-column label="产品名称"
                       prop="productName" />
      <el-table-column label="规格型号"
                       prop="productSpec" />
      <el-table-column label="批次号"
                       prop="batchNumber" />
      <el-table-column label="生产日期"
                       prop="productionDate"
                       align="center">
        <template #default="scope">
          <span>{{ parseTime(scope.row.productionDate, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="到期日期" prop="expiryDate" align="center">
      <el-table-column label="到期日期"
                       prop="expiryDate"
                       align="center">
        <template #default="scope">
          <span>{{ parseTime(scope.row.expiryDate, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="退回数量" prop="returnQuantity" />
      <el-table-column label="退回原因" prop="returnReason" />
      <el-table-column label="退回日期" prop="returnDate" align="center">
      <el-table-column label="退回数量"
                       prop="returnQuantity" />
      <el-table-column label="退回原因"
                       prop="returnReason" />
      <el-table-column label="退回日期"
                       prop="returnDate"
                       align="center">
        <template #default="scope">
          <span>{{ parseTime(scope.row.returnDate, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="处理状态" prop="status" align="center">
      <el-table-column label="处理状态"
                       prop="status"
                       align="center">
        <template #default="scope">
          <dict-tag :options="statusOptions" :value="scope.row.status"/>
          <dict-tag :options="statusOptions"
                    :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
      <el-table-column label="操作"
                       align="center"
                       class-name="small-padding fixed-width">
        <template #default="scope">
          <el-button size="mini" type="text" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
          <el-button size="mini" type="text" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
          <el-button size="mini"
                     type="text"
                     icon="Edit"
                     @click="handleUpdate(scope.row)">修改</el-button>
          <el-button size="mini"
                     type="text"
                     icon="Delete"
                     @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <pagination
      v-show="total>0"
    <pagination v-show="total>0"
      :total="total"
      v-model:page="queryParams.pageNum"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />
                @pagination="getList" />
    <!-- 添加或修改临期退回台账对话框 -->
    <el-dialog :title="title" v-model="open" width="800px" append-to-body>
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
    <el-dialog :title="title"
               v-model="open"
               width="800px"
               append-to-body>
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="100px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="产品名称" prop="productName">
              <el-input v-model="form.productName" placeholder="请输入产品名称" />
            <el-form-item label="产品名称"
                          prop="productName">
              <el-input v-model="form.productName"
                        placeholder="请输入产品名称" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="产品规格" prop="productSpec">
              <el-input v-model="form.productSpec" placeholder="请输入产品规格" />
            <el-form-item label="规格型号"
                          prop="productSpec">
              <el-input v-model="form.productSpec"
                        placeholder="请输入规格型号" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="批次号" prop="batchNumber">
              <el-input v-model="form.batchNumber" placeholder="请输入批次号" />
            <el-form-item label="批次号"
                          prop="batchNumber">
              <el-input v-model="form.batchNumber"
                        placeholder="请输入批次号" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="退回数量" prop="returnQuantity">
              <el-input-number v-model="form.returnQuantity" controls-position="right" :min="1" />
            <el-form-item label="退回数量"
                          prop="returnQuantity">
              <el-input-number v-model="form.returnQuantity"
                               controls-position="right"
                               :min="1" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="生产日期" prop="productionDate">
              <el-date-picker
                clearable
            <el-form-item label="生产日期"
                          prop="productionDate">
              <el-date-picker clearable
                v-model="form.productionDate"
                type="date"
                value-format="YYYY-MM-DD"
@@ -154,9 +191,9 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="到期日期" prop="expiryDate">
              <el-date-picker
                clearable
            <el-form-item label="到期日期"
                          prop="expiryDate">
              <el-date-picker clearable
                v-model="form.expiryDate"
                type="date"
                value-format="YYYY-MM-DD"
@@ -167,9 +204,9 @@
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="退回日期" prop="returnDate">
              <el-date-picker
                clearable
            <el-form-item label="退回日期"
                          prop="returnDate">
              <el-date-picker clearable
                v-model="form.returnDate"
                type="date"
                value-format="YYYY-MM-DD"
@@ -178,36 +215,43 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="处理状态" prop="status">
              <el-select v-model="form.status" placeholder="请选择处理状态">
                <el-option
                  v-for="dict in statusOptions"
            <el-form-item label="处理状态"
                          prop="status">
              <el-select v-model="form.status"
                         placeholder="请选择处理状态">
                <el-option v-for="dict in statusOptions"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
                           :value="dict.value"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="退回原因" prop="returnReason">
              <el-input v-model="form.returnReason" type="textarea" placeholder="请输入退回原因" />
            <el-form-item label="退回原因"
                          prop="returnReason">
              <el-input v-model="form.returnReason"
                        type="textarea"
                        placeholder="请输入退回原因" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="备注" prop="remark">
              <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
            <el-form-item label="备注"
                          prop="remark">
              <el-input v-model="form.remark"
                        type="textarea"
                        placeholder="请输入备注" />
            </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 type="primary"
                     @click="submitForm">确 定</el-button>
          <el-button @click="cancel">取 消</el-button>
        </div>
      </template>
@@ -237,7 +281,7 @@
const statusOptions = ref([
  { label: "待处理", value: "0" },
  { label: "处理中", value: "1" },
  { label: "已完成", value: "2" }
    { label: "已完成", value: "2" },
]);
const data = reactive({
@@ -247,37 +291,37 @@
    pageSize: 10,
    productName: null,
    batchNumber: null,
    returnDate: null
      returnDate: null,
  },
  rules: {
    productName: [
      { required: true, message: "产品名称不能为空", trigger: "blur" }
        { required: true, message: "产品名称不能为空", trigger: "blur" },
    ],
    productSpec: [
      { required: true, message: "产品规格不能为空", trigger: "blur" }
        { required: true, message: "规格型号不能为空", trigger: "blur" },
    ],
    batchNumber: [
      { required: true, message: "批次号不能为空", trigger: "blur" }
        { required: true, message: "批次号不能为空", trigger: "blur" },
    ],
    returnQuantity: [
      { required: true, message: "退回数量不能为空", trigger: "blur" }
        { required: true, message: "退回数量不能为空", trigger: "blur" },
    ],
    productionDate: [
      { required: true, message: "生产日期不能为空", trigger: "blur" }
        { required: true, message: "生产日期不能为空", trigger: "blur" },
    ],
    expiryDate: [
      { required: true, message: "到期日期不能为空", trigger: "blur" }
        { required: true, message: "到期日期不能为空", trigger: "blur" },
    ],
    returnDate: [
      { required: true, message: "退回日期不能为空", trigger: "blur" }
        { required: true, message: "退回日期不能为空", trigger: "blur" },
    ],
    returnReason: [
      { required: true, message: "退回原因不能为空", trigger: "blur" }
        { required: true, message: "退回原因不能为空", trigger: "blur" },
    ],
    status: [
      { required: true, message: "处理状态不能为空", trigger: "change" }
    ]
  }
        { required: true, message: "处理状态不能为空", trigger: "change" },
      ],
    },
});
const { queryParams, form, rules } = toRefs(data);
@@ -310,7 +354,7 @@
    returnReason: null,
    returnDate: null,
    status: null,
    remark: null
      remark: null,
  };
  proxy.resetForm("formRef");
}
@@ -371,15 +415,21 @@
/** 删除按钮操作 */
function handleDelete(row) {
  const deleteIds = row.id || ids.value;
  ElMessageBox.confirm('是否确认删除临期退回台账编号为"' + deleteIds + '"的数据项?', "警告", {
    ElMessageBox.confirm(
      '是否确认删除临期退回台账编号为"' + deleteIds + '"的数据项?',
      "警告",
      {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning"
  }).then(function() {
        type: "warning",
      }
    )
      .then(function () {
    // 不调用接口,只显示成功提示
    proxy.$modal.msgSuccess("删除成功");
    getList();
  }).catch(() => {});
      })
      .catch(() => {});
}
/** 导出按钮操作 */
src/views/qualityManagement/processInspection/index.vue
@@ -3,30 +3,44 @@
    <div class="search_form mb20">
      <div>
        <span class="search_title">工序:</span>
        <el-input
            v-model="searchForm.process"
        <el-input v-model="searchForm.process"
            style="width: 240px"
            placeholder="请输入工序搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <span  style="margin-left: 10px" class="search_title">检测日期:</span>
        <el-date-picker  v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                         placeholder="请选择" clearable @change="changeDaterange" />
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button
        >
                  :prefix-icon="Search" />
        <span style="margin-left: 10px"
              class="search_title">检测日期:</span>
        <el-date-picker v-model="searchForm.entryDate"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        type="daterange"
                        placeholder="请选择"
                        clearable
                        @change="changeDaterange" />
        <span style="margin-left: 10px"
              class="search_title">生产工单号:</span>
        <el-input v-model="searchForm.workOrderNo"
                  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 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>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
      <PIMTable rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
@@ -34,25 +48,39 @@
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
                :total="page.total"></PIMTable>
    </div>
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <files-dia ref="filesDia" @close="handleQuery"></files-dia>
        <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
    <InspectionFormDia ref="inspectionFormDia"
                       @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia"
             @close="handleQuery"></FormDia>
    <files-dia ref="filesDia"
               @close="handleQuery"></files-dia>
    <el-dialog v-model="dialogFormVisible"
               title="编辑检验员"
               width="30%"
                             @close="closeDia">
            <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
                <el-form-item label="检验员:" prop="checkName">
                    <el-select v-model="form.checkName" placeholder="请选择" clearable>
                        <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-form-item label="检验员:"
                      prop="checkName">
          <el-select v-model="form.checkName"
                     placeholder="请选择"
                     clearable>
            <el-option v-for="item in userList"
                       :key="item.nickName"
                       :label="item.nickName"
                                             :value="item.nickName"/>
                    </el-select>
                </el-form-item>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
                    <el-button @click="closeDia">取消</el-button>
                </div>
            </template>
@@ -62,15 +90,23 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
  import {
    onMounted,
    ref,
    reactive,
    toRefs,
    getCurrentInstance,
    nextTick,
  } from "vue";
import InspectionFormDia from "@/views/qualityManagement/processInspection/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/processInspection/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {
    downloadQualityInspect,
    qualityInspectDel,
    qualityInspectListPage, qualityInspectUpdate,
    submitQualityInspect
    qualityInspectListPage,
    qualityInspectUpdate,
    submitQualityInspect,
} from "@/api/qualityManagement/rawMaterialInspection.js";
import FilesDia from "@/views/qualityManagement/processInspection/components/filesDia.vue";
import dayjs from "dayjs";
@@ -81,6 +117,7 @@
  searchForm: {
    process: "",
    entryDate: undefined, // 录入日期
      workOrderNo: "",
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
@@ -93,17 +130,17 @@
  {
    label: "检测日期",
    prop: "checkTime",
    width: 120
      width: 120,
  },
  {
    label: "生产工单号",
    prop: "workOrderNo",
    width: 120
      width: 120,
  },
  {
    label: "工序",
    prop: "process",
    width: 230
      width: 230,
  },
  {
    label: "检验员",
@@ -124,41 +161,58 @@
  {
    label: "总数量",
    prop: "quantity",
    width: 100
      width: 100,
  },
  {
    label: "合格数量",
    prop: "qualifiedQuantity",
    width: 100
      width: 100,
  },
  {
    label: "不合格数量",
    prop: "unqualifiedQuantity",
    width: 100
      width: 100,
    },
    {
      label: "合格率",
      prop: "passRate",
      width: 100,
      dataType: "tag",
      formatType: params => {
        if (!params) return "";
        const rate = parseFloat(params);
        if (rate < 90) {
          return "danger";
        } else if (rate === 100) {
          return "success";
        } else {
          return "warning";
        }
      },
  },
  {
    label: "检测单位",
    prop: "checkCompany",
    width: 120
      width: 120,
  },
  {
    label: "检测结果",
    prop: "checkResult",
    dataType: "tag",
    formatType: (params) => {
      if (params == '不合格') {
      formatType: params => {
        if (params == "不合格") {
        return "danger";
      } else if (params == '合格') {
        } else if (params == "合格") {
        return "success";
      } else {
        return 'danger';
          return "danger";
      }
    },
  },
    {
        label: "提交状态",
        prop: "inspectState",
        formatData: (params) => {
      formatData: params => {
            if (params) {
                return "已提交";
            } else {
@@ -176,10 +230,10 @@
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openForm("edit", row);
        },
                disabled: (row) => {
          disabled: row => {
                    // 已提交则禁用
                    if (row.inspectState == 1) return true;
                    // 如果检验员有值,只有当前登录用户能编辑
@@ -187,29 +241,29 @@
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
          },
      },
      {
        name: "查看",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openForm("view", row);
        },
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openFilesFormDia(row);
        },
      },
            {
                name: "提交",
                type: "text",
                clickFun: (row) => {
          clickFun: row => {
                    submit(row.id);
                },
                disabled: (row) => {
          disabled: row => {
                    // 已提交则禁用
                    if (row.inspectState == 1) return true;
                    // 如果检验员有值,只有当前登录用户能提交
@@ -217,26 +271,26 @@
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
          },
            },
            {
                name: "分配检验员",
                type: "text",
                clickFun: (row) => {
          clickFun: row => {
                    if (!row.checkName) {
                        open(row)
              open(row);
                    } else {
                        proxy.$modal.msgError("检验员已存在");
                    }
                },
                disabled: (row) => {
          disabled: row => {
                    return row.inspectState == 1 || row.checkName;
                }
          },
            },
            {
                name: "下载",
                type: "text",
                clickFun: (row) => {
          clickFun: row => {
                    downLoadFile(row);
                },
            },
@@ -244,25 +298,25 @@
  },
]);
const userList = ref([]);
const currentRow = ref(null)
  const currentRow = ref(null);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const dialogFormVisible = ref(false);
const form = ref({
    checkName: ""
    checkName: "",
});
const page = reactive({
  current: 1,
  size: 100,
  total: 0
    total: 0,
});
const formDia = ref()
const filesDia = ref()
const inspectionFormDia = ref()
const { proxy } = getCurrentInstance()
const userStore = useUserStore()
const changeDaterange = (value) => {
  const formDia = ref();
  const filesDia = ref();
  const inspectionFormDia = ref();
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
  const changeDaterange = value => {
  searchForm.value.entryDateStart = undefined;
  searchForm.value.entryDateEnd = undefined;
  if (value) {
@@ -277,7 +331,7 @@
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
@@ -285,52 +339,65 @@
const getList = () => {
  tableLoading.value = true;
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined
  qualityInspectListPage({...params, inspectType: 1}).then(res => {
    params.entryDate = undefined;
    qualityInspectListPage({ ...params, inspectType: 1 })
      .then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
        tableData.value = res.data.records.map(item => {
          const quantity = parseFloat(item.quantity);
          const qualifiedQuantity = parseFloat(item.qualifiedQuantity);
          let passRate = null;
          if (!isNaN(quantity) && !isNaN(qualifiedQuantity) && quantity > 0) {
            passRate = ((qualifiedQuantity / quantity) * 100).toFixed(2) + "%";
          }
          return {
            ...item,
            passRate: passRate,
          };
        });
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
      .catch(err => {
        tableLoading.value = false;
      });
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
// 打开弹框
const openForm = (type, row) => {
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
      formDia.value?.openDialog(type, row);
    });
};
// 打开新增检验弹框
const openInspectionForm = (type, row) => {
  nextTick(() => {
    inspectionFormDia.value?.openDialog(type, row)
  })
      inspectionFormDia.value?.openDialog(type, row);
    });
};
// 打开附件弹框
const openFilesFormDia = (type, row) => {
  nextTick(() => {
    filesDia.value?.openDialog(type, row)
  })
      filesDia.value?.openDialog(type, row);
    });
};
// 提价
const submit = async (id) => {
    const res = await submitQualityInspect({id: id})
  const submit = async id => {
    const res = await submitQualityInspect({ id: id });
    if (res.code === 200) {
        proxy.$modal.msgSuccess("提交成功");
        getList();
    }
}
const open = async (row) => {
  };
  const open = async row => {
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    currentRow.value = row
    dialogFormVisible.value = true
}
    currentRow.value = row;
    dialogFormVisible.value = true;
  };
// 关闭弹框
const closeDia = () => {
    proxy.resetForm("formRef");
@@ -340,13 +407,13 @@
    if (currentRow.value) {
        const data = {
            ...form.value,
            id: currentRow.value.id
        }
        id: currentRow.value.id,
      };
        qualityInspectUpdate(data).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            getList();
        })
      });
    }
};
@@ -354,7 +421,7 @@
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -365,7 +432,7 @@
    type: "warning",
  })
      .then(() => {
        qualityInspectDel(ids).then((res) => {
        qualityInspectDel(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
@@ -374,22 +441,22 @@
        proxy.$modal.msg("已取消");
      });
};
const downLoadFile = (row) => {
    downloadQualityInspect({ id: row.id }).then((blobData) => {
  const downLoadFile = row => {
    downloadQualityInspect({ id: row.id }).then(blobData => {
        const blob = new Blob([blobData], {
            type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        })
        const downloadUrl = window.URL.createObjectURL(blob)
        type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      });
      const downloadUrl = window.URL.createObjectURL(blob);
        const link = document.createElement('a')
        link.href = downloadUrl
        link.download = '过程检验报告.docx'
        document.body.appendChild(link)
        link.click()
      const link = document.createElement("a");
      link.href = downloadUrl;
      link.download = "过程检验报告.docx";
      document.body.appendChild(link);
      link.click();
        document.body.removeChild(link)
        window.URL.revokeObjectURL(downloadUrl)
    })
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
    });
};
// 导出
const handleOut = () => {
@@ -399,7 +466,11 @@
    type: "warning",
  })
      .then(() => {
        proxy.download("/quality/qualityInspect/export", {inspectType: 1}, "过程检验.xlsx");
        proxy.download(
          "/quality/qualityInspect/export",
          { inspectType: 1 },
          "过程检验.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -3,31 +3,45 @@
    <div class="search_form mb20">
      <div>
        <span class="search_title">供应商:</span>
        <el-input
            v-model="searchForm.supplier"
        <el-input v-model="searchForm.supplier"
            style="width: 240px"
            placeholder="请输入供应商搜索"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
        <span style="margin-left: 10px" class="search_title">检测日期:</span>
        <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                        placeholder="请选择" clearable @change="changeDaterange"/>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索
        </el-button
        >
                  :prefix-icon="Search" />
        <span style="margin-left: 10px"
              class="search_title">检测日期:</span>
        <el-date-picker v-model="searchForm.entryDate"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        type="daterange"
                        placeholder="请选择"
                        clearable
                        @change="changeDaterange" />
        <span style="margin-left: 10px"
              class="search_title">采购订单号:</span>
        <el-input v-model="searchForm.purchaseContractNo"
                  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 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>
    </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
      <PIMTable rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
@@ -35,44 +49,65 @@
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
                :total="page.total"></PIMTable>
    </div>
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <files-dia ref="filesDia" @close="handleQuery"></files-dia>
    <el-dialog v-model="dialogFormVisible" title="编辑检验员" width="30%"
    <InspectionFormDia ref="inspectionFormDia"
                       @close="handleQuery"></InspectionFormDia>
    <FormDia ref="formDia"
             @close="handleQuery"></FormDia>
    <files-dia ref="filesDia"
               @close="handleQuery"></files-dia>
    <el-dialog v-model="dialogFormVisible"
               title="编辑检验员"
               width="30%"
               @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-form-item label="检验员:" prop="checkName">
          <el-select v-model="form.checkName" placeholder="请选择" clearable>
            <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
      <el-form :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        <el-form-item label="检验员:"
                      prop="checkName">
          <el-select v-model="form.checkName"
                     placeholder="请选择"
                     clearable>
            <el-option v-for="item in userList"
                       :key="item.nickName"
                       :label="item.nickName"
                       :value="item.nickName"/>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {Search} from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
  import {
    onMounted,
    ref,
    reactive,
    toRefs,
    getCurrentInstance,
    nextTick,
  } from "vue";
import InspectionFormDia from "@/views/qualityManagement/rawMaterialInspection/components/inspectionFormDia.vue";
import FormDia from "@/views/qualityManagement/rawMaterialInspection/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {
  downloadQualityInspect,
  qualityInspectDel,
  qualityInspectListPage, qualityInspectUpdate,
  submitQualityInspect
    qualityInspectListPage,
    qualityInspectUpdate,
    submitQualityInspect,
} from "@/api/qualityManagement/rawMaterialInspection.js";
import FilesDia from "@/views/qualityManagement/rawMaterialInspection/components/filesDia.vue";
import dayjs from "dayjs";
@@ -83,6 +118,7 @@
  searchForm: {
    supplier: "",
    entryDate: undefined, // 录入日期
      purchaseContractNo: "",
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
@@ -95,17 +131,17 @@
  {
    label: "检测日期",
    prop: "checkTime",
    width: 120
      width: 120,
  },
  {
    label: "采购订单号",
    prop: "purchaseContractNo",
    width: 120
      width: 120,
  },
  {
    label: "供应商",
    prop: "supplier",
    width: 230
      width: 230,
  },
  {
    label: "检验员",
@@ -126,46 +162,63 @@
  {
    label: "总数量",
    prop: "quantity",
    width: 100
      width: 100,
  },
  {
    label: "合格数量",
    prop: "qualifiedQuantity",
    width: 100
      width: 100,
  },
  {
    label: "不合格数量",
    prop: "unqualifiedQuantity",
    width: 100
      width: 100,
    },
    {
      label: "合格率",
      prop: "passRate",
      width: 100,
      dataType: "tag",
      formatType: params => {
        if (!params) return "";
        const rate = parseFloat(params);
        if (rate < 90) {
          return "danger";
        } else if (rate === 100) {
          return "success";
        } else {
          return "warning";
        }
      },
  },
  {
    label: "检测单位",
    prop: "checkCompany",
    width: 120
      width: 120,
  },
  {
    label: "检测单位",
    prop: "checkCompany",
    width: 120
      width: 120,
  },
  {
    label: "检测结果",
    prop: "checkResult",
    dataType: "tag",
    formatType: (params) => {
      if (params === '不合格') {
      formatType: params => {
        if (params === "不合格") {
        return "danger";
      } else if (params === '合格') {
        } else if (params === "合格") {
        return "success";
      } else {
        return 'danger';
          return "danger";
      }
    },
  },
  {
    label: "提交状态",
    prop: "inspectState",
    formatData: (params) => {
      formatData: params => {
      if (params) {
        return "已提交";
      } else {
@@ -183,10 +236,10 @@
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openForm("edit", row);
        },
                disabled: (row) => {
          disabled: row => {
                    // 已提交则禁用
                    if (row.inspectState == 1) return true;
                    // 如果检验员有值,只有当前登录用户能编辑
@@ -194,29 +247,29 @@
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
          },
      },
      {
        name: "查看",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openForm("view", row);
        },
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openFilesFormDia(row);
        },
      },
      {
        name: "提交",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          submit(row.id);
        },
                disabled: (row) => {
          disabled: row => {
                    // 已提交则禁用
                    if (row.inspectState == 1) return true;
                    // 如果检验员有值,只有当前登录用户能提交
@@ -224,26 +277,26 @@
                        return row.checkName !== userStore.nickName;
                    }
                    return false;
                }
          },
      },
      {
        name: "分配检验员",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          if (!row.checkName) {
            open(row)
              open(row);
          } else {
            proxy.$modal.msgError("检验员已存在");
          }
        },
                disabled: (row) => {
          disabled: row => {
                    return row.inspectState == 1 || row.checkName;
                }
          },
      },
      {
        name: "下载",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          downLoadFile(row);
        },
      },
@@ -256,20 +309,20 @@
const userList = ref([]);
const dialogFormVisible = ref(false);
const form = ref({
  checkName: ""
    checkName: "",
});
const page = reactive({
  current: 1,
  size: 100,
  total: 0
    total: 0,
});
const currentRow = ref(null)
const formDia = ref()
const filesDia = ref()
const inspectionFormDia = ref()
const {proxy} = getCurrentInstance()
const userStore = useUserStore()
const changeDaterange = (value) => {
  const currentRow = ref(null);
  const formDia = ref();
  const filesDia = ref();
  const inspectionFormDia = ref();
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
  const changeDaterange = value => {
  searchForm.value.entryDateStart = undefined;
  searchForm.value.entryDateEnd = undefined;
  if (value) {
@@ -284,7 +337,7 @@
  page.current = 1;
  getList();
};
const pagination = (obj) => {
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
@@ -292,38 +345,51 @@
const getList = () => {
  tableLoading.value = true;
  const params = {...searchForm.value, ...page};
  params.entryDate = undefined
  qualityInspectListPage({...params, inspectType: 0}).then(res => {
    params.entryDate = undefined;
    qualityInspectListPage({ ...params, inspectType: 0 })
      .then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
        tableData.value = res.data.records.map(item => {
          const quantity = parseFloat(item.quantity);
          const qualifiedQuantity = parseFloat(item.qualifiedQuantity);
          let passRate = null;
          if (!isNaN(quantity) && !isNaN(qualifiedQuantity) && quantity > 0) {
            passRate = ((qualifiedQuantity / quantity) * 100).toFixed(2) + "%";
          }
          return {
            ...item,
            passRate: passRate,
          };
        });
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
      .catch(err => {
        tableLoading.value = false;
      });
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
// 打开弹框
const openForm = (type, row) => {
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
      formDia.value?.openDialog(type, row);
    });
};
// 打开附件弹框
const openFilesFormDia = (type, row) => {
  nextTick(() => {
    filesDia.value?.openDialog(type, row)
  })
      filesDia.value?.openDialog(type, row);
    });
};
// 删除
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -334,7 +400,7 @@
    type: "warning",
  })
      .then(() => {
        qualityInspectDel(ids).then((res) => {
        qualityInspectDel(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
@@ -351,7 +417,11 @@
    type: "warning",
  })
      .then(() => {
        proxy.download("/quality/qualityInspect/export", {inspectType: 0}, "原材料检验.xlsx");
        proxy.download(
          "/quality/qualityInspect/export",
          { inspectType: 0 },
          "原材料检验.xlsx"
        );
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
@@ -359,13 +429,13 @@
};
// 提价
const submit = async (id) => {
  const res = await submitQualityInspect({id: id})
  const submit = async id => {
    const res = await submitQualityInspect({ id: id });
  if (res.code === 200) {
    proxy.$modal.msgSuccess("提交成功");
    getList();
  }
}
  };
// 关闭弹框
const closeDia = () => {
@@ -377,39 +447,39 @@
  if (currentRow.value) {
    const data = {
      ...form.value,
      id: currentRow.value.id
    }
        id: currentRow.value.id,
      };
    qualityInspectUpdate(data).then(res => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
      getList();
    })
      });
  }
};
const open = async (row) => {
  const open = async row => {
  let userLists = await userListNoPage();
  userList.value = userLists.data;
  currentRow.value = row
  dialogFormVisible.value = true
}
    currentRow.value = row;
    dialogFormVisible.value = true;
  };
const downLoadFile = (row) => {
  downloadQualityInspect({ id: row.id }).then((blobData) => {
  const downLoadFile = row => {
    downloadQualityInspect({ id: row.id }).then(blobData => {
    const blob = new Blob([blobData], {
      type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    })
    const downloadUrl = window.URL.createObjectURL(blob)
        type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      });
      const downloadUrl = window.URL.createObjectURL(blob);
    const link = document.createElement('a')
    link.href = downloadUrl
    link.download = '原材料检验报告.docx'
    document.body.appendChild(link)
    link.click()
      const link = document.createElement("a");
      link.href = downloadUrl;
      link.download = "原材料检验报告.docx";
      document.body.appendChild(link);
      link.click();
    document.body.removeChild(link)
    window.URL.revokeObjectURL(downloadUrl)
  })
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
    });
};
onMounted(() => {
src/views/safeProduction/dangerInvestigation/index.vue
@@ -160,7 +160,7 @@
            <el-form-item label="隐患编号:"
                          prop="hiddenCode">
              <el-input v-model="form.hiddenCode"
                        placeholder="自动生成"
                        placeholder="保存后自动生成"
                        disabled
                        clearable />
            </el-form-item>
src/views/safeProduction/safetyTrainingAssessment/index.vue
@@ -76,7 +76,21 @@
                          prop="courseCode">
              <el-input v-model="form.courseCode"
                        disabled
                        placeholder="自动生成" />
                        placeholder="保存后自动生成" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="创建时间"
                          prop="createTime">
              <el-date-picker style="width: 100%"
                              v-model="form.createTime"
                              value-format="YYYY-MM-DD"
                              format="YYYY-MM-DD"
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
@@ -445,6 +459,7 @@
      trainingMode: "", // 培训方式
      placeTraining: "", // 培训地点
      classHour: "", // 课时
      createTime: "", // 创建时间
    },
    dialogVisible: false,
    dialogTitle: "",
@@ -1025,6 +1040,7 @@
        trainingMode: "", // 培训方式
        placeTraining: "", // 培训地点
        classHour: "", // 课时
        createTime: new Date().toISOString().split("T")[0], // 创建时间
      });
    } else if (type === "edit" && row) {
      dialogTitle.value = "编辑培训";
src/views/salesManagement/salesLedger/index.vue
@@ -1,42 +1,47 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="客户名称:">
          <el-input v-model="searchForm.customerName"
          <el-input
            v-model="searchForm.customerName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="销售合同号:">
          <el-input v-model="searchForm.salesContractNo"
          <el-input
            v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input v-model="searchForm.projectName"
          <el-input
            v-model="searchForm.projectName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    @change="handleQuery" />
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="录入日期:">
          <el-date-picker v-model="searchForm.entryDate"
          <el-date-picker
            v-model="searchForm.entryDate"
                          value-format="YYYY-MM-DD"
                          format="YYYY-MM-DD"
                          type="daterange"
                          placeholder="请选择"
                          clearable
                          @change="changeDaterange" />
            @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>
@@ -44,26 +49,19 @@
      <div class="actions">
        <div></div>
        <div>
          <el-button type="primary"
                     @click="openForm('add')">
          <el-button type="primary" @click="openForm('add')">
            新增台账
          </el-button>
          <el-button type="primary"
                     plain
                     @click="handleImport">导入
          <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>
          <el-button type="danger" plain @click="handleDelete">删除 </el-button>
          <el-button type="primary" plain @click="handlePrint">打印 </el-button>
        </div>
      </div>
      <el-table :data="tableData"
      <el-table
        :data="tableData"
                border
                v-loading="tableLoading"
                @selection-change="handleSelectionChange"
@@ -74,109 +72,108 @@
                style="width: 100%"
                :summary-method="summarizeMainTable"
                @expand-change="expandChange"
                height="calc(100vh - 18.5em)">
        <el-table-column align="center"
        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">
          fixed="left"
        />
        <el-table-column type="expand" width="60" fixed="left">
          <template #default="props">
            <el-table :data="props.row.children"
            <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-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="单位"
                               prop="unit" />
              <el-table-column label="产品状态"
                               width="100px"
                               align="center">
              :summary-method="
                (param) => summarizeChildrenTable(param, props.row)
              "
            >
              <el-table-column align="center" label="序号" type="index" />
              <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 "
                          type="success">充足
                  <el-tag v-if="scope.row.approveStatus === 1" type="success"
                    >充足
                  </el-tag>
                  <el-tag v-else-if="scope.row.approveStatus === 0 && scope.row.noQuantity === 0"
                          type="success">已出库
                  <el-tag
                    v-else-if="
                      scope.row.approveStatus === 0 &&
                      scope.row.noQuantity === 0
                    "
                    type="success"
                    >已出库
                  </el-tag>
                  <el-tag v-else
                          type="danger">不足
                  </el-tag>
                  <el-tag v-else type="danger">不足 </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="发货状态"
                               width="140"
                               align="center">
              <el-table-column label="发货状态" width="140" align="center">
                <template #default="scope">
                  <el-tag :type="getShippingStatusType(scope.row)"
                          size="small">
                  <el-tag :type="getShippingStatusType(scope.row)" size="small">
                    {{ getShippingStatusText(scope.row) }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="快递公司"
              <el-table-column
                label="快递公司"
                               prop="expressCompany"
                               show-overflow-tooltip />
              <el-table-column label="快递单号"
                show-overflow-tooltip
              />
              <el-table-column
                label="快递单号"
                               prop="expressNumber"
                               show-overflow-tooltip />
              <el-table-column label="发货车牌"
                               minWidth="100px"
                               align="center">
                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 type="success" v-if="scope.row.shippingCarNumber"
                      >{{ scope.row.shippingCarNumber }}
                    </el-tag>
                    <el-tag v-else
                            type="info">-
                    </el-tag>
                    <el-tag v-else type="info">- </el-tag>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="发货日期"
                               minWidth="100px"
                               align="center">
              <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 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="noQuantity" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="待发货数量" prop="noQuantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column
                label="含税单价(元)"
                               prop="taxInclusiveUnitPrice"
                               :formatter="sensitiveAmountFormatter" />
              <el-table-column label="含税总价(元)"
                :formatter="sensitiveAmountFormatter"
              />
              <el-table-column
                label="含税总价(元)"
                               prop="taxInclusiveTotalPrice"
                               :formatter="sensitiveAmountFormatter" />
              <el-table-column label="不含税总价(元)"
                :formatter="sensitiveAmountFormatter"
              />
              <el-table-column
                label="不含税总价(元)"
                               prop="taxExclusiveTotalPrice"
                               :formatter="sensitiveAmountFormatter" />
                :formatter="sensitiveAmountFormatter"
              />
              <!--操作-->
              <el-table-column Width="60px"
                               label="操作"
                               align="center">
              <el-table-column Width="60px" label="操作" align="center">
                <template #default="scope">
                  <el-button link
                  <el-button
                    link
                             type="primary"
                             :disabled="!canShip(scope.row)"
                             @click="openDeliveryForm(scope.row)">
                    @click="openDeliveryForm(scope.row)"
                  >
                    发货
                  </el-button>
                </template>
@@ -184,150 +181,198 @@
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center"
                         label="序号"
                         type="index"
                         width="60" />
        <el-table-column label="销售合同号"
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
          label="销售合同号"
                         prop="salesContractNo"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="客户名称"
          show-overflow-tooltip
        />
        <el-table-column
          label="客户名称"
                         prop="customerName"
                         width="300"
                         show-overflow-tooltip />
        <el-table-column label="业务员"
          show-overflow-tooltip
        />
        <el-table-column
          label="业务员"
                         prop="salesman"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="项目名称"
          show-overflow-tooltip
        />
        <el-table-column
          label="项目名称"
                         prop="projectName"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="付款方式"
          show-overflow-tooltip
        />
        <el-table-column
          label="付款方式"
                         prop="paymentMethod"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
          show-overflow-tooltip
        />
        <el-table-column
          label="合同金额(元)"
                         prop="contractAmount"
                         width="220"
                         show-overflow-tooltip
                         :formatter="formattedNumber" />
        <el-table-column label="录入人"
          :formatter="formattedNumber"
        />
        <el-table-column
          label="录入人"
                         prop="entryPersonName"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="录入日期"
          show-overflow-tooltip
        />
        <el-table-column
          label="录入日期"
                         prop="entryDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="签订日期"
          show-overflow-tooltip
        />
        <el-table-column
          label="签订日期"
                         prop="executionDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="交付日期"
          show-overflow-tooltip
        />
        <el-table-column
          label="交付日期"
                         prop="deliveryDate"
                         width="120"
                         show-overflow-tooltip />
        <el-table-column label="备注"
          show-overflow-tooltip
        />
        <el-table-column
          label="备注"
                         prop="remarks"
                         width="200"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
                         width="220"
                         align="center">
          show-overflow-tooltip
        />
        <el-table-column fixed="right" label="操作" width="220" align="center">
          <template #default="scope">
            <el-button link
                       type="primary"
                       @click="openForm('view', scope.row)">详情
            <el-button link type="primary" @click="openForm('view', scope.row)"
              >详情
            </el-button>
            <el-button link
            <el-button
              link
                       type="primary"
                       @click="openForm('edit', scope.row)"
                       :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">编辑
              :disabled="
                !scope.row.isEdit ||
                scope.row.hasProductionRecord ||
                !canEditLedger(scope.row)
              "
              >编辑
            </el-button>
            <el-button link
                       type="primary"
                       @click="openFileDialog(scope.row)">附件
            <el-button link type="primary" @click="openFileDialog(scope.row)"
              >附件
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0"
      <pagination
        v-show="total > 0"
                  :total="total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="page.current"
                  :limit="page.size"
                  @pagination="paginationChange" />
        @pagination="paginationChange"
      />
    </div>
    <FormDialog v-model="dialogFormVisible"
                :title="operationType === 'add' ? '新增销售台账页面' : (operationType === 'edit' ? '编辑销售台账页面' : '销售台账详情')"
    <FormDialog
      v-model="dialogFormVisible"
      :title="
        operationType === 'add'
          ? '新增销售台账页面'
          : operationType === 'edit'
          ? '编辑销售台账页面'
          : '销售台账详情'
      "
                :width="'70%'"
                :operation-type="operationType"
                @close="closeDia"
                @confirm="submitForm"
                @cancel="closeDia">
      <el-form :model="form"
      @cancel="closeDia"
    >
      <el-form
        :model="form"
               label-width="140px"
               label-position="top"
               :rules="rules"
               ref="formRef">
        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-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">
              <div style="display: flex; align-items: center; gap: 12px;width: 100%;">
                <el-checkbox v-model="form.autoGenerateContractNo"
                             v-if="operationType === 'add'">自动生成
            <el-form-item label="销售合同号:" prop="salesContractNo">
              <div
                style="
                  display: flex;
                  align-items: center;
                  gap: 12px;
                  width: 100%;
                "
              >
                <el-checkbox
                  v-model="form.autoGenerateContractNo"
                  v-if="operationType === 'add'"
                  >自动生成
                </el-checkbox>
                <el-input v-model="form.salesContractNo"
                          :placeholder="form.autoGenerateContractNo ? '自动生成' : '请输入'"
                <el-input
                  v-model="form.salesContractNo"
                  :placeholder="
                    form.autoGenerateContractNo ? '保存后自动生成' : '请输入'
                  "
                          clearable
                          :disabled="form.autoGenerateContractNo || operationType === 'view'" />
                  :disabled="
                    form.autoGenerateContractNo || operationType === 'view'
                  "
                />
              </div>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:"
                          prop="salesman">
              <el-select v-model="form.salesman"
            <el-form-item label="业务员:" prop="salesman">
              <el-select
                v-model="form.salesman"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'"
                         filterable>
                <el-option v-for="item in userList"
                filterable
              >
                <el-option
                  v-for="item in userList"
                           :key="item.nickName"
                           :label="item.nickName"
                           :value="item.nickName" />
                  :value="item.nickName"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户名称:"
                          prop="customerId">
              <el-select v-model="form.customerId"
            <el-form-item label="客户名称:" prop="customerId">
              <el-select
                v-model="form.customerId"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'"
                         filterable>
                <el-option v-for="item in customerOption"
                filterable
              >
                <el-option
                  v-for="item in customerOption"
                           :key="item.id"
                           :label="item.customerName"
                           :value="item.id">
                  :value="item.id"
                >
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                  }}
@@ -336,278 +381,320 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="项目名称:"
                          prop="projectName">
              <el-input v-model="form.projectName"
            <el-form-item label="项目名称:" prop="projectName">
              <el-input
                v-model="form.projectName"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
                :disabled="operationType === 'view'"
              />
            </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%"
            <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'" />
                :disabled="operationType === 'view'"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
              <el-input
                v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable
                        :disabled="operationType === 'view'" />
                :disabled="operationType === 'view'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入人:"
                          prop="entryPerson">
              <el-select v-model="form.entryPerson"
            <el-form-item label="录入人:" prop="entryPerson">
              <el-select
                v-model="form.entryPerson"
                         filterable
                         default-first-option
                         :reserve-keyword="false"
                         placeholder="请选择"
                         clearable
                         :disabled="operationType === 'view'"
                         @change="changs">
                <el-option v-for="item in userList"
                @change="changs"
              >
                <el-option
                  v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
                  :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%"
            <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
                              :disabled="operationType === 'view'" />
                :disabled="operationType === 'view'"
              />
            </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%"
            <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
                              :disabled="operationType === 'view'" />
                :disabled="operationType === 'view'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-form-item label="产品信息:"
                        prop="entryDate">
            <el-button v-if="operationType !== 'view'"
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button
              v-if="operationType !== 'view'"
                       type="primary"
                       @click="openProductForm('add')">添加
              @click="openProductForm('add')"
              >添加
            </el-button>
            <el-button v-if="operationType !== 'view'"
            <el-button
              v-if="operationType !== 'view'"
                       plain
                       type="danger"
                       @click="deleteProduct">删除
              @click="deleteProduct"
              >删除
            </el-button>
          </el-form-item>
        </el-row>
        <el-table :data="productData"
        <el-table
          :data="productData"
                  border
                  @selection-change="productSelected"
                  show-summary
                  :summary-method="summarizeMainTable">
          <el-table-column align="center"
          :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"
            :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="含税单价(元)"
            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="含税总价(元)"
            :formatter="formattedNumber"
          />
          <el-table-column
            label="含税总价(元)"
                           prop="taxInclusiveTotalPrice"
                           :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)"
            :formatter="formattedNumber"
          />
          <el-table-column
            label="不含税总价(元)"
                           prop="taxExclusiveTotalPrice"
                           :formatter="formattedNumber" />
          <el-table-column label="是否生产"
                           prop="isProduction"
                           width="150">
            :formatter="formattedNumber"
          />
          <el-table-column label="是否生产" prop="isProduction" width="150">
            <template #default="scope">
              <el-tag :type="scope.row.isProduction ? 'success' : 'info'">
                {{ scope.row.isProduction ? '是' : '否' }}
                {{ scope.row.isProduction ? "是" : "否" }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column fixed="right"
          <el-table-column
            fixed="right"
                           label="操作"
                           min-width="60"
                           align="center"
                           v-if="operationType !== 'view'">
            v-if="operationType !== 'view'"
          >
            <template #default="scope">
              <el-button link
              <el-button
                link
                         type="primary"
                         size="small"
                         :disabled="isProductShipped(scope.row)"
                         @click="openProductForm('edit', scope.row,scope.$index)">编辑
                @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"
            <el-form-item label="备注:" prop="remarks">
              <el-input
                v-model="form.remarks"
                        placeholder="请输入"
                        clearable
                        type="textarea"
                        :rows="2"
                        :disabled="operationType === 'view'" />
                :disabled="operationType === 'view'"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row v-if="operationType !== 'view'"
                :gutter="30">
        <el-row v-if="operationType !== 'view'" :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:"
                          prop="salesLedgerFiles">
              <FileUpload v-model:file-list="fileList"
                          :disabled="operationType === 'view'" />
            <el-form-item label="附件材料:" prop="salesLedgerFiles">
              <FileUpload
                v-model:file-list="fileList"
                :disabled="operationType === 'view'"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </FormDialog>
    <!-- 从报价单导入(仅审批通过) -->
    <el-dialog v-model="quotationDialogVisible"
    <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"
      :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"
          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>
          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"
      <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"
        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"
          show-overflow-tooltip
        />
        <el-table-column
          prop="customer"
                         label="客户名称"
                         min-width="220"
                         show-overflow-tooltip />
        <el-table-column prop="salesperson"
          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"
          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"
          align="center"
        />
        <el-table-column
          prop="totalAmount"
                         label="报价金额(元)"
                         width="160"
                         align="right">
          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">
        <el-table-column fixed="right" label="操作" width="120" align="center">
          <template #default="scope">
            <el-button type="primary"
                       link
                       @click="applyQuotation(scope.row)">选择
            <el-button type="primary" link @click="applyQuotation(scope.row)"
              >选择
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="quotationPage.total > 0"
      <pagination
        v-show="quotationPage.total > 0"
                  :total="quotationPage.total"
                  layout="total, sizes, prev, pager, next, jumper"
                  :page="quotationPage.current"
                  :limit="quotationPage.size"
                  @pagination="quotationPaginationChange" />
        @pagination="quotationPaginationChange"
      />
      <template #footer>
        <el-button @click="quotationDialogVisible = false">关闭</el-button>
      </template>
    </el-dialog>
    <FormDialog v-model="productFormVisible"
    <FormDialog
      v-model="productFormVisible"
                :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
                :width="'40%'"
                :operation-type="productOperationType"
                @close="closeProductDia"
                @confirm="submitProduct"
                @cancel="closeProductDia">
      <el-form :model="productForm"
      @cancel="closeProductDia"
    >
      <el-form
        :model="productForm"
               label-width="140px"
               label-position="top"
               :rules="productRules"
               ref="productFormRef">
        ref="productFormRef"
      >
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:"
                          prop="productCategory">
              <el-tree-select v-model="productForm.productCategory"
            <el-form-item label="产品大类:" prop="productCategory">
              <el-tree-select
                v-model="productForm.productCategory"
                              placeholder="请选择"
                              clearable
                              filterable
@@ -615,121 +702,132 @@
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
                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"
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select
                v-model="productForm.productModelId"
                         placeholder="请选择"
                         clearable
                         @change="getProductModel"
                         filterable>
                <el-option v-for="item in modelOptions"
                filterable
              >
                <el-option
                  v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
                  :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"
            <el-form-item label="单位:" prop="unit">
              <el-input
                v-model="productForm.unit"
                        placeholder="请输入"
                        clearable />
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="税率(%):"
                          prop="taxRate">
              <el-select v-model="productForm.taxRate"
            <el-form-item label="税率(%):" prop="taxRate">
              <el-select
                v-model="productForm.taxRate"
                         placeholder="请选择"
                         clearable
                         @change="calculateFromTaxRate">
                <el-option v-for="dict in tax_rate"
                @change="calculateFromTaxRate"
              >
                <el-option
                  v-for="dict in tax_rate"
                           :key="dict.value"
                           :label="dict.label"
                           :value="dict.value" />
                  :value="dict.value"
                />
              </el-select>
            </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"
            <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" />
                @change="calculateFromUnitPrice"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:"
                          prop="quantity">
              <el-input-number :step="0.1"
            <el-form-item label="数量:" prop="quantity">
              <el-input-number
                :step="0.1"
                               :min="0"
                               v-model="productForm.quantity"
                               placeholder="请输入"
                               clearable
                               :precision="2"
                               @change="calculateFromQuantity"
                               style="width: 100%" />
                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"
            <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
              <el-input
                v-model="productForm.taxInclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromTotalPrice" />
                @change="calculateFromTotalPrice"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(元):"
                          prop="taxExclusiveTotalPrice">
              <el-input v-model="productForm.taxExclusiveTotalPrice"
            <el-form-item
              label="不含税总价(元):"
              prop="taxExclusiveTotalPrice"
            >
              <el-input
                v-model="productForm.taxExclusiveTotalPrice"
                        placeholder="请输入"
                        clearable
                        @change="calculateFromExclusiveTotalPrice" />
                @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"
            <el-form-item label="发票类型:" prop="invoiceType">
              <el-select
                v-model="productForm.invoiceType"
                         placeholder="请选择"
                         clearable>
                <el-option label="增普票"
                           value="增普票" />
                <el-option label="增专票"
                           value="增专票" />
                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="isProduction">
            <el-form-item label="是否生产:" prop="isProduction">
              <el-radio-group v-model="productForm.isProduction">
                <el-radio label="是"
                          :value="true" />
                <el-radio label="否"
                          :value="false" />
                <el-radio label="是" :value="true" />
                <el-radio label="否" :value="false" />
              </el-radio-group>
            </el-form-item>
          </el-col>
@@ -737,13 +835,16 @@
      </el-form>
    </FormDialog>
    <!-- 导入弹窗 -->
    <FormDialog v-model="importUpload.open"
    <FormDialog
      v-model="importUpload.open"
                :title="importUpload.title"
                :width="'600px'"
                @close="importUpload.open = false"
                @confirm="submitImportFile"
                @cancel="importUpload.open = false">
      <el-upload ref="importUploadRef"
      @cancel="importUpload.open = false"
    >
      <el-upload
        ref="importUploadRef"
                 :limit="1"
                 accept=".xlsx,.xls"
                 :action="importUpload.url"
@@ -754,17 +855,15 @@
                 :on-progress="importUpload.onProgress"
                 :on-change="importUpload.onChange"
                 :auto-upload="false"
                 drag>
        drag
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将文件拖到此处,或<em>点击上传</em>
        </div>
        <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 link type="primary" @click="downloadTemplate"
              >下载导入模板
            </el-button>
          </div>
        </template>
@@ -772,35 +871,50 @@
    </FormDialog>
    <!-- // todo 附件预览相关 -->
    <!-- 附件列表弹窗 -->
    <FileList v-if="fileDialogVisible"
    <FileList
      v-if="fileDialogVisible"
              v-model:visible="fileDialogVisible"
              record-type="sales_ledger"
              :record-id="recordId" />
      :record-id="recordId"
    />
    <!-- 打印预览弹窗 -->
    <el-dialog v-model="printPreviewVisible"
    <el-dialog
      v-model="printPreviewVisible"
               title="打印预览"
               width="90%"
               :close-on-click-modal="false"
               class="print-preview-dialog">
      class="print-preview-dialog"
    >
      <div class="print-preview-container">
        <div class="print-preview-header">
          <el-button type="primary"
                     @click="executePrint">执行打印
          </el-button>
          <el-button type="primary" @click="executePrint">执行打印 </el-button>
          <el-button @click="printPreviewVisible = false">关闭预览</el-button>
        </div>
        <div class="print-preview-content">
          <div v-if="printData.length === 0"
               style="text-align: center; padding: 50px; color: #999;">
          <div
            v-if="printData.length === 0"
            style="text-align: center; padding: 50px; color: #999"
          >
            暂无打印数据
          </div>
          <div v-else
               style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
          <div
            v-else
            style="
              text-align: center;
              padding: 10px;
              color: #666;
              font-size: 14px;
              background: #e8f4fd;
              margin-bottom: 10px;
            "
          >
            共 {{ printData.length }} 条数据待打印
          </div>
          <div v-for="(item, index) in printData"
          <div
            v-for="(item, index) in printData"
               :key="index"
               class="print-page">
            class="print-page"
          >
            <div class="delivery-note">
              <div class="header">
                <div class="document-title">零售发货单</div>
@@ -838,18 +952,17 @@
                    </tr>
                  </thead>
                  <tbody>
                    <tr v-for="product in item.products"
                        :key="product.id">
                      <td>{{ product.productCategory || '' }}</td>
                      <td>{{ product.specificationModel || '' }}</td>
                      <td>{{ product.unit || '' }}</td>
                      <td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
                      <td>{{ product.quantity || '0' }}</td>
                      <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
                    <tr v-for="product in item.products" :key="product.id">
                      <td>{{ product.productCategory || "" }}</td>
                      <td>{{ product.specificationModel || "" }}</td>
                      <td>{{ product.unit || "" }}</td>
                      <td>{{ product.taxInclusiveUnitPrice || "0" }}</td>
                      <td>{{ product.quantity || "0" }}</td>
                      <td>{{ product.taxInclusiveTotalPrice || "0" }}</td>
                    </tr>
                    <tr v-if="!item.products || item.products.length === 0">
                      <td colspan="6"
                          style="text-align: center; color: #999;">暂无产品数据
                      <td colspan="6" style="text-align: center; color: #999">
                        暂无产品数据
                      </td>
                    </tr>
                  </tbody>
@@ -859,8 +972,12 @@
                      <td class="total-value"></td>
                      <td class="total-value"></td>
                      <td class="total-value"></td>
                      <td class="total-value">{{ getTotalQuantity(item.products) }}</td>
                      <td class="total-value">{{ getTotalAmount(item.products) }}</td>
                      <td class="total-value">
                        {{ getTotalQuantity(item.products) }}
                      </td>
                      <td class="total-value">
                        {{ getTotalAmount(item.products) }}
                      </td>
                    </tr>
                  </tfoot>
                </table>
@@ -883,7 +1000,9 @@
                <div class="footer-row">
                  <div class="footer-item">
                    <span class="label">操作员:</span>
                    <span class="value">{{ userStore.nickName || '撕开前' }}</span>
                    <span class="value">{{
                      userStore.nickName || "撕开前"
                    }}</span>
                  </div>
                  <div class="footer-item">
                    <span class="label">打印日期:</span>
@@ -897,116 +1016,127 @@
      </div>
    </el-dialog>
    <!-- 发货弹框 -->
    <el-dialog v-model="deliveryFormVisible"
    <el-dialog
      v-model="deliveryFormVisible"
               title="发货信息"
               width="40%"
               @close="closeDeliveryDia">
      <el-form :model="deliveryForm"
      @close="closeDeliveryDia"
    >
      <el-form
        :model="deliveryForm"
               label-width="120px"
               label-position="top"
               :rules="deliveryRules"
               ref="deliveryFormRef">
        ref="deliveryFormRef"
      >
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货类型:"
                          prop="type">
              <el-select v-model="deliveryForm.type"
            <el-form-item label="发货类型:" prop="type">
              <el-select
                v-model="deliveryForm.type"
                         placeholder="请选择发货类型"
                         style="width: 100%"
                         @change="handleDeliveryTypeChange">
                <el-option label="货车"
                           value="货车" />
                <el-option label="快递"
                           value="快递" />
                @change="handleDeliveryTypeChange"
              >
                <el-option label="货车" value="货车" />
                <el-option label="快递" value="快递" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="待发货数量:">
              <el-input :model-value="currentDeliveryRow?.noQuantity"
                        disabled />
              <el-input
                :model-value="currentDeliveryRow?.noQuantity"
                disabled
              />
            </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"
          <el-col :span="24" v-if="deliveryForm.type === '货车'">
            <el-form-item label="发货车牌号:" prop="shippingCarNumber">
              <el-input
                v-model="deliveryForm.shippingCarNumber"
                        placeholder="请输入发货车牌号"
                        clearable />
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="24"
                  v-else>
            <el-form-item label="快递公司:"
                          prop="expressCompany">
              <el-input v-model="deliveryForm.expressCompany"
          <el-col :span="24" v-else>
            <el-form-item label="快递公司:" prop="expressCompany">
              <el-input
                v-model="deliveryForm.expressCompany"
                        placeholder="请输入快递公司"
                        clearable />
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30"
                v-if="deliveryForm.type === '快递'">
        <el-row :gutter="30" v-if="deliveryForm.type === '快递'">
          <el-col :span="24">
            <el-form-item label="快递单号:"
                          prop="expressNumber">
              <el-input v-model="deliveryForm.expressNumber"
            <el-form-item label="快递单号:" prop="expressNumber">
              <el-input
                v-model="deliveryForm.expressNumber"
                        placeholder="请输入快递单号"
                        clearable />
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货图片:">
              <ImageUpload v-model:file-list="deliveryFileList"
                           :limit="9" />
              <ImageUpload v-model:file-list="deliveryFileList" :limit="9" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="库存:"
                          prop="batchNo">
              <el-table :data="deliveryForm.batchNoList"
            <el-form-item label="库存:" prop="batchNo">
              <el-table
                :data="deliveryForm.batchNoList"
                        border
                        size="small"
                        max-height="260"
                        style="width: 100%;">
                <el-table-column label="批号"
                                 prop="batchNo"
                                 min-width="180" />
                <el-table-column label="产品大类"
                style="width: 100%"
              >
                <el-table-column label="批号" prop="batchNo" min-width="180" />
                <el-table-column
                  label="产品大类"
                                 prop="productName"
                                 min-width="100" />
                <el-table-column label="规格型号"
                  min-width="100"
                />
                <el-table-column
                  label="规格型号"
                                 prop="model"
                                 min-width="100" />
                <el-table-column label="单位"
                                 prop="unit"
                                 min-width="100" />
                <el-table-column label="库存数量"
                  min-width="100"
                />
                <el-table-column label="单位" prop="unit" min-width="100" />
                <el-table-column
                  label="库存数量"
                                 min-width="120"
                                 align="center">
                  align="center"
                >
                  <template #default="scope">
                    {{ getDeliveryBatchQuantity(scope.row) }}
                  </template>
                </el-table-column>
                <el-table-column label="发货数量"
                <el-table-column
                  label="发货数量"
                                 min-width="160"
                                 align="center">
                  align="center"
                >
                  <template #default="scope">
                    <el-input-number v-model="scope.row.deliveryQuantity"
                    <el-input-number
                      v-model="scope.row.deliveryQuantity"
                                     :min="0"
                                     :max="getDeliveryBatchDeliveryMax(scope.row)"
                                     :precision="2"
                                     :step="0.01"
                                     controls-position="right"
                                     @change="handleDeliveryBatchQuantityChange(scope.row)"
                                     style="width: 100%;" />
                      style="width: 100%"
                    />
                  </template>
                </el-table-column>
              </el-table>
@@ -1016,8 +1146,8 @@
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitDelivery">确认发货
          <el-button type="primary" @click="submitDelivery"
            >确认发货
          </el-button>
          <el-button @click="closeDeliveryDia">取消</el-button>
        </div>
@@ -1106,6 +1236,7 @@
      productData: [],
      executionDate: "",
      hasProductionRecord: false,
    createTime: "",
    },
    rules: {
      salesman: [{ required: true, message: "请选择", trigger: "change" }],
@@ -1183,7 +1314,7 @@
  // 发货相关
  const deliveryFormVisible = ref(false);
  const currentDeliveryRow = ref(null);
  const getDeliveryBatchQuantity = item => {
const getDeliveryBatchQuantity = (item) => {
    const quantity =
      item?.qualitity ??
      item?.quantity ??
@@ -1196,7 +1327,7 @@
  const getCurrentDeliveryRowQuantity = () => {
    return Number(currentDeliveryRow.value?.noQuantity || 0);
  };
  const getDeliveryBatchDeliveryMax = row => {
const getDeliveryBatchDeliveryMax = (row) => {
    const productQuantity = getCurrentDeliveryRowQuantity();
    const batchQuantity = Number(getDeliveryBatchQuantity(row) || 0);
    const otherBatchTotal = (deliveryForm.value.batchNoList || []).reduce(
@@ -1212,7 +1343,7 @@
    );
    return Math.max(0, Math.min(batchQuantity, remainingProductQuantity));
  };
  const handleDeliveryBatchQuantityChange = row => {
const handleDeliveryBatchQuantityChange = (row) => {
    const productQuantity = getCurrentDeliveryRowQuantity();
    const batchQuantity = Number(getDeliveryBatchQuantity(row) || 0);
    const otherBatchTotal = (deliveryForm.value.batchNoList || []).reduce(
@@ -1238,10 +1369,10 @@
  };
  const getSelectedDeliveryBatchRows = () => {
    return (deliveryForm.value.batchNoList || []).filter(
      item => Number(item?.deliveryQuantity || 0) > 0
    (item) => Number(item?.deliveryQuantity || 0) > 0
    );
  };
  const getDeliveryBatchNoList = async productModelId => {
const getDeliveryBatchNoList = async (productModelId) => {
    if (!productModelId) return [];
    const res = await getStockInventoryByModelId(productModelId);
    const rawList = Array.isArray(res?.data)
@@ -1249,14 +1380,14 @@
      : res?.data?.records || res?.data?.rows || [];
    const seenIds = new Set();
    return rawList
      .filter(item => {
    .filter((item) => {
        if (!item?.id || !item?.batchNo || seenIds.has(item.id)) {
          return false;
        }
        seenIds.add(item.id);
        return true;
      })
      .map(item => ({
    .map((item) => ({
        ...item,
        deliveryQuantity: 0,
      }));
@@ -1300,7 +1431,7 @@
    url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: file => {
  beforeUpload: (file) => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
      const isLt10M = file.size / 1024 / 1024 < 10;
      if (!isExcel) {
@@ -1340,7 +1471,7 @@
    },
  });
  const changeDaterange = value => {
const changeDaterange = (value) => {
    if (value) {
      searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
      searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
@@ -1362,7 +1493,7 @@
    expandedRowKeys.value = [];
    getList();
  };
  const paginationChange = obj => {
const paginationChange = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
@@ -1375,10 +1506,10 @@
    // 移除录入日期的默认值设置,只保留范围日期字段
    delete params.entryDate;
    return ledgerListPage(params)
      .then(res => {
    .then((res) => {
        tableLoading.value = false;
        tableData.value = res.records;
        tableData.value.map(item => {
      tableData.value.map((item) => {
          item.children = [];
        });
        total.value = res.total;
@@ -1391,7 +1522,7 @@
  // 获取产品大类tree数据
  const getProductOptions = () => {
    // 返回 Promise,便于在编辑产品时等待加载完成
    return productTreeList().then(res => {
  return productTreeList().then((res) => {
      productOptions.value = convertIdToValue(res);
      return productOptions.value;
    });
@@ -1402,7 +1533,7 @@
    }
    return parseFloat(cellValue).toFixed(2);
  };
  const findLedgerRecordByRow = row => {
const findLedgerRecordByRow = (row) => {
    if (!row) return null;
    if (
      row.maintainer !== undefined ||
@@ -1415,13 +1546,13 @@
    if (row.salesLedgerId !== undefined && row.salesLedgerId !== null) {
      return (
        tableData.value.find(
          item => String(item.id) === String(row.salesLedgerId)
        (item) => String(item.id) === String(row.salesLedgerId)
        ) || null
      );
    }
    return null;
  };
  const isCurrentUserMaintainer = row => {
const isCurrentUserMaintainer = (row) => {
    const ledgerRecord = findLedgerRecordByRow(row);
    if (!ledgerRecord) return true;
    const currentUserId = String(userStore.id ?? "");
@@ -1447,8 +1578,8 @@
    }
    return true;
  };
  const canEditLedger = row => isCurrentUserMaintainer(row);
  const canDeleteLedger = row => isCurrentUserMaintainer(row);
const canEditLedger = (row) => isCurrentUserMaintainer(row);
const canDeleteLedger = (row) => isCurrentUserMaintainer(row);
  const sensitiveAmountFormatter = (row, column, cellValue) => {
    if (!isCurrentUserMaintainer(row)) {
      return "*****";
@@ -1456,14 +1587,14 @@
    return formattedNumber(row, column, cellValue);
  };
  // 获取tree子数据
  const getModels = value => {
const getModels = (value) => {
    productForm.value.productCategory = findNodeById(productOptions.value, value);
    modelList({ id: value }).then(res => {
  modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
  };
  const getProductModel = value => {
    const index = modelOptions.value.findIndex(item => item.id === 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;
@@ -1488,7 +1619,7 @@
  };
  function convertIdToValue(data) {
    return data.map(item => {
  return data.map((item) => {
      const { id, children, ...rest } = item;
      const newItem = {
        ...rest,
@@ -1517,12 +1648,12 @@
  }
  // 表格选择数据
  const handleSelectionChange = selection => {
const handleSelectionChange = (selection) => {
    // 过滤掉子数据
    selectedRows.value = selection.filter(item => item.children !== undefined);
  selectedRows.value = selection.filter((item) => item.children !== undefined);
    console.log("selection", selectedRows.value);
  };
  const productSelected = selectedRows => {
const productSelected = (selectedRows) => {
    productSelectedRows.value = selectedRows;
  };
  const expandedRowKeys = ref([]);
@@ -1531,8 +1662,8 @@
    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);
      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;
          }
@@ -1563,7 +1694,7 @@
    }
  };
  // 主表合计方法
  const summarizeMainTable = param => {
const summarizeMainTable = (param) => {
    return proxy.summarizeTable(param, [
      "contractAmount",
      "taxInclusiveTotalPrice",
@@ -1608,7 +1739,7 @@
    selectedQuotation.value = null;
    let userLists = await userListNoPage();
    userList.value = userLists.data;
    listCustomer({ current: -1, size: -1, type: 0 }).then(res => {
  listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
      customerOption.value = res.data.records;
    });
    form.value.entryPerson = userStore.id;
@@ -1617,11 +1748,13 @@
      form.value.entryDate = getCurrentDate();
      // 签订日期默认为当天
      form.value.executionDate = getCurrentDate();
    // 创建时间默认为当天
    form.value.createTime = getCurrentDate();
      // 默认自动生成销售合同号
      form.value.autoGenerateContractNo = true;
    } else {
      currentId.value = row.id;
      getSalesLedgerWithProducts({ id: row.id, type: 1 }).then(res => {
    getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
        form.value = { ...res };
        form.value.entryPerson = Number(res.entryPerson);
        productData.value = form.value.productData;
@@ -1649,7 +1782,7 @@
    // 先确保客户列表已加载,便于后续回填 customerId
    if (!customerOption.value || customerOption.value.length === 0) {
      try {
        listCustomer({ current: -1, size: -1 }).then(res => {
      listCustomer({ current: -1, size: -1 }).then((res) => {
          customerOption.value = res.data.records;
        });
      } catch (e) {
@@ -1685,14 +1818,14 @@
  };
  // 报价单弹框分页切换
  const quotationPaginationChange = obj => {
const quotationPaginationChange = (obj) => {
    quotationPage.current = obj.page;
    quotationPage.size = obj.limit;
    fetchQuotationList();
  };
  // 选中报价单后回填到台账表单
  const applyQuotation = row => {
const applyQuotation = (row) => {
    if (!row) return;
    selectedQuotation.value = row;
@@ -1701,7 +1834,7 @@
    // 客户名称 -> customerId
    const qCustomerName = String(row.customer || "").trim();
    const customer = (customerOption.value || []).find(c => {
  const customer = (customerOption.value || []).find((c) => {
      const name = String(c.customerName || "").trim();
      return (
        name === qCustomerName ||
@@ -1718,7 +1851,7 @@
    // 产品信息映射:报价 products -> 台账 productData
    const products = Array.isArray(row.products) ? row.products : [];
    productData.value = products.map(p => {
  productData.value = products.map((p) => {
      const quantity = Number(p.quantity ?? 0) || 0;
      const unitPrice = Number(p.unitPrice ?? 0) || 0;
      const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改)
@@ -1753,7 +1886,7 @@
  // 提交表单
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
  proxy.$refs["formRef"].validate((valid) => {
      if (valid) {
        console.log("productData.value--", productData.value);
        if (productData.value !== null && productData.value.length > 0) {
@@ -1767,7 +1900,7 @@
        if (form.value.autoGenerateContractNo) {
          form.value.salesContractNo = "";
        }
        addOrUpdateSalesLedger(form.value).then(res => {
      addOrUpdateSalesLedger(form.value).then((res) => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
          expandedRowKeys.value = [];
@@ -1815,7 +1948,7 @@
          modelOptions.value = models || [];
          // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
          const currentModel = (modelOptions.value || []).find(
            m => m.model === productForm.value.specificationModel
          (m) => m.model === productForm.value.specificationModel
          );
          if (currentModel) {
            productForm.value.productModelId = currentModel.id;
@@ -1832,7 +1965,7 @@
  };
  // 提交产品表单
  const submitProduct = () => {
    proxy.$refs["productFormRef"].validate(valid => {
  proxy.$refs["productFormRef"].validate((valid) => {
      if (valid) {
        if (operationType.value === "edit") {
          submitProductEdit();
@@ -1850,10 +1983,10 @@
  const submitProductEdit = () => {
    productForm.value.salesLedgerId = currentId.value;
    productForm.value.type = 1;
    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
      proxy.$modal.msgSuccess("提交成功");
      closeProductDia();
      getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(res => {
    getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
        productData.value = res.productData;
      });
    });
@@ -1866,7 +1999,7 @@
    }
    // 检查是否有已发货或审核通过的产品
    const shippedProducts = productSelectedRows.value.filter(row =>
  const shippedProducts = productSelectedRows.value.filter((row) =>
      isProductShipped(row)
    );
    if (shippedProducts.length > 0) {
@@ -1876,13 +2009,13 @@
    if (operationType.value === "add") {
      productData.value = productData.value.filter(
        item => !productSelectedRows.value.includes(item)
      (item) => !productSelectedRows.value.includes(item)
      );
      productSelectedRows.value = [];
    } else {
      let ids = [];
      if (productSelectedRows.value.length > 0) {
        ids = productSelectedRows.value.map(item => item.id);
      ids = productSelectedRows.value.map((item) => item.id);
      }
      ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
@@ -1890,11 +2023,11 @@
        type: "warning",
      })
        .then(() => {
          delProduct(ids).then(res => {
        delProduct(ids).then((res) => {
            proxy.$modal.msgSuccess("删除成功");
            closeProductDia();
            getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
              res => {
            (res) => {
                productData.value = res.productData;
              }
            );
@@ -1945,7 +2078,7 @@
      });
  };
  /** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
  const isProductShipped = product => {
const isProductShipped = (product) => {
    if (!product) return false;
    const status = String(product.shippingStatus || "").trim();
    // 如果发货状态是"已发货"或"审核通过",则不可编辑和删除
@@ -1953,9 +2086,9 @@
  };
  /** 判断销售订单下是否存在已发货/发货完成的产品(不可删除) */
  const hasShippedProducts = products => {
const hasShippedProducts = (products) => {
    if (!products || !products.length) return false;
    return products.some(p => {
  return products.some((p) => {
      const status = String(p.shippingStatus || "").trim();
      // 有发货日期或车牌号视为已发货
      if (p.shippingDate || p.shippingCarNumber) return true;
@@ -1973,13 +2106,13 @@
      return;
    }
    const unauthorizedRows = selectedRows.value.filter(
      row => !canDeleteLedger(row)
    (row) => !canDeleteLedger(row)
    );
    if (unauthorizedRows.length > 0) {
      proxy.$modal.msgWarning("当前登录用户不是录入人,不能删除该数据");
      return;
    }
    const ids = selectedRows.value.map(item => item.id);
  const ids = selectedRows.value.map((item) => item.id);
    // 检查是否有已进行发货或发货完成的销售订单,若有则不允许删除
    const cannotDeleteNames = [];
@@ -2011,7 +2144,7 @@
      type: "warning",
    })
      .then(() => {
        delLedger(ids).then(res => {
      delLedger(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
@@ -2268,7 +2401,9 @@
                                                                                                  0
                                                                                                  ? item.products
                                                                                                      .map(
                                                                                                        product => `
                                                                                                        (
                                                                                                          product
                                                                                                        ) => `
                                                                                                  <tr>
                                                                                                    <td>${
                                                                                                      product.productCategory ||
@@ -2375,7 +2510,7 @@
    };
  };
  // 格式化日期
  const formatDate = dateString => {
const formatDate = (dateString) => {
    if (!dateString) return getCurrentDate();
    const date = new Date(dateString);
    const year = date.getFullYear();
@@ -2384,7 +2519,7 @@
    return `${year}/${month}/${day}`;
  };
  // 格式化日期时间
  const formatDateTime = date => {
const formatDateTime = (date) => {
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
@@ -2394,7 +2529,7 @@
    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
  };
  // 计算产品总数量
  const getTotalQuantity = products => {
const getTotalQuantity = (products) => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
@@ -2403,7 +2538,7 @@
  };
  // 计算产品总金额
  const getTotalAmount = products => {
const getTotalAmount = (products) => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
@@ -2412,7 +2547,7 @@
  };
  // 用于打印的计算函数
  const getTotalQuantityForPrint = products => {
const getTotalQuantityForPrint = (products) => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
@@ -2420,7 +2555,7 @@
    return total.toFixed(2);
  };
  const getTotalAmountForPrint = products => {
const getTotalAmountForPrint = (products) => {
    if (!products || products.length === 0) return "0";
    const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
@@ -2606,7 +2741,7 @@
   * 获取发货状态文本
   * @param row 行数据
   */
  const getShippingStatusText = row => {
const getShippingStatusText = (row) => {
    // 如果已发货(有发货日期或车牌号),显示"已发货"
    // if (row.shippingDate || row.shippingCarNumber) {
    //   return "已发货";
@@ -2638,7 +2773,7 @@
   * 获取发货状态标签类型(颜色)
   * @param row 行数据
   */
  const getShippingStatusType = row => {
const getShippingStatusType = (row) => {
    // 如果已发货(有发货日期或车牌号),显示绿色
    if (row.shippingStatus === "已发货") {
      return "success";
@@ -2671,7 +2806,7 @@
   * 只有在产品状态是充足,发货状态是待发货和审核拒绝的时候才可以发货
   * @param row 行数据
   */
  const canShip = row => {
const canShip = (row) => {
    // 产品状态必须是充足(approveStatus === 1)
    if (row.approveStatus !== 1) {
      return false;
@@ -2699,13 +2834,13 @@
  const fileDialogVisible = ref(false);
  // 打开附件弹框
  const openFileDialog = async row => {
const openFileDialog = async (row) => {
    recordId.value = row.id;
    fileDialogVisible.value = true;
  };
  // 打开发货弹框
  const openDeliveryForm = async row => {
const openDeliveryForm = async (row) => {
    // 检查是否可以发货
    if (!canShip(row)) {
      proxy.$modal.msgWarning(
@@ -2732,7 +2867,7 @@
  // 提交发货表单
  const submitDelivery = () => {
    proxy.$refs["deliveryFormRef"].validate(valid => {
  proxy.$refs["deliveryFormRef"].validate((valid) => {
      if (valid) {
        const selectedBatchRows = getSelectedDeliveryBatchRows();
        if (selectedBatchRows.length === 0) {
@@ -2756,7 +2891,7 @@
        // 保存当前展开的行ID,以便发货后重新加载子表格数据
        const currentExpandedKeys = [...expandedRowKeys.value];
        const salesLedgerId = currentDeliveryRow.value.salesLedgerId;
        deliveryForm.value.batchNo = selectedBatchRows.map(item => item.id);
      deliveryForm.value.batchNo = selectedBatchRows.map((item) => item.id);
        const productModelId =
          currentDeliveryRow.value.productModelId ||
          currentDeliveryRow.value.modelId;
@@ -2778,7 +2913,7 @@
              : "",
          storageBlobDTOs: deliveryFileList.value || [],
          batchNo: deliveryForm.value.batchNo,
          batchNoDetailList: selectedBatchRows.map(item => ({
        batchNoDetailList: selectedBatchRows.map((item) => ({
            stockInventoryId: item.id,
            batchNo: item.batchNo,
            quantity: Number(item.deliveryQuantity || 0),
@@ -2792,11 +2927,11 @@
            // 如果之前有展开的行,重新加载这些行的子表格数据
            if (currentExpandedKeys.length > 0) {
              // 使用 Promise.all 并行加载所有展开行的子表格数据
              const loadPromises = currentExpandedKeys.map(ledgerId => {
            const loadPromises = currentExpandedKeys.map((ledgerId) => {
                return productList({ salesLedgerId: ledgerId, type: 1 }).then(
                  res => {
                (res) => {
                    const index = tableData.value.findIndex(
                      item => item.id === ledgerId
                    (item) => item.id === ledgerId
                    );
                    if (index > -1) {
                      tableData.value[index].children = res.data;
@@ -2816,7 +2951,7 @@
  };
  // 关闭发货弹框
  const handleDeliveryTypeChange = val => {
const handleDeliveryTypeChange = (val) => {
    if (val === "货车") {
      deliveryForm.value.expressCompany = "";
      deliveryForm.value.expressNumber = "";
@@ -2839,7 +2974,7 @@
  onMounted(() => {
    searchForm.salesContractNo = route.query.salesContractNo;
    getList();
    userListNoPage().then(res => {
  userListNoPage().then((res) => {
      userList.value = res.data;
    });
    getCurrentFactoryName();