liding
4 天以前 5ed0bc2c3b4ecfe79e33abb0a3d3636b467e8e46
src/views/procurementManagement/procurementLedger/index.vue
@@ -53,8 +53,7 @@
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="primary"
                   @click="openForm('add')">新增台账</el-button>
        <el-button type="success"
                   @click="openScanAddDialog">扫码新增</el-button>
        <el-button type="primary" plain @click="handleImport">导入</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger"
                   plain
@@ -69,8 +68,7 @@
                show-summary
                :summary-method="summarizeMainTable"
                @expand-change="expandChange"
                height="calc(100vh - 19em)"
                :row-class-name="tableRowClassName">
                height="calc(100vh - 21.5em)">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
@@ -88,6 +86,7 @@
                               prop="productCategory" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="UID码" prop="uidNo" />
              <el-table-column label="单位"
                               prop="unit" />
              <el-table-column label="数量"
@@ -112,40 +111,20 @@
                         width="60" />
        <el-table-column label="采购合同号"
                         prop="purchaseContractNumber"
                         width="200"
                         width="160"
                         show-overflow-tooltip />
        <el-table-column label="销售合同号"
                         prop="salesContractNo"
                          width="160"
                         show-overflow-tooltip />
        <el-table-column label="供应商名称"
                         prop="supplierName"
                          width="160"
                         show-overflow-tooltip />
        <el-table-column label="订单状态"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag v-if="scope.row.isInvalid"
                    type="danger"
                    size="small">失效</el-tag>
            <el-tag v-else
                    type="success"
                    size="small">正常</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="项目名称"
        <!-- <el-table-column label="项目名称"
                         prop="projectName"
                         width="420"
                         show-overflow-tooltip />
        <el-table-column label="审批状态"
                         prop="approvalStatus"
                         width="200"
                         show-overflow-tooltip>
          <template #default="scope">
            <el-tag size="small">
              {{ approvalStatusText[scope.row.approvalStatus] || '未知状态' }}
            </el-tag>
          </template>
        </el-table-column>
                         width="320"
                         show-overflow-tooltip /> -->
        <el-table-column label="签订日期"
                         prop="executionDate"
                         width="100"
@@ -167,19 +146,20 @@
                         prop="entryDate"
                         width="100"
                         show-overflow-tooltip />
        <el-table-column label="备注"
                         prop="remarks"
                         width="200"
                         show-overflow-tooltip />
        <el-table-column fixed="right"
                         label="操作"
                         width="180"
                         width="120"
                         align="center">
          <template #default="scope">
            <el-button link
                       type="primary"
                       size="small"
                       @click="openForm('edit', scope.row)">编辑</el-button>
            <el-button link
                       type="success"
                       size="small"
                       @click="showQRCode(scope.row)">生成二维码</el-button>
                       @click="openForm('edit', scope.row)"
                       :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">编辑</el-button>
            <el-button link
                       type="primary"
                       size="small"
@@ -194,10 +174,13 @@
                  :limit="page.size"
                  @pagination="paginationChange" />
    </div>
    <el-dialog v-model="dialogFormVisible"
    <FormDialog v-model="dialogFormVisible"
               :title="operationType === 'add' ? '新增采购台账页面' : '编辑采购台账页面'"
               width="70%"
               @close="closeDia">
               :width="'70%'"
               :operation-type="operationType"
               @close="closeDia"
               @confirm="submitForm"
               @cancel="closeDia">
      <el-form :model="form"
               label-width="140px"
               label-position="top"
@@ -239,25 +222,8 @@
                <el-option v-for="item in supplierList"
                           :key="item.id"
                           :label="item.supplierName"
                           :value="item.id" />
                                        :value="item.id" >{{item.supplierName + '---' + item.supplierType}}</el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="项目名称"
                          prop="projectName">
              <el-input v-model="form.projectName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -272,27 +238,33 @@
                              clearable />
            </el-form-item>
          </el-col>
          <!-- <el-col :span="12">
            <el-form-item label="项目名称"
                          prop="projectName">
              <el-input v-model="form.projectName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col> -->
        </el-row>
        <el-row :gutter="30">
          <!-- <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col> -->
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="审批人:"
                          prop="approverId">
              <el-select v-model="form.approverId"
                         placeholder="请选择审批人"
                         clearable>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
                           :value="item.userId" />
              </el-select>
            </el-form-item>
            <el-form-item label="录入人:"
                          prop="recorderId"
                          v-show="false">
                          prop="recorderId">
              <el-select v-model="form.recorderId"
                         placeholder="请选择"
                         clearable
                         disabled>
                         filterable>
                <el-option v-for="item in userList"
                           :key="item.userId"
                           :label="item.nickName"
