huminmin
2026-06-12 a32e60bf3366bf3b2d8d0d53b8257290acfb3d4e
src/views/procurementManagement/procurementLedger/index.vue
@@ -66,6 +66,9 @@
    </div>
    <div class="table_list">
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="success"
                   plain
                   @click="handleBatchGenerate">批量生成数据</el-button>
        <el-button type="primary"
                   @click="openForm('add')">新增台账</el-button>
        <el-button type="primary"
@@ -106,7 +109,7 @@
                               prop="specificationModel" />
              <el-table-column label="单位"
                               prop="unit" />
                               <el-table-column label="入库审核状态"
              <el-table-column label="入库审核状态"
                               prop="stockInApprovalStatus"
                               width="120">
                <template #default="scope">
@@ -122,6 +125,12 @@
                               prop="availableQuality" />
              <el-table-column label="退货数量"
                               prop="returnQuality" />
              <el-table-column label="运费单价(元)"
                               prop="freightUnitPrice"
                               :formatter="formattedNumber" />
              <el-table-column label="总运费(元)"
                               prop="totalFreight"
                               :formatter="formattedNumber" />
              <el-table-column label="税率(%)"
                               prop="taxRate" />
              <el-table-column label="含税单价(元)"
@@ -186,6 +195,14 @@
                         width="100"
                         prop="paymentMethod"
                         show-overflow-tooltip />
        <el-table-column label="车牌号"
                         prop="carPlateNumber"
                         width="140"
                         show-overflow-tooltip />
        <el-table-column label="运输单位/个人"
                         prop="transportUnitOrPerson"
                         width="180"
                         show-overflow-tooltip />
        <el-table-column label="合同金额(元)"
                         prop="contractAmount"
                         width="200"
@@ -212,6 +229,11 @@
                       type="primary"
                       @click="openForm('edit', scope.row)"
                       :disabled="scope.row.stockInStatus === '完全入库'">编辑
            </el-button>
            <el-button link
                       type="primary"
                       :disabled="scope.row.approvalStatus !== 3 || scope.row.stockInStatus === '完全入库'"
                       @click="handleStockIn(scope.row)">入库
            </el-button>
            <el-button link
                       type="primary"
@@ -306,6 +328,24 @@
                              type="date"
                              placeholder="请选择"
                              clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="车牌号:"
                          prop="carPlateNumber">
              <el-input v-model="form.carPlateNumber"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="运输单位/个人:"
                          prop="transportUnitOrPerson">
              <el-input v-model="form.transportUnitOrPerson"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
@@ -410,6 +450,14 @@
          <el-table-column label="数量"
                           prop="quantity"
                           width="70" />
          <el-table-column label="运费单价(元)"
                           prop="freightUnitPrice"
                           :formatter="formattedNumber"
                           width="150" />
          <el-table-column label="总运费(元)"
                           prop="totalFreight"
                           :formatter="formattedNumber"
                           width="150" />
          <el-table-column label="库存预警数量"
                           prop="warnNum"
                           width="120"
