ZN
4 天以前 4486457e02c39a14d20ea791403f28ec0d5e8103
src/views/procurementManagement/procurementLedger/index.vue
@@ -118,6 +118,31 @@
          width="420"
          show-overflow-tooltip
        />
        <el-table-column label="审批状态" width="140">
          <template #default="scope">
            <el-tag
              v-if="(scope.row.approveStatus ?? scope.row.approvalStatus ?? scope.row.auditStatus) == 0"
              type="warning"
            >待审核</el-tag>
            <el-tag
              v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus ?? scope.row.auditStatus) == 1"
              type="primary"
            >审核中</el-tag>
            <el-tag
              v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus ?? scope.row.auditStatus) == 2"
              type="success"
            >审核完成</el-tag>
            <el-tag
              v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus ?? scope.row.auditStatus) == 3"
              type="danger"
            >审核未通过</el-tag>
            <el-tag
              v-else-if="(scope.row.approveStatus ?? scope.row.approvalStatus ?? scope.row.auditStatus) == 4"
              type="info"
            >已重新提交</el-tag>
            <el-tag v-else type="info">-</el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="付款方式"
          width="100"
@@ -155,7 +180,6 @@
              type="primary"
              size="small"
              @click="openForm('edit', scope.row)"
                     :disabled="scope.row.receiptPaymentAmount>0 || scope.row.recorderName !== userStore.nickName"
              >编辑</el-button
            >
            <el-button
@@ -246,6 +270,30 @@
            </el-form-item>
          </el-col>
        </el-row>
            <el-row :gutter="30">
               <el-col :span="12">
                  <el-form-item label="付款方式">
                     <el-input
                        v-model="form.paymentMethod"
                        placeholder="请输入"
                        clearable
                     />
                  </el-form-item>
               </el-col>
               <el-col :span="12">
                  <el-form-item label="签订日期:" prop="executionDate">
                     <el-date-picker
                        style="width: 100%"
                        v-model="form.executionDate"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        type="date"
                        placeholder="请选择"
                        clearable
                     />
                  </el-form-item>
               </el-col>
            </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入人:" prop="recorderId">
@@ -267,7 +315,6 @@
          <el-col :span="12">
            <el-form-item label="录入日期:" prop="entryDate">
              <el-date-picker
                disabled
                style="width: 100%"
                v-model="form.entryDate"
                value-format="YYYY-MM-DD"
@@ -279,14 +326,45 @@
            </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-row>
          <el-col :span="24">
            <el-form-item>
              <template #label>
                <span>审批人选择:</span>
                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">新增节点</el-button>
              </template>
              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
                <div
                  v-for="(node, index) in approverNodes"
                  :key="node.id"
                  style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
                >
                  <div>
                    <span>审批人</span>
                    →
                  </div>
                  <el-select
                    v-model="node.userId"
                    placeholder="选择人员"
                    style="width: 140px; margin-bottom: 8px;"
                  >
                    <el-option
                      v-for="user in userList"
                      :key="user.userId"
                      :label="user.nickName"
                      :value="user.userId"
                    />
                  </el-select>
                  <div>
                    <el-button
                      type="danger"
                      size="small"
                      @click="removeApproverNode(index)"
                      v-if="approverNodes.length > 1"
                    >删除</el-button>
                  </div>
                </div>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
@@ -318,6 +396,7 @@
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" width="70" />
          <el-table-column label="数量" prop="quantity" width="70" />
               <el-table-column label="库存预警数量" prop="warnNum" width="120" show-overflow-tooltip />
          <el-table-column label="税率(%)" prop="taxRate" width="80" />
          <el-table-column
            label="含税单价(元)"
@@ -473,6 +552,11 @@
                     </el-select>
                  </el-form-item>
               </el-col>
          <el-col :span="24">
            <el-form-item label=" ">
              <el-button type="warning" :disabled="!(productForm.productId && productForm.productModelId && productForm.taxRate)" @click="showPriceReference" icon="Search">查看历史采购价格参考</el-button>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
@@ -519,7 +603,7 @@
              label="不含税总价(元):"
              prop="taxExclusiveTotalPrice"
            >
              <el-input
              <el-input
                v-model="productForm.taxExclusiveTotalPrice"
                @change="reverseMathNum('taxExclusiveTotalPrice')"
              />
@@ -539,6 +623,17 @@
              </el-select>
            </el-form-item>
          </el-col>
               <el-col :span="12">
                  <el-form-item label="库存预警数量:" prop="warnNum">
                     <el-input-number
                        v-model="productForm.warnNum"
                        :precision="2"
                        :step="0.1"
                        clearable
                        style="width: 100%"
                     />
                  </el-form-item>
               </el-col>
        </el-row>
      </el-form>
      <template #footer>