@@ -322,33 +294,6 @@
                       type="danger"
                       @click="deleteProduct">删除</el-button>
          </el-form-item>
          <div class="select-button-group"
               style="width: 220px; margin: 20px 0;"
               v-if="operationType === 'add'">
            <el-select filterable
                       allow-create
                       :reserve-keyword="true"
                       :default-first-option="false"
                       v-model="templateName"
                       :input-value="filterInputValue"
                       @filter-change="onTemplateFilterChange"
                       @change="onTemplateChange"
                       style="width: 180px; border-right: none; border-radius: 4px 0 0 4px;"
                       placeholder="请选择"
                       class="no-arrow-select">
              <el-option v-for="item in templateList"
                         :key="item.value"
                         :label="item.templateName"
                         :value="item.templateName"></el-option>
            </el-select>
            <!-- 按钮:与 Select 高度匹配,去掉左侧边框,无缝衔接 -->
            <el-button size="small"
                       style="height: 32px; border-radius: 0 4px 4px 0; margin-left: -1px;"
                       @click="handleButtonClick"
                       :disabled="!templateName || templateName.trim() === '' || isTemplateNameDuplicate">
              保存
            </el-button>
          </div>
        </el-row>
        <el-table :data="productData"
                  border
@@ -369,6 +314,8 @@
          <el-table-column label="单位"
                           prop="unit"
                           width="70" />
          <el-table-column label="UID码" prop="uidNo" />
          <el-table-column label="批次号" prop="batchNo" />
          <el-table-column label="数量"
                           prop="quantity"
                           width="70" />
@@ -415,8 +362,8 @@
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="备注·:"
                          prop="remark">
              <el-input v-model="form.remark"
                          prop="remarks">
              <el-input v-model="form.remarks"
                        placeholder="请输入"
                        clearable
                        type="textarea"
@@ -427,7 +374,7 @@
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="附件材料:"
                          prop="remark">
                          prop="purchaseLedgerFiles">
              <el-upload v-model:file-list="fileList"
                         :action="upload.url"
                         multiple
@@ -450,18 +397,49 @@
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
    </FormDialog>
    <!-- 导入弹窗 -->
    <FormDialog
      v-model="importUpload.open"
      :title="importUpload.title"
      :width="'600px'"
      @close="importUpload.open = false"
      @confirm="submitImportFile"
      @cancel="importUpload.open = false"
    >
      <el-upload
        ref="importUploadRef"
        :limit="1"
        accept=".xlsx,.xls"
        :action="importUpload.url"
        :headers="importUpload.headers"
        :before-upload="importUpload.beforeUpload"
        :on-success="importUpload.onSuccess"
        :on-error="importUpload.onError"
        :on-progress="importUpload.onProgress"
        :on-change="importUpload.onChange"
        :auto-upload="false"
        drag
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将文件拖到此处,或<em>点击上传</em>
        </div>
      </template>
    </el-dialog>
    <el-dialog v-model="productFormVisible"
        <template #tip>
          <div class="el-upload__tip">
            仅支持 xls/xlsx,大小不超过 10MB。
            <el-button link type="primary" @click="downloadTemplate">下载导入模板</el-button>
          </div>
        </template>
      </el-upload>
    </FormDialog>
    <FormDialog v-model="productFormVisible"
               :title="productOperationType === 'add' ? '新增产品' : '编辑产品'"
               width="40%"
               @close="closeProductDia">
               :width="'40%'"
               :operation-type="productOperationType"
               @close="closeProductDia"
               @confirm="submitProduct"
               @cancel="closeProductDia">
      <el-form :model="productForm"
               label-width="140px"
               label-position="top"
@@ -498,6 +476,27 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="UID码:"
                          prop="uidNo">
              <el-input v-model="productForm.uidNo"
                        placeholder="请输入"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="批次号:"
                          prop="batchNo">
              <el-input v-model="productForm.batchNo"
                        placeholder="请输入"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单位:"