@@ -608,7 +656,32 @@
                               style="width: 100%"
                               v-model="productForm.quantity"
                               placeholder="请输入"
                               @change="mathNum" />
                               @change="handleQuantityChange" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="运费单价(元):"
                          prop="freightUnitPrice">
              <el-input-number v-model="productForm.freightUnitPrice"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%"
                               @change="handleFreightUnitPriceChange" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="总运费(元):"
                          prop="totalFreight">
              <el-input-number v-model="productForm.totalFreight"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               clearable
                               style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -679,6 +752,86 @@
        </el-row>
      </el-form>
    </FormDialog>
    <!-- 入库弹窗 -->
    <FormDialog v-model="stockInDialogVisible"
                title="入库确认"
                :width="'90%'"
                @close="stockInDialogVisible = false"
                @confirm="submitStockIn"
                @cancel="stockInDialogVisible = false">
      <el-form :model="stockInForm"
               label-width="120px"
               label-position="top">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="采购合同号">
              <el-input v-model="stockInForm.purchaseContractNumber"
                        disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-table :data="stockInForm.details"
                  border>
          <el-table-column align="center"
                           label="序号"
                           type="index"
                           width="60" />
          <el-table-column label="产品大类"
                           prop="productCategory"
                           show-overflow-tooltip />
          <el-table-column label="规格型号"
                           prop="specificationModel"
                           show-overflow-tooltip />
          <el-table-column label="单位"
                           prop="unit"
                           width="70" />
          <el-table-column label="待入库数量"
                           prop="availableQuality"
                           width="100" />
          <el-table-column label="本次入库数量"
                           width="130">
            <template #default="scope">
              <el-input-number v-model="scope.row.inboundQuantity"
                               :step="0.01"
                               :min="0"
                               :max="scope.row.availableQuality"
                               @change="handleInboundChange(scope.row)"
                               controls-position="right"
                               style="width: 100%" />
            </template>
          </el-table-column>
          <el-table-column label="是否含水"
                           width="100"
                           align="center">
            <template #default="scope">
              <el-switch v-model="scope.row.isContainsWater"
                         @change="handleInboundChange(scope.row)" />
            </template>
          </el-table-column>
          <el-table-column label="含水量(%)"
                           width="130">
            <template #default="scope">
              <el-input-number v-if="scope.row.isContainsWater"
                               v-model="scope.row.waterContent"
                               :precision="2"
                               :step="0.1"
                               :min="0"
                               :max="100"
                               @change="handleInboundChange(scope.row)"
                               controls-position="right"
                               style="width: 100%" />
              <span v-else>--</span>
            </template>
          </el-table-column>
          <el-table-column label="实际入库数量"
                           width="130">
            <template #default="scope">
              <span style="font-weight: bold; color: #409EFF;">{{ scope.row.actualInboundQuantity }}</span>
            </template>
          </el-table-column>
        </el-table>
      </el-form>
    </FormDialog>
    <FileList v-if="fileListDialogVisible"
              v-model:visible="fileListDialogVisible"
              record-type="purchase_ledger"