@@ -548,7 +643,63 @@
        </div>
      </template>
    </el-dialog>
    <!-- 历史采购价格参考弹窗 -->
    <el-dialog
      v-model="priceReferenceVisible"
      title="历史采购价格参考"
      width="1000px"
      append-to-body
    >
      <el-table
        v-loading="priceReferenceLoading"
        :data="priceReferenceData"
        border
        style="width: 100%"
      >
        <el-table-column label="商品名称" prop="productName" min-width="150" show-overflow-tooltip />
        <el-table-column label="规格型号" prop="specification" width="150" show-overflow-tooltip />
        <el-table-column label="供应商" prop="supplierName" width="200" show-overflow-tooltip />
        <el-table-column label="基础价格" width="120" align="right">
          <template #default="{ row }">
            <span style="color: #f56c6c; font-weight: bold;">¥{{ row.basePrice }}</span>
          </template>
        </el-table-column>
        <el-table-column label="折扣信息" width="120" align="center">
          <template #default="{ row }">
            <el-tag v-if="row.discountType === 'percentage'" type="success">
              {{ row.discountValue }}%
            </el-tag>
            <el-tag v-else-if="row.discountType === 'fixed'" type="warning">
              -¥{{ row.discountValue }}
            </el-tag>
            <span v-else>无折扣</span>
          </template>
        </el-table-column>
        <el-table-column label="生效时间" prop="effectiveTime" width="180" align="center" />
        <el-table-column label="操作" width="100" align="center" fixed="right">
          <template #default="{ row }">
            <el-button type="primary" link @click="selectPriceReference(row)">选择</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination-container" style="margin-top: 20px; display: flex; justify-content: flex-end;">
        <el-pagination
          v-model:current-page="priceReferencePagination.current"
          v-model:page-size="priceReferencePagination.size"
          :page-sizes="[10, 20, 50]"
          :total="priceReferenceTotal"
          layout="total, sizes, prev, pager, next"
          @size-change="handlePriceReferenceSizeChange"
          @current-change="handlePriceReferenceCurrentChange"
        />
      </div>
      <template #footer>
        <el-button @click="priceReferenceVisible = false">关闭</el-button>
      </template>
    </el-dialog>
    <!-- 二维码显示对话框 -->
    <el-dialog
      v-model="qrCodeDialogVisible"