@@ -617,228 +616,12 @@
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitProduct">确认</el-button>
          <el-button @click="closeProductDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 二维码显示对话框 -->
    <el-dialog v-model="qrCodeDialogVisible"
               title="采购合同号二维码"
               width="400px"
               center>
      <div style="text-align: center;">
        <img :src="qrCodeUrl"
             alt="二维码"
             style="width:200px;height:200px;" />
        <div style="margin: 20px;">
          <el-button type="primary"
                     @click="downloadQRCode">下载二维码图片</el-button>
        </div>
      </div>
    </el-dialog>
    <!-- 扫码新增对话框 -->
    <el-dialog v-model="scanAddDialogVisible"
               title="扫码新增采购台账"
               width="70%"
               @close="closeScanAddDialog">
      <el-form :model="scanAddForm"
               label-width="140px"
               label-position="top"
               :rules="scanAddRules"
               ref="scanAddFormRef">
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="扫码内容:">
              <el-input v-model="scanAddForm.scanContent"
                        type="textarea"
                        :rows="3"
                        placeholder="请扫描二维码或手动输入采购合同信息"
                        @input="parseScanContent" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="采购合同号:"
                          prop="purchaseContractNumber">
              <el-input v-model="scanAddForm.purchaseContractNumber"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商名称:"
                          prop="supplierName">
              <el-input v-model="scanAddForm.supplierName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="项目名称:"
                          prop="projectName">
              <el-input v-model="scanAddForm.projectName"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="合同金额(元):"
                          prop="contractAmount">
              <el-input-number v-model="scanAddForm.contractAmount"
                               :precision="2"
                               :step="0.1"
                               clearable
                               style="width: 100%"
                               placeholder="请输入" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款方式:">
              <el-input v-model="scanAddForm.paymentMethod"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="录入人:">
              <el-input v-model="scanAddForm.recorderName"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="备注:">
              <el-input v-model="scanAddForm.remark"
                        type="textarea"
                        :rows="2"
                        placeholder="请输入备注信息"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitScanAdd">确认新增</el-button>
          <el-button @click="closeScanAddDialog">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 扫码登记对话框 -->
    <el-dialog v-model="scanDialogVisible"
               title="扫码登记"
               width="60%"
               @close="closeScanDialog">
      <el-form :model="scanForm"
               label-width="120px"
               label-position="left"
               :rules="scanRules"
               ref="scanFormRef">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="采购合同号:">
              <el-input v-model="scanForm.purchaseContractNumber"
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商名称:">
              <el-input v-model="scanForm.supplierName"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="项目名称:">
              <el-input v-model="scanForm.projectName"
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="扫码时间:">
              <el-input v-model="scanForm.scanTime"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="扫码人:">
              <el-input v-model="scanForm.scannerName"
                        disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="扫码状态:">
              <el-tag :type="scanForm.scanStatus === '已扫码' ? 'success' : 'warning'">
                {{ scanForm.scanStatus }}
              </el-tag>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="扫码备注:">
              <el-input v-model="scanForm.scanRemark"
                        type="textarea"
                        :rows="3"
                        placeholder="请输入扫码备注信息" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="扫码记录:">
              <el-table :data="scanRecords"
                        border
                        style="width: 100%">
                <el-table-column label="序号"
                                 type="index"
                                 width="60"
                                 align="center" />
                <el-table-column label="扫码时间"
                                 prop="scanTime"
                                 width="180" />
                <el-table-column label="扫码人"
                                 prop="scannerName"
                                 width="120" />
                <el-table-column label="扫码状态"
                                 prop="scanStatus"
                                 width="100">
                  <template #default="scope">
                    <el-tag :type="scope.row.scanStatus === '已扫码' ? 'success' : 'warning'">
                      {{ scope.row.scanStatus }}
                    </el-tag>
                  </template>
                </el-table-column>
                <el-table-column label="备注"
                                 prop="scanRemark" />
              </el-table>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="submitScan">确认扫码</el-button>
          <el-button @click="closeScanDialog">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <FileList ref="fileListRef" />
    </FormDialog>
    <FileListDialog
      ref="fileListRef"
      v-model="fileListDialogVisible"
      title="附件列表"
    />
  </div>