@@ -721,6 +874,8 @@
    getOptions,
    getPurchaseTemplateList,
    delPurchaseTemplate,
    batchGeneratePurchaseInboundSteps,
    manualStockIn,
  } from "@/api/procurementManagement/procurementLedger.js";
  import useFormData from "@/hooks/useFormData.js";
  const FileList = defineAsyncComponent(() =>
@@ -741,18 +896,23 @@
  const tableLoading = ref(false);
  const recordId = ref();
  const fileListDialogVisible = ref(false);
  // 入库弹窗相关
  const stockInDialogVisible = ref(false);
  const stockInLoading = ref(false);
  const stockInForm = reactive({
    purchaseLedgerId: null,
    purchaseContractNumber: "",
    details: [],
  });
  const page = reactive({
    current: 1,
    size: 100,
  });
  const total = ref(0);
  const fileList = ref([]);
  import useUserStore from "@/store/modules/user";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import dayjs from "dayjs";
  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
  const userStore = useUserStore();
  // 订单审批状态显示文本
  const approvalStatusText = {
@@ -776,9 +936,9 @@
  // 获取入库状态标签类型
  const getStockInStatusType = status => {
    const typeMap = {
      "待入库": "info", // 待入库 - 灰色
      "入库中": "warning", // 入库中 - 橙色
      "完全入库": "success", // 完全入库 - 绿色
      待入库: "info", // 待入库 - 灰色
      入库中: "warning", // 入库中 - 橙色
      完全入库: "success", // 完全入库 - 绿色
    };
    return typeMap[status] || "";
  };
@@ -786,9 +946,9 @@
  // 获取入库审核状态标签类型
  const getStockInApprovalStatusType = status => {
    const typeMap = {
      "待入库": "info", // 待入库 - 灰色
      "入库中": "warning", // 入库中 - 橙色
      "完全入库": "success", // 完全入库 - 绿色
      待入库: "info", // 待入库 - 灰色
      入库中: "warning", // 入库中 - 橙色
      完全入库: "success", // 完全入库 - 绿色
    };
    return typeMap[status] || "";
  };
@@ -918,12 +1078,14 @@
      supplierName: "",
      supplierId: "",
      paymentMethod: "",
      carPlateNumber: "",
      transportUnitOrPerson: "",
      executionDate: "",
      isChecked: false,
    },
    rules: {
      purchaseContractNumber: [
        { required: true, message: "请输入", trigger: "blur" },
        { required: false, message: "请输入", trigger: "blur" },
      ],
      projectName: [
        { required: true, message: "请输入项目名称", trigger: "blur" },
@@ -958,6 +1120,8 @@
      specificationModel: "",
      unit: "",
      quantity: "",
      freightUnitPrice: "",
      totalFreight: "",
      taxInclusiveUnitPrice: "",
      taxRate: "",
      taxInclusiveTotalPrice: "",
@@ -1075,7 +1239,12 @@
  };
  const formattedNumber = (row, column, cellValue) => {
    return parseFloat(cellValue).toFixed(2);
    if (cellValue === undefined || cellValue === null || cellValue === "") {
      return "0.00";
    }
    const num = Number(cellValue);
    if (Number.isNaN(num)) return "0.00";
    return num.toFixed(2);
  };
  // 查询列表
  /** 搜索按钮操作 */
@@ -1186,6 +1355,8 @@
    return proxy.summarizeTable(
      param,
      [
        "freightUnitPrice",
        "totalFreight",
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
@@ -1234,6 +1405,106 @@
    productSelectedRows.value = selectedRows;
  };
  const expandedRowKeys = ref([]);
  // 入库处理
  const handleStockIn = async row => {
    stockInForm.purchaseLedgerId = row.id;
    stockInForm.purchaseContractNumber = row.purchaseContractNumber;
    stockInForm.details = [];
    try {
      proxy.$modal.loading("正在加载产品信息...");
      const res = await productList({ salesLedgerId: row.id, type: 2 });
      if (res.code === 200) {
        // 过滤掉已经完全入库的产品(如果有这个状态的话,或者直接显示所有可用数量大于0的产品)
        stockInForm.details = (res.data || [])
          .filter(item => (item.availableQuality || 0) > 0)
          .map(item => ({
            ...item,
            inboundQuantity: item.availableQuality || 0, // 默认入库全部可用数量
            isContainsWater: false,
            waterContent: 0,
            actualInboundQuantity: item.availableQuality || 0,
          }));
        if (stockInForm.details.length === 0) {
          proxy.$modal.msgWarning("该合同下没有可入库的产品记录");
          return;
        }
        stockInDialogVisible.value = true;
      }
    } catch (error) {
      console.error("加载产品列表失败:", error);
      proxy.$modal.msgError("加载产品列表失败");
    } finally {
      proxy.$modal.closeLoading();
    }
  };
  // 计算实际入库数量
  const handleInboundChange = row => {
    if (row.isContainsWater) {
      // 实际入库 = 本次入库数量 * (1 - 含水量/100)
      row.actualInboundQuantity = Number(
        (row.inboundQuantity * (1 - (row.waterContent || 0) / 100)).toFixed(2)
      );
    } else {
      row.actualInboundQuantity = row.inboundQuantity;
    }
  };
  const submitStockIn = async () => {
    if (stockInForm.details.length === 0) {
      proxy.$modal.msgWarning("请选择入库产品");
      return;
    }
    // 验证入库数量
    const invalid = stockInForm.details.some(
      item => !item.inboundQuantity || item.inboundQuantity <= 0
    );
    if (invalid) {
      proxy.$modal.msgWarning("请输入有效的入库数量");
      return;
    }
    const overLimit = stockInForm.details.some(
      item => item.inboundQuantity > item.availableQuality
    );
    if (overLimit) {
      proxy.$modal.msgWarning("入库数量不能超过可用数量");
      return;
    }
    try {
      stockInLoading.value = true;
      const params = {
        purchaseLedgerId: stockInForm.purchaseLedgerId,
        details: stockInForm.details.map(item => ({
          id: item.id,
          inboundQuantity: Number(item.inboundQuantity),
          isContainsWater: !!item.isContainsWater,
          waterContent: item.isContainsWater ? Number(item.waterContent || 0) : 0,
          actualInboundQuantity: Number(item.actualInboundQuantity),
        })),
      };
      const res = await manualStockIn(params);
      if (res.code === 200) {
        proxy.$modal.msgSuccess("入库成功");
        stockInDialogVisible.value = false;
        getList(); // 刷新列表
      } else {
        proxy.$modal.msgError(res.msg || "入库失败");
      }
    } catch (error) {
      console.error("入库提交失败:", error);
      proxy.$modal.msgError("入库提交失败");
    } finally {
      stockInLoading.value = false;
    }
  };
  // 展开行
  const expandChange = async (row, expandedRows) => {
    if (expandedRows.length > 0) {
@@ -1268,13 +1539,14 @@
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
      "totalFreight",
    ]);
  };
  // 打开弹框
  const openForm = async (type, row) => {
    // 编辑时检查入库状态,完全入库时不能编辑
    if (type === "edit" && row) {
      if (row.stockInStatus === '完全入库') {
      if (row.stockInStatus === "完全入库") {
        proxy.$modal.msgWarning("完全入库状态的记录不能编辑");
        return;
      }
@@ -1306,18 +1578,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 {
@@ -1325,7 +1586,10 @@
          form.value = { ...purchaseRes, stockInStatus: row.stockInStatus };
          fileList.value = purchaseRes.storageBlobVOS || [];
          // 使用 productList 接口获取产品列表,以获取入库审核状态
          const productRes = await productList({ salesLedgerId: row.id, type: 2 });
          const productRes = await productList({
            salesLedgerId: row.id,
            type: 2,
          });
          productData.value = productRes.data || [];
        } catch (error) {
          console.error("加载采购台账数据失败:", error);
@@ -1398,7 +1662,7 @@
  // 提交表单
  const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
    proxy.$refs["formRef"].validate(async valid => {
      if (valid) {
        if (productData.value.length > 0) {
          // 新增时,需要从每个产品对象中删除 id 字段
@@ -1428,6 +1692,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();
@@ -1444,7 +1722,7 @@
  // 打开产品弹框
  const openProductForm = async (type, row, index) => {
    // 编辑时检查产品入库审核状态,完全入库时不能编辑
    if (type === "edit" && row && row.stockInApprovalStatus === '完全入库') {
    if (type === "edit" && row && row.stockInApprovalStatus === "完全入库") {
      proxy.$modal.msgWarning("完全入库状态的产品不能编辑");
      return;
    }
@@ -1468,6 +1746,12 @@
    if (type === "edit") {
      // 复制行数据
      productForm.value = { ...row };
      if (productForm.value.freightUnitPrice === undefined) {
        productForm.value.freightUnitPrice = "";
      }
      if (productForm.value.totalFreight === undefined) {
        productForm.value.totalFreight = "";
      }
      // 如果是从模板加载的数据,可能没有 productId 和 productModelId
      // 需要根据 productCategory 和 specificationModel 来查找对应的 ID
@@ -1532,6 +1816,34 @@
      // 最后再等待一次 DOM 更新,确保所有数据都已设置
      await nextTick();
    }
    computeTotalFreight();
  };
  const computeTotalFreight = () => {
    const hasQuantity =
      productForm.value.quantity !== "" &&
      productForm.value.quantity !== null &&
      productForm.value.quantity !== undefined;
    const hasFreightUnitPrice =
      productForm.value.freightUnitPrice !== "" &&
      productForm.value.freightUnitPrice !== null &&
      productForm.value.freightUnitPrice !== undefined;
    if (!hasQuantity || !hasFreightUnitPrice) return;
    const quantity = Number(productForm.value.quantity);
    const freightUnitPrice = Number(productForm.value.freightUnitPrice);
    if (!Number.isFinite(quantity) || !Number.isFinite(freightUnitPrice)) return;
    productForm.value.totalFreight = (quantity * freightUnitPrice).toFixed(2);
  };
  const handleQuantityChange = () => {
    mathNum();
    computeTotalFreight();
  };
  const handleFreightUnitPriceChange = () => {
    computeTotalFreight();
  };
  const getProductOptions = () => {
    return productTreeList().then(res => {
@@ -1601,8 +1913,9 @@
          submitProductEdit();
        } else {
          if (productOperationType.value === "add") {
            productData.value.push({ ...productForm.value });
            console.log("productForm.value---", productForm.value);
            console.log("productData.value---", productData.value);
            productData.value.push({ ...productForm.value });
          } else {
            productData.value[productOperationIndex.value] = {
              ...productForm.value,
@@ -1633,7 +1946,7 @@
    }
    // 检查选中的产品中是否有完全入库的
    const hasFullyStocked = productSelectedRows.value.some(
      row => row.stockInApprovalStatus === '完全入库'
      row => row.stockInApprovalStatus === "完全入库"
    );
    if (hasFullyStocked) {
      proxy.$modal.msgWarning("选中的产品中包含完全入库的产品,无法删除");
@@ -1709,6 +2022,37 @@
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  const handleBatchGenerate = async () => {
    if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    const ids = selectedRows.value.map(item => item.id);
    ElMessageBox.confirm("确认批量生成数据?", "批量生成", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "info",
    })
      .then(() => {
        proxy.$modal.loading("正在批量生成数据,请稍候...");
        batchGeneratePurchaseInboundSteps({ ids })
          .then(res => {
            proxy.$modal.msgSuccess("批量生成成功");
            getList();
          })
          .catch(() => {
            proxy.$modal.msgError("批量生成失败");
          })
          .finally(() => {
            proxy.$modal.closeLoading();
          });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
@@ -1852,7 +2196,7 @@
      contractNo: form.value.salesLedgerId,
    });
    if (code == 200) {
      productData.value = data;
      productData.value = data || [];
    }
  };