@@ -809,8 +960,11 @@
});
const total = ref(0);
const fileList = ref([]);
const approverNodes = ref([{ id: 1, userId: null }]);
let nextApproverId = 2;
import useUserStore from "@/store/modules/user";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import { listPage as listAdvancedPrice } from "@/api/procurementManagement/advancedPriceManagement.js";
import dayjs from "dayjs";
const userStore = useUserStore();
@@ -842,6 +996,7 @@
    supplierName: "",
    supplierId: "",
    paymentMethod: "",
      executionDate: "",
  },
  rules: {
    purchaseContractNumber: [
@@ -849,10 +1004,19 @@
    ],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const {  form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
const addApproverNode = () => {
  approverNodes.value.push({ id: nextApproverId++, userId: null });
};
const removeApproverNode = (index) => {
  approverNodes.value.splice(index, 1);
};
// 产品表单弹框数据
const productFormVisible = ref(false);
@@ -872,6 +1036,7 @@
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
      warnNum: "",
  },
  productRules: {
    productId: [{ required: true, message: "请选择", trigger: "change" }],
@@ -882,6 +1047,7 @@
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxRate: [{ required: true, message: "请选择", trigger: "change" }],
      warnNum: [{ required: false, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
@@ -892,6 +1058,72 @@
  },
});
const { productForm, productRules } = toRefs(productFormData);
// 采购价格管理参考弹窗
const priceReferenceVisible = ref(false);
const priceReferenceLoading = ref(false);
const priceReferenceData = ref([]);
const priceReferenceTotal = ref(0);
const priceReferencePagination = reactive({
  current: 1,
  size: 10
});
const showPriceReference = () => {
  priceReferenceVisible.value = true;
  handlePriceReferenceSearch();
};
const handlePriceReferenceSearch = () => {
  priceReferenceLoading.value = true;
  // 模拟搜索参数:productId 映射为 productName 或 ID,这里根据 advancedPriceManagement 的 API 确定参数
  // 假设高级价格管理的 listPage 接收 productId
  const query = {
    productId: productForm.value.productId,
    specificationId: productForm.value.productModelId,
    current: priceReferencePagination.current,
    size: priceReferencePagination.size
  };
  listAdvancedPrice(query).then(res => {
    priceReferenceData.value = res.data.records;
    priceReferenceTotal.value = res.data.total;
    priceReferenceLoading.value = false;
  }).catch(() => {
    priceReferenceLoading.value = false;
  });
};
const handlePriceReferenceSizeChange = (size) => {
  priceReferencePagination.size = size;
  handlePriceReferenceSearch();
};
const handlePriceReferenceCurrentChange = (page) => {
  priceReferencePagination.current = page;
  handlePriceReferenceSearch();
};
const selectPriceReference = (row) => {
   // 计算实际价格:基础价格 - 折扣
   let actualPrice = row.basePrice;
   if (row.discountType === 'percentage') {
     actualPrice = row.basePrice * (1 - row.discountValue / 100);
   } else if (row.discountType === 'fixed') {
     actualPrice = row.basePrice - row.discountValue;
   }
   // 填充含税单价,保留两位小数
   productForm.value.taxInclusiveUnitPrice = Number(Math.max(actualPrice, 0)).toFixed(2);
   // 如果已经输入了数量,则自动计算总价
   if (productForm.value.quantity) {
     mathNum();
   }
   priceReferenceVisible.value = false;
   proxy.$modal.msgSuccess("已引用历史价格");
 };
const upload = reactive({
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
@@ -1005,6 +1237,8 @@
  form.value = {};
  productData.value = [];
  fileList.value = [];
  approverNodes.value = [{ id: 1, userId: null }];
  nextApproverId = 2;
  if (operationType.value == "add") {
    createPurchaseNo().then((res) => {
      form.value.purchaseContractNumber = res.data;
@@ -1026,6 +1260,17 @@
    getPurchaseById({ id: row.id, type: 2 }).then((res) => {
      form.value = { ...res };
      productData.value = form.value.productData;
      const approveUserIds = form.value.approveUserIds || form.value.approverIds;
      if (approveUserIds) {
        const ids = String(approveUserIds)
          .split(",")
          .map((id) => Number(id.trim()))
          .filter((id) => !Number.isNaN(id));
        if (ids.length > 0) {
          approverNodes.value = ids.map((id, idx) => ({ id: idx + 1, userId: id }));
          nextApproverId = ids.length + 1;
        }
      }
      if (form.value.salesLedgerFiles) {
        fileList.value = form.value.salesLedgerFiles;
      } else {
@@ -1064,9 +1309,9 @@
// 移除文件
function handleRemove(file) {
  console.log("handleRemove", file.id);
  if (file.size > 1024 * 1024 * 10) {
  if (file.size > 1024 * 1024 * 10) {
    // 仅前端清理,不调用删除接口和提示
    return;
    return;
  }
  if (operationType.value === "edit") {
    let ids = [];
@@ -1080,6 +1325,12 @@
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      const hasEmptyApprover = approverNodes.value.some((node) => !node.userId);
      if (hasEmptyApprover) {
        proxy.$modal.msgWarning("请为所有审批节点选择审批人");
        return;
      }
      form.value.approveUserIds = approverNodes.value.map((node) => node.userId).join(",");
      if (productData.value.length > 0) {
        form.value.productData = proxy.HaveJson(productData.value);
      } else {
@@ -1107,6 +1358,10 @@
};
// 打开产品弹框
const openProductForm = (type, row, index) => {
  if(!form.value.supplierId){
    proxy.$modal.msgWarning("请选择供应商");
    return;
  }
  productOperationType.value = type;
  productOperationIndex.value = index;
  productForm.value = {};
@@ -1123,10 +1378,15 @@
  });
};
const getModels = (value) => {
  productForm.value.productCategory = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  });
  if (value) {
    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
  } else {
    productForm.value.productCategory = "";
    modelOptions.value = [];
  }
};
const getProductModel = (value) => {
  const index = modelOptions.value.findIndex((item) => item.id === value);
@@ -1141,12 +1401,12 @@
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // 找到节点,返回该节点
      return nodes[i].label; // 找到节点,返回该节点的label
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode.label; // 在子节点中找到,返回该节点
        return foundNode; // 在子节点中找到,直接返回(已经是label字符串)
      }
    }
  }
@@ -1331,29 +1591,29 @@
  if (field === 'taxInclusiveTotalPrice') {
    // 已知含税总价和数量,反算含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
    }
    // 已知含税总价和含税单价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
    }
    // 反算不含税总价
    productForm.value.taxExclusiveTotalPrice =
    productForm.value.taxExclusiveTotalPrice =
      (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
  } else if (field === 'taxExclusiveTotalPrice') {
    // 反算含税总价
    productForm.value.taxInclusiveTotalPrice =
    productForm.value.taxInclusiveTotalPrice =
      (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
    // 已知数量,反算含税单价
    if (productForm.value.quantity) {
      productForm.value.taxInclusiveUnitPrice =
      productForm.value.taxInclusiveUnitPrice =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
    }
    // 已知含税单价,反算数量
    else if (productForm.value.taxInclusiveUnitPrice) {
      productForm.value.quantity =
      productForm.value.quantity =
        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
    }
  }
@@ -1409,7 +1669,7 @@
    proxy.$modal.msgWarning("二维码未生成");
    return;
  }
  const a = document.createElement('a');
  a.href = qrCodeUrl.value;
  a.download = `采购合同号二维码_${new Date().getTime()}.png`;
@@ -1469,7 +1729,7 @@
// 解析扫码内容(模拟解析二维码数据)
const parseScanContent = (content) => {
  if (!content) return;
  // 模拟解析二维码内容,这里可以根据实际需求调整解析逻辑
  // 假设扫码内容格式为:合同号|供应商|项目|金额|付款方式
  const parts = content.split('|');
@@ -1504,11 +1764,11 @@
        remark: scanAddForm.scanRemark,
        type: 2
      };
      // 模拟新增成功
      proxy.$modal.msgSuccess("扫码新增成功!");
      closeScanAddDialog();
      // 可以选择是否刷新列表
      // getList();
    }