</template>
@@ -853,10 +636,11 @@
    getCurrentInstance,
    nextTick,
  } from "vue";
  import { Search } from "@element-plus/icons-vue";
  import { Search, Delete } from "@element-plus/icons-vue";
  import { ElMessageBox, ElMessage } from "element-plus";
  import { userListNoPage } from "@/api/system/user.js";
  import FileList from "./fileList.vue";
  import FormDialog from '@/components/Dialog/FormDialog.vue';
  import FileListDialog from '@/components/Dialog/FileListDialog.vue';
  import {
    getSalesLedgerWithProducts,
    addOrUpdateSalesLedgerProduct,
@@ -867,6 +651,7 @@
  import {
    addOrEditPurchase,
    addPurchaseTemplate,
    updatePurchaseTemplate,
    createPurchaseNo,
    delPurchase,
    getSalesNo,
@@ -875,9 +660,9 @@
    getPurchaseById,
    getOptions,
    getPurchaseTemplateList,
    delPurchaseTemplate,
  } from "@/api/procurementManagement/procurementLedger.js";
  import useFormData from "@/hooks/useFormData.js";
  import QRCode from "qrcode";
  const { proxy } = getCurrentInstance();
  const tableData = ref([]);
@@ -902,21 +687,12 @@
  const userStore = useUserStore();
  // 二维码相关变量
  const qrCodeDialogVisible = ref(false);
  const qrCodeUrl = ref("");
  // 订单审批状态显示文本
  const approvalStatusText = {
    0: "审批中",
    1: "审批通过",
    2: "审批失败",
  };
  const templateName = ref("");
  const filterInputValue = ref("");
  const templateList = ref([]);
  const isTemplateNameDuplicate = ref(false); // 标记模板名称是否重复
  // 当前选中的模板ID(用于区分新增模板还是更新模板)
  const currentTemplateId = ref(null);
  // 检查模板名称是否重复
  const checkTemplateNameDuplicate = name => {
@@ -970,22 +746,28 @@
    );
    if (matchedTemplate?.id) {
      // 如果找到模板,加载模板数据
      form.value = {
        ...form.value,
        ...matchedTemplate,
      };
      productData.value = matchedTemplate.productData || [];
      // 生成新的采购合同号
      try {
        const res = await createPurchaseNo();
        if (res?.data) {
          form.value.purchaseContractNumber = res.data;
        }
      } catch (error) {
        console.error("生成采购合同号失败:", error);
      // 记录当前选中的模板ID,后续保存时进行更新操作
      currentTemplateId.value = matchedTemplate.id;
      // 选中已有模板时,不应视为“模板名称重复导致不可保存”
      isTemplateNameDuplicate.value = false;
      // 如果找到模板,只赋值供应商、项目名称、付款方式和产品信息
      if (matchedTemplate.supplierId) {
        form.value.supplierId = matchedTemplate.supplierId;
      }
      if (matchedTemplate.supplierName) {
        form.value.supplierName = matchedTemplate.supplierName;
      }
      if (matchedTemplate.projectName) {
        form.value.projectName = matchedTemplate.projectName;
      }
      if (matchedTemplate.paymentMethod) {
        form.value.paymentMethod = matchedTemplate.paymentMethod;
      }
      // 模板数据中的产品字段是 productList,需要转换为 productData
      productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
    } else {
      // 未匹配到已有模板,视为新模板
      currentTemplateId.value = null;
      // 如果没有找到模板,重置表单(保持当前表单状态)
      const currentFormData = { ...form.value };
      const currentProductData = [...productData.value];
@@ -1036,9 +818,6 @@
      purchaseContractNumber: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      approverId: [
        { required: true, message: "请选择审批人", trigger: "change" },
      ],
      projectName: [
        { required: true, message: "请输入项目名称", trigger: "blur" },
      ],
@@ -1069,6 +848,8 @@
      productId: "",
      productCategory: "",
      productModelId: "",
      uidNo: "",
        batchNo: "",
      specificationModel: "",
      unit: "",
      quantity: "",
@@ -1083,6 +864,7 @@
    productRules: {
      productId: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      batchNo: [{ required: true, message: "请输入批次号", trigger: "blur" }],
      unit: [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      taxInclusiveUnitPrice: [
@@ -1107,6 +889,71 @@
    // 设置上传的请求头部
    headers: { Authorization: "Bearer " + getToken() },
  });
  // 导入相关
  const importUploadRef = ref(null);
  const importUpload = reactive({
    title: "导入采购台账",
    open: false,
    url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import",
    headers: { Authorization: "Bearer " + getToken() },
    isUploading: false,
    beforeUpload: (file) => {
      const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
      const isLt10M = file.size / 1024 / 1024 < 10;
      if (!isExcel) {
        proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!");
        return false;
      }
      if (!isLt10M) {
        proxy.$modal.msgError("上传文件大小不能超过 10MB!");
        return false;
      }
      return true;
    },
    onChange: (file, fileList) => {
      // noop
    },
    onProgress: (event, file, fileList) => {
      // noop
    },
    onSuccess: (response, file, fileList) => {
      importUpload.isUploading = false;
      if (response?.code === 200) {
        proxy.$modal.msgSuccess("导入成功");
        importUpload.open = false;
        if (importUploadRef.value) {
          importUploadRef.value.clearFiles?.();
        }
        getList();
      } else {
        proxy.$modal.msgError(response?.msg || "导入失败");
      }
    },
    onError: () => {
      importUpload.isUploading = false;
      proxy.$modal.msgError("导入失败,请重试");
    },
  });
  const handleImport = () => {
    importUpload.title = "导入采购台账";
    importUpload.open = true;
    importUpload.isUploading = false;
    if (importUploadRef.value) {
      importUploadRef.value.clearFiles?.();
    }
  };
  // 下载导入模板(如后端路径不同,可在此处调整)
  const downloadTemplate = () => {
    proxy.download("/purchase/ledger/exportTemplate", {}, "采购台账导入模板.xlsx");
  };
  const submitImportFile = () => {
    importUpload.isUploading = true;
    proxy.$refs["importUploadRef"]?.submit?.();
  };
  const changeDaterange = value => {
    if (value) {
@@ -1140,14 +987,17 @@
      return;
    }
    // 检查模板名称是否重复
    const isDuplicate = checkTemplateNameDuplicate(templateName.value);
    if (isDuplicate) {
      ElMessage({
        message: "模板名称已存在,请更换模板名称",
        type: "warning",
      });
      return;
    // 如果是“新增模板”(没有选中已有模板),才需要做重名校验;
    // 若是选中已有模板后修改,则允许使用原名称(视为更新)
    if (!currentTemplateId.value) {
      const isDuplicate = checkTemplateNameDuplicate(templateName.value);
      if (isDuplicate) {
        ElMessage({
          message: "模板名称已存在,请更换模板名称",
          type: "warning",
        });
        return;
      }
    }
    // 检查供应商是否选择
@@ -1160,13 +1010,13 @@
    }
    // 检查是否有产品数据
    // if (!productData.value || productData.value.length === 0) {
    //   ElMessage({
    //     message: '请先添加产品信息',
    //     type: 'warning',
    //   });
    //   return;
    // }
    if (!productData.value || productData.value.length === 0) {
      ElMessage({
        message: '请先添加产品信息',
        type: 'warning',
      });
      return;
    }
    try {
      let params = {
@@ -1174,15 +1024,26 @@
        supplierId: form.value.supplierId,
        paymentMethod: form.value.paymentMethod,
        recorderId: form.value.recorderId,
        approverId: form.value.approverId,
        projectName: form.value.projectName,
        templateName: templateName.value.trim(),
      };
      console.log(params);
      let res = await addPurchaseTemplate(params);
      console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value);
      // 如果 currentTemplateId 有值,说明当前是“编辑已有模板” → 调用更新接口
      // 否则为“新建模板” → 调用新增接口
      let res;
      if (currentTemplateId.value) {
        res = await updatePurchaseTemplate({
          id: currentTemplateId.value,
          ...params,
        });
      } else {
        res = await addPurchaseTemplate(params);
      }
      if (res && res.code === 200) {
        ElMessage({
          message: "模板保存成功",
          message: currentTemplateId.value ? "模板更新成功" : "模板保存成功",
          type: "success",
        });
        // 保存成功后重新获取模板列表
@@ -1191,6 +1052,8 @@
        templateName.value = "";
        filterInputValue.value = "";
        isTemplateNameDuplicate.value = false;
        // 保存/更新完成后,重置当前模板ID
        currentTemplateId.value = null;
      } else {
        ElMessage({
          message: res?.msg || "模板保存失败",
@@ -1238,7 +1101,6 @@
        // tableData.value = res.data.records;
        tableData.value = res.data.records.map(record => ({
          ...record,
          isInvalid: record.isWhite === 1,
        }));
        // 初始化子数据数组
        tableData.value.forEach(item => {
@@ -1412,7 +1274,15 @@
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (productData.value.length > 0) {
          form.value.productData = proxy.HaveJson(productData.value);
          // 新增时,需要从每个产品对象中删除 id 字段
          let processedProductData = productData.value;
          if (operationType.value === "add") {
            processedProductData = productData.value.map(product => {
              const { id, ...rest } = product;
              return rest;
            });
          }
          form.value.productData = proxy.HaveJson(processedProductData);
        } else {
          proxy.$modal.msgWarning("请添加产品信息");
          return;
@@ -1429,7 +1299,13 @@
          form.value.salesContractNo = "";
        }
        addOrEditPurchase(form.value).then(res => {
        // 新增时不传递id
        const submitData = { ...form.value };
        if (operationType.value === "add") {
          delete submitData.id;
        }
        addOrEditPurchase(submitData).then(res => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
          getList();
@@ -1443,32 +1319,96 @@
    dialogFormVisible.value = false;
  };
  // 打开产品弹框
  const openProductForm = (type, row, index) => {
  const openProductForm = async (type, row, index) => {
    productOperationType.value = type;
    productOperationIndex.value = index;
    productForm.value = {};
    proxy.resetForm("productFormRef");
    if (type === "edit") {
      productForm.value = { ...row };
    }
    productFormVisible.value = true;
    getProductOptions();
    // 先获取产品选项,确保数据加载完成
    await getProductOptions();
    // 等待 DOM 更新
    await nextTick();
    if (type === "edit") {
      // 复制行数据
      productForm.value = { ...row };
      // 如果是从模板加载的数据,可能没有 productId 和 productModelId
      // 需要根据 productCategory 和 specificationModel 来查找对应的 ID
      if (!productForm.value.productId && productForm.value.productCategory) {
        // 根据 productCategory 查找 productId
        const findProductIdByCategory = (nodes, categoryName) => {
          for (let i = 0; i < nodes.length; i++) {
            if (nodes[i].label === categoryName) {
              return nodes[i].value;
            }
            if (nodes[i].children && nodes[i].children.length > 0) {
              const found = findProductIdByCategory(nodes[i].children, categoryName);
              if (found) return found;
            }
          }
          return null;
        };
        const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory);
        if (productId) {
          productForm.value.productId = productId;
          // 获取型号列表并等待完成
          const modelRes = await modelList({ id: productId });
          modelOptions.value = modelRes;
          // 等待 DOM 更新
          await nextTick();
          // 根据 specificationModel 查找 productModelId
          if (productForm.value.specificationModel && modelOptions.value.length > 0) {
            const modelItem = modelOptions.value.find(
              item => item.model === productForm.value.specificationModel
            );
            if (modelItem) {
              productForm.value.productModelId = modelItem.id;
              // 设置规格型号和单位
              getProductModel(modelItem.id);
            }
          }
        }
      } else if (productForm.value.productId) {
        // 如果有 productId,正常加载型号列表
        await getModels(productForm.value.productId);
        // 等待 DOM 更新
        await nextTick();
        if (productForm.value.productModelId) {
          getProductModel(productForm.value.productModelId);
        }
      }
      // 最后再等待一次 DOM 更新,确保所有数据都已设置
      await nextTick();
    }
  };
  const getProductOptions = () => {
    productTreeList().then(res => {
    return productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
      return res;
    });
  };
  const getModels = value => {
    if (value) {
      productForm.value.productCategory =
        findNodeById(productOptions.value, value) || "";
      modelList({ id: value }).then(res => {
      return modelList({ id: value }).then(res => {
        modelOptions.value = res;
        return res;
      });
    } else {
      productForm.value.productCategory = "";
      modelOptions.value = [];
      return Promise.resolve([]);
    }
  };
  const getProductModel = value => {
@@ -1476,9 +1416,11 @@
    if (index !== -1) {
      productForm.value.specificationModel = modelOptions.value[index].model;
      productForm.value.unit = modelOptions.value[index].unit;
      productForm.value.uidNo = modelOptions.value[index].uidNo || "";
    } else {
      productForm.value.specificationModel = null;
      productForm.value.unit = null;
      productForm.value.uidNo = "";
    }
  };
  const findNodeById = (nodes, productId) => {
@@ -1569,7 +1511,7 @@
          delProduct(ids).then(res => {
            proxy.$modal.msgSuccess("删除成功");
            closeProductDia();
            getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
            getPurchaseById({ id: currentId.value, type: 2 }).then(
              res => {
                productData.value = res.productData;
              }
@@ -1604,14 +1546,6 @@
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      // 检查是否有他人维护的数据
      const unauthorizedData = selectedRows.value.filter(
        item => item.recorderName !== userStore.nickName
      );
      if (unauthorizedData.length > 0) {
        proxy.$modal.msgWarning("不可删除他人维护的数据");
        return;
      }
      ids = selectedRows.value.map(item => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
@@ -1773,205 +1707,11 @@
  };
  const fileListRef = ref(null);
  const fileListDialogVisible = ref(false);
  const downLoadFile = row => {
    fileListRef.value.open(row.salesLedgerFiles);
  };
  // 显示二维码
  const showQRCode = async row => {
    try {
      // 构建二维码内容,只包含采购合同号(纯文本)
      const qrContent = row.purchaseContractNumber || "";
      // 检查内容是否为空
      if (!qrContent || qrContent.trim() === "") {
        proxy.$modal.msgWarning("该行没有采购合同号,无法生成二维码");
        return;
      }
      qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
        width: 200,
        margin: 2,
        color: {
          dark: "#000000",
          light: "#FFFFFF",
        },
      });
      qrCodeDialogVisible.value = true;
    } catch (error) {
      console.error("生成二维码失败:", error);
      proxy.$modal.msgError("生成二维码失败:" + error.message);
    if (fileListRef.value) {
      fileListRef.value.open(row.salesLedgerFiles);
    }
  };
  // 下载二维码
  const downloadQRCode = () => {
    if (!qrCodeUrl.value) {
      proxy.$modal.msgWarning("二维码未生成");
      return;
    }
    const a = document.createElement("a");
    a.href = qrCodeUrl.value;
    a.download = `采购合同号二维码_${new Date().getTime()}.png`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    proxy.$modal.msgSuccess("下载成功");
  };
  // 扫码新增对话框相关变量
  const scanAddDialogVisible = ref(false);
  const scanAddForm = reactive({
    scanContent: "",
    purchaseContractNumber: "",
    supplierName: "",
    projectName: "",
    contractAmount: "",
    paymentMethod: "",
    recorderName: "",
    scanRemark: "",
  });
  const scanAddRules = {
    purchaseContractNumber: [
      { required: true, message: "请输入采购合同号", trigger: "blur" },
    ],
    supplierName: [
      { required: true, message: "请输入供应商名称", trigger: "blur" },
    ],
    projectName: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
  };
  // 扫码登记对话框相关变量
  const scanDialogVisible = ref(false);
  const scanForm = reactive({
    purchaseContractNumber: "",
    supplierName: "",
    projectName: "",
    scanTime: "",
    scannerName: "",
    scanStatus: "未扫码",
    scanRemark: "",
  });
  const scanRules = {
    scanRemark: [{ required: true, message: "请输入扫码备注", trigger: "blur" }],
  };
  const scanRecords = ref([]);
  // 打开扫码新增对话框
  const openScanAddDialog = () => {
    scanAddForm.scanContent = "";
    scanAddForm.purchaseContractNumber = "";
    scanAddForm.supplierName = "";
    scanAddForm.projectName = "";
    scanAddForm.contractAmount = "";
    scanAddForm.paymentMethod = "";
    scanAddForm.recorderName = userStore.nickName;
    scanAddForm.scanRemark = "";
    scanAddDialogVisible.value = true;
  };
  // 解析扫码内容(模拟解析二维码数据)
  const parseScanContent = content => {
    if (!content) return;
    // 模拟解析二维码内容,这里可以根据实际需求调整解析逻辑
    // 假设扫码内容格式为:合同号|供应商|金额|付款方式
    const parts = content.split("|");
    if (parts.length >= 2) {
      scanAddForm.purchaseContractNumber = parts[0] || "";
      scanAddForm.supplierName = parts[1] || "";
      scanAddForm.contractAmount = parts[2] || "";
      scanAddForm.paymentMethod = parts[3] || "";
      scanAddForm.projectName = parts[4] || "";
      // scanAddForm.contractAmount = parts[3] || "";
      // scanAddForm.paymentMethod = parts[4] || "";
    }
  };
  // 关闭扫码新增对话框
  const closeScanAddDialog = () => {
    scanAddDialogVisible.value = false;
    proxy.resetForm("scanAddFormRef");
  };
  // 提交扫码新增
  const submitScanAdd = () => {
    proxy.$refs["scanAddFormRef"].validate(valid => {
      if (valid) {
        // 构建新增数据
        const newData = {
          purchaseContractNumber: scanAddForm.purchaseContractNumber,
          supplierName: scanAddForm.supplierName,
          projectName: scanAddForm.projectName,
          contractAmount: scanAddForm.contractAmount,
          paymentMethod: scanAddForm.paymentMethod,
          recorderName: scanAddForm.recorderName,
          entryDate: getCurrentDate(),
          remark: scanAddForm.scanRemark,
          type: 2,
        };
        // 模拟新增成功
        proxy.$modal.msgSuccess("扫码新增成功!");
        closeScanAddDialog();
        // 可以选择是否刷新列表
        // getList();
      }
    });
  };
  // 打开扫码登记对话框
  const openScanDialog = row => {
    scanForm.purchaseContractNumber = row.purchaseContractNumber;
    scanForm.supplierName = row.supplierName;
    scanForm.projectName = row.projectName;
    scanForm.scanTime = getCurrentDateTime();
    scanForm.scannerName = userStore.nickName;
    scanForm.scanStatus = "未扫码";
    scanForm.scanRemark = "";
    scanRecords.value = [];
    scanDialogVisible.value = true;
  };
  // 关闭扫码登记对话框
  const closeScanDialog = () => {
    scanDialogVisible.value = false;
    proxy.resetForm("scanFormRef");
  };
  // 提交扫码登记
  const submitScan = () => {
    proxy.$refs["scanFormRef"].validate(valid => {
      if (valid) {
        // 添加扫码记录
        scanRecords.value.push({
          ...scanForm,
          id: Date.now(), // 模拟ID
          scanTime: getCurrentDateTime(),
        });
        scanForm.scanStatus = "已扫码";
        scanForm.scanRemark = scanForm.scanRemark || "无";
        proxy.$modal.msgSuccess("扫码登记成功!");
        closeScanDialog();
      }
    });
  };
  // 获取当前日期时间
  function getCurrentDateTime() {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, "0");
    const day = String(now.getDate()).padStart(2, "0");
    const hours = String(now.getHours()).padStart(2, "0");
    const minutes = String(now.getMinutes()).padStart(2, "0");
    const seconds = String(now.getSeconds()).padStart(2, "0");
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  }
  // 添加行类名方法
  const tableRowClassName = ({ row }) => {
    return row.isInvalid ? "invalid-row" : "";
  };
  // 获取模板信息
@@ -1979,6 +1719,54 @@
    let res = await getPurchaseTemplateList();
    if (res && res.code === 200 && Array.isArray(res.data)) {
      templateList.value = res.data;
    }
  };
  // 删除模板
  const handleDeleteTemplate = async (item) => {
    if (!item.id) {
      proxy.$modal.msgWarning("无法删除该模板");
      return;
    }
    try {
      await ElMessageBox.confirm(
        `确定要删除模板"${item.templateName}"吗?`,
        "删除确认",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      );
      const res = await delPurchaseTemplate([item.id]);
      if (res && res.code === 200) {
        ElMessage({
          message: "删除成功",
          type: "success",
        });
        // 如果删除的是当前选中的模板,清空选择
        if (templateName.value === item.templateName) {
          templateName.value = "";
          filterInputValue.value = "";
        }
        // 重新获取模板列表
        await getTemplateList();
      } else {
        ElMessage({
          message: res?.msg || "删除失败",
          type: "error",
        });
      }
    } catch (error) {
      if (error !== "cancel") {
        console.error("删除模板失败:", error);
        ElMessage({
          message: "删除失败,请稍后重试",
          type: "error",
        });
      }
    }
  };
@@ -2004,4 +1792,13 @@
    display: flex;
    align-items: center;
  }
  // 删除图标样式
  .delete-icon {
    transition: all 0.3s;
    &:hover {
      color: #f56c6c !important;
      transform: scale(1.2);
    }
  }
</style>