zhangwencui
2026-02-12 ab8c184a629b4e92f7b472e2e5cbb870120afee4
src/views/procurementManagement/invoiceEntry/components/Modal.vue
@@ -96,22 +96,6 @@
                  />
               </el-form-item>
            </el-col>
            <el-col :span="12">
               <el-form-item label="上传附件">
                  <FileUpload
                     :showTip="false"
                     accept="*"
                     :autoUpload="true"
                     :action="action"
                     :headers="{
                Authorization: 'Bearer ' + getToken(),
              }"
                     :limit="10"
                     @success="uploadSuccess"
                     @remove="removeFile"
                  />
               </el-form-item>
            </el-col>
         
         </el-row>
         <el-form-item label="产品信息:"> </el-form-item>
@@ -149,10 +133,15 @@
            />
            <el-table-column label="本次开票数" prop="ticketsNum" width="180">
               <template #default="scope">
                  <el-input-number :step="0.1" :min="0" style="width: 100%"
                                           :precision="2"
                                           v-model="scope.row.ticketsNum"
                                           @change="invoiceNumBlur(scope.row)"
                  <el-input-number
                     :step="0.1"
                     :min="0"
                     :max="scope.row.tempFutureTickets || 0"
                     style="width: 100%"
                     :precision="2"
                     v-model="scope.row.ticketsNum"
                     :disabled="isProductDisabled(scope.row)"
                     @change="invoiceNumBlur(scope.row)"
                  />
               </template>
            </el-table-column>
@@ -162,10 +151,14 @@
               width="180"
            >
               <template #default="scope">
                  <el-input-number :step="0.01" :min="0" style="width: 100%"
                                           :precision="2"
                                           v-model="scope.row.ticketsAmount"
                                           @change="invoiceAmountBlur(scope.row)"
                  <el-input-number
                     :step="0.01"
                     :min="0"
                     style="width: 100%"
                     :precision="2"
                     v-model="scope.row.ticketsAmount"
                     :disabled="isProductDisabled(scope.row)"
                     @change="invoiceAmountBlur(scope.row)"
                  />
               </template>
            </el-table-column>
@@ -205,14 +198,12 @@
import { defineEmits } from 'vue';
import { useModal } from "@/hooks/useModal";
import useFormData from "@/hooks/useFormData";
import FileUpload from "@/components/Upload/FileUpload.vue";
import {
   getPurchaseNoById,
   getInfo,
   addOrUpdateRegistration,
} from "@/api/procurementManagement/invoiceEntry.js";
import { getPurchaseById } from "@/api/procurementManagement/procurementLedger.js";
import { getToken } from "@/utils/auth";
import useUserStore from "@/store/modules/user";
import dayjs from "dayjs";
@@ -221,7 +212,6 @@
});
const userStore = useUserStore();
const action = import.meta.env.VITE_APP_BASE_API + "/file/upload";
const formRef = ref();
const { proxy } = getCurrentInstance();
const { form } = useFormData({
@@ -237,7 +227,6 @@
   salesContractNoId: undefined, // 开票日期
   enterDate: dayjs().format("YYYY-MM-DD"),
   productData: [], // 表格
   tempFileIds: [], // 文件
});
const selectedContracts = ref([]); // 存储选中的合同数据
@@ -398,10 +387,11 @@
               result.data.productData.forEach(item => {
                  allProductData.push({
                     ...item,
                     id: contractId, // 明确设置合同ID
                     purchaseLedgerId: contractId, // 添加合同ID用于筛选
                     purchaseLedgerNo: contract.purchaseContractNumber, // 添加采购合同号
                     supplierName: contract.supplierName, // 添加供应商名称
                     projectName: contract.projectName // 添加项目名称
                     // 保留产品本身的id,不覆盖
                  });
               });
            }
@@ -421,40 +411,86 @@
         
         // 设置产品数据,并初始化开票数量和金额
         allProductData.forEach(item => {
            // 本次开票数默认为总数量
            item.ticketsNum = Number(item.quantity || 0);
            // 本次开票金额默认为含税总价
            item.ticketsAmount = Number(item.taxInclusiveTotalPrice || 0);
            // 保存原始未来票数和金额(用于计算)
            item.tempFutureTickets = Number(item.quantity || 0);
            item.tempFutureTicketsAmount = Number(item.taxInclusiveTotalPrice || 0);
            // 未来票数和金额初始为0(因为全部开票)
            item.futureTickets = 0;
            item.futureTicketsAmount = 0;
            // 保存"原始未来票数/金额"(用于校验与计算)
            // 优先使用后端返回的 futureTickets/futureTicketsAmount;没有则回退到 quantity/taxInclusiveTotalPrice
            item.tempFutureTickets = Number(
               item.futureTickets !== undefined ? item.futureTickets : (item.quantity || 0)
            );
            item.tempFutureTicketsAmount = Number(
               item.futureTicketsAmount !== undefined ? item.futureTicketsAmount : (item.taxInclusiveTotalPrice || 0)
            );
            // 如果未来票金额为0,则本次开票数和金额都设置为0
            if (item.tempFutureTicketsAmount <= 0) {
               item.ticketsNum = 0;
               item.ticketsAmount = 0;
               item.futureTickets = Number(item.tempFutureTickets || 0);
               item.futureTicketsAmount = 0;
            } else {
               // 新增时:本次开票数默认 = 未来票数(且不能大于未来票数)
               item.ticketsNum = Number(item.tempFutureTickets || 0);
               // 联动计算本次开票金额、未来票数、未来票金额
               const unitPrice = Number(item.taxInclusiveUnitPrice || 0);
               item.ticketsAmount = Number((item.ticketsNum * unitPrice).toFixed(2));
               item.futureTickets = Number((item.tempFutureTickets - item.ticketsNum).toFixed(2));
               item.futureTicketsAmount = Number(
                  (item.tempFutureTicketsAmount - item.ticketsAmount).toFixed(2)
               );
            }
         });
         
         form.productData = allProductData;
         
         // 计算发票金额:所有产品的含税总价之和
         // 计算发票金额:所有产品的本次开票金额之和(新增默认 0)
         const totalAmount = allProductData.reduce((sum, item) => {
            return sum + (Number(item.taxInclusiveTotalPrice) || 0);
            return sum + (Number(item.ticketsAmount) || 0);
         }, 0);
         form.invoiceAmount = totalAmount.toFixed(2);
         form.invoiceAmount = Number(totalAmount.toFixed(2));
         
         // 存储选中的合同数据
         selectedContracts.value = selectedRows;
      });
   } else if (type == "edit") {
      const id = Array.isArray(selectedRows) ? selectedRows[0].id : selectedRows;
      const data = await getPurchaseById({ id, type: 2 });
      form.purchaseLedgerNo = data.purchaseContractNumber;
      const response = await getPurchaseById({ id, type: 2 });
      // 兼容不同的返回格式:可能是 { code, data } 或直接返回数据
      const data = response.data || response;
      // 兼容不同的字段名:purchaseContractNumber 或 purchaseLedgerNo
      form.purchaseLedgerNo = data.purchaseContractNumber || data.purchaseLedgerNo || "";
      form.invoiceAmount = data.invoiceAmount;
      form.invoiceNumber = data.invoiceNumber;
      form.salesContractNo = data.salesContractNo;
      form.projectName = data.projectName;
      form.supplierName = data.supplierName;
      form.entryDate = data.entryDate;
      form.productData = data.productData;
      form.enterDate = data.enterDate || dayjs().format("YYYY-MM-DD");
      // 编辑时也需要初始化产品数据的 tempFutureTickets 和 tempFutureTicketsAmount
      // 同时为每个产品添加合同号等信息
      const contractNumber = data.purchaseContractNumber || data.purchaseLedgerNo || "";
      if (data.productData && Array.isArray(data.productData)) {
         data.productData.forEach(item => {
            // 保存"原始未来票数/金额"(用于校验与计算)
            // 优先使用后端返回的 futureTickets/futureTicketsAmount;没有则回退到 quantity/taxInclusiveTotalPrice
            item.tempFutureTickets = Number(
               item.futureTickets !== undefined ? item.futureTickets : (item.quantity || 0)
            );
            item.tempFutureTicketsAmount = Number(
               item.futureTicketsAmount !== undefined ? item.futureTicketsAmount : (item.taxInclusiveTotalPrice || 0)
            );
            // 确保每个产品都有合同号,用于显示在"所属合同"列
            if (!item.purchaseLedgerNo) {
               item.purchaseLedgerNo = contractNumber;
            }
         });
      }
      form.productData = data.productData || [];
      // 编辑模式下,根据产品数据中的本次开票金额自动计算发票金额
      calculateinvoiceAmount();
   }
};
// 子表合计方法
@@ -476,16 +512,15 @@
      row.ticketsNum = 0;
   }
   if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) {
      proxy.$modal.msgWarning("本次开票数不得大于未开票数");
      row.ticketsNum = 0;
      return;
      proxy.$modal.msgWarning("本次开票数不能大于未来票数");
      row.ticketsNum = Number(row.tempFutureTickets || 0);
   }
   // 计算本次来票金额
   row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2)
   row.ticketsAmount = Number((Number(row.ticketsNum) * Number(row.taxInclusiveUnitPrice || 0)).toFixed(2));
   // 计算未来票数
   row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
   row.futureTickets = Number((Number(row.tempFutureTickets || 0) - Number(row.ticketsNum || 0)).toFixed(2));
   // 计算未来票金额
   row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
   row.futureTicketsAmount = Number((Number(row.tempFutureTicketsAmount || 0) - Number(row.ticketsAmount || 0)).toFixed(2));
   calculateinvoiceAmount();
};
@@ -497,16 +532,23 @@
   // 计算是否超过来票总金额
   if (row.ticketsAmount > row.tempFutureTicketsAmount) {
      proxy.$modal.msgWarning("本次来票金额不得大于未来票金额");
      row.ticketsAmount = 0;
      row.ticketsAmount = Number(row.tempFutureTicketsAmount || 0);
   }
   // 计算本次来票数
   row.ticketsNum = Number(
      (row.ticketsAmount / row.taxInclusiveUnitPrice).toFixed(2)
   );
   // 检查本次开票数是否大于未来票数
   if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) {
      proxy.$modal.msgWarning("本次开票数不能大于未来票数");
      row.ticketsNum = Number(row.tempFutureTickets || 0);
      // 重新计算本次来票金额
      row.ticketsAmount = Number((Number(row.ticketsNum) * Number(row.taxInclusiveUnitPrice || 0)).toFixed(2));
   }
   // 计算未来票数
   row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
   row.futureTickets = Number((Number(row.tempFutureTickets || 0) - Number(row.ticketsNum || 0)).toFixed(2));
   // 计算未来票金额
   row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
   row.futureTicketsAmount = Number((Number(row.tempFutureTicketsAmount || 0) - Number(row.ticketsAmount || 0)).toFixed(2));
   calculateinvoiceAmount();
};
@@ -517,18 +559,47 @@
         invoiceAmountTotal += Number(item.ticketsAmount);
      }
   });
   form.invoiceAmount = invoiceAmountTotal.toFixed(2);
   form.invoiceAmount = Number(invoiceAmountTotal.toFixed(2));
};
// 判断产品是否可以继续来票操作:如果未来票数和未来票金额都为0或小于等于0,则禁用
const isProductDisabled = (row) => {
   // 优先使用 tempFutureTickets(原始未来票数),如果没有则使用 futureTickets
   const futureTickets = Number(row.tempFutureTickets !== undefined
      ? row.tempFutureTickets
      : (row.futureTickets !== undefined ? row.futureTickets : 0));
   // 优先使用 tempFutureTicketsAmount(原始未来票金额),如果没有则使用 futureTicketsAmount
   const futureAmount = Number(row.tempFutureTicketsAmount !== undefined
      ? row.tempFutureTicketsAmount
      : (row.futureTicketsAmount !== undefined ? row.futureTicketsAmount : 0));
   // 只有当未来票数和未来票金额都为0或小于等于0时,才禁用
   return futureTickets <= 0 && futureAmount <= 0;
};
const open = async (type, selectedRows) => {
   visible.value = true;
   // 如果是批量操作,设置标题
   if (Array.isArray(selectedRows) && selectedRows.length > 1) {
      modalOptions.title = `批量新增 (${selectedRows.length}条)`;
   } else {
      modalOptions.title = type === "add" ? "新增" : "编辑";
   // 确保 modalOptions.value 是对象
   if (!modalOptions.value || typeof modalOptions.value !== 'object') {
      modalOptions.value = {};
   }
   // 根据操作类型和选中数据设置标题
   if (Array.isArray(selectedRows) && selectedRows.length > 1) {
      // 批量操作
      modalOptions.value.title = type === "add" ? `批量新增 (${selectedRows.length}条)` : `批量编辑 (${selectedRows.length}条)`;
   } else {
      // 单个操作 - 明确判断 type 的值
      if (type === "add" || type === "新增") {
         modalOptions.value.title = "新增";
      } else if (type === "edit" || type === "编辑") {
         modalOptions.value.title = "编辑";
      } else {
         modalOptions.value.title = "来票登记"; // 默认标题
      }
   }
   visible.value = true;
   
   // 如果是单个操作,获取id
   if (!Array.isArray(selectedRows) || selectedRows.length === 1) {
@@ -539,15 +610,6 @@
   await getTableData(type, selectedRows);
};
const uploadSuccess = (response) => {
   form.tempFileIds.push(response.data.tempId);
   console.log(form);
};
const removeFile = (file) => {
   const { tempId } = file.response.data;
   form.tempFileIds = form.tempFileIds.filter((item) => item !== tempId);
};
const closeAndRefresh = () => {
   closeModal();
@@ -563,22 +625,21 @@
            const batchData = selectedContracts.value.map(contract => {
               // 筛选出属于当前合同的产品数据
               const contractProductData = form.productData.filter(item =>
                  item.id === contract.id
                  item.purchaseLedgerId === contract.id
               );
               
               // 为每个采购合同创建独立的对象
               return {
                  // 基础表单数据
                  invoiceNumber: form.invoiceNumber,
                  invoiceAmount: form.invoiceAmount,
                  entryDate: form.entryDate,
                  enterDate: form.enterDate,
                  issUerId: form.issUerId, // 录入人id
                  issUer: form.issUer, // 录入人
                  tempFileIds: form.tempFileIds,
                  // 合同实际信息
                  purchaseLedgerId: contract.id, // 使用id作为字段名,值为purchaseLedgerId
               // 基础表单数据
               invoiceNumber: form.invoiceNumber,
               invoiceAmount: form.invoiceAmount,
               entryDate: form.entryDate,
               enterDate: form.enterDate,
               issUerId: form.issUerId, // 录入人id
               issUer: form.issUer, // 录入人
               // 合同实际信息
               purchaseLedgerId: contract.id, // 使用id作为字段名,值为purchaseLedgerId
                  purchaseContractNumber: contract.purchaseContractNumber, // 使用实际的采购合同号
                  salesContractNo: contract.salesContractNo, // 使用实际的销售合同号
                  supplierName: contract.supplierName, // 使用实际的供应商名称
@@ -609,17 +670,16 @@
               // 单个合同提交逻辑 - 以数组格式传递
               const singleContract = selectedContracts.value[0];
               const singleFormArray = [{
                  // 基础表单数据
                  invoiceNumber: form.invoiceNumber,
                  invoiceAmount: form.invoiceAmount,
                  entryDate: form.entryDate,
                  enterDate: form.enterDate,
                  issUerId: form.issUerId, // 录入人id
                  issUer: form.issUer, // 录入人
                  tempFileIds: form.tempFileIds,
                  // 合同实际信息
                  purchaseLedgerId: singleContract.id, // 使用id作为字段名,值为purchaseLedgerId
               // 基础表单数据
               invoiceNumber: form.invoiceNumber,
               invoiceAmount: form.invoiceAmount,
               entryDate: form.entryDate,
               enterDate: form.enterDate,
               issUerId: form.issUerId, // 录入人id
               issUer: form.issUer, // 录入人
               // 合同实际信息
               purchaseLedgerId: singleContract.id, // 使用id作为字段名,值为purchaseLedgerId
                  purchaseContractNumber: singleContract.purchaseContractNumber, // 使用实际的采购合同号
                  salesContractNo: singleContract.salesContractNo, // 使用实际的销售合同号
                  supplierName: singleContract.supplierName, // 使用实际的供应商名称