| | |
| | | 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" |
| | |
| | | type="primary" |
| | | size="small" |
| | | @click="openForm('edit', scope.row)" |
| | | :disabled="scope.row.receiptPaymentAmount>0 || scope.row.recorderName !== userStore.nickName" |
| | | >编辑</el-button |
| | | > |
| | | <el-button |
| | |
| | | </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"> |
| | |
| | | <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" |
| | |
| | | </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> |
| | |
| | | <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="含税单价(元)" |
| | |
| | | </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"> |
| | |
| | | label="不含税总价(元):" |
| | | prop="taxExclusiveTotalPrice" |
| | | > |
| | | <el-input |
| | | <el-input |
| | | v-model="productForm.taxExclusiveTotalPrice" |
| | | @change="reverseMathNum('taxExclusiveTotalPrice')" |
| | | /> |
| | |
| | | </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> |
| | |
| | | </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" |
| | |
| | | }); |
| | | 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(); |
| | |
| | | supplierName: "", |
| | | supplierId: "", |
| | | paymentMethod: "", |
| | | executionDate: "", |
| | | }, |
| | | rules: { |
| | | purchaseContractNumber: [ |
| | |
| | | ], |
| | | 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); |
| | |
| | | taxInclusiveTotalPrice: "", |
| | | taxExclusiveTotalPrice: "", |
| | | invoiceType: "", |
| | | warnNum: "", |
| | | }, |
| | | productRules: { |
| | | productId: [{ required: true, message: "请选择", trigger: "change" }], |
| | |
| | | { required: true, message: "请输入", trigger: "blur" }, |
| | | ], |
| | | taxRate: [{ required: true, message: "请选择", trigger: "change" }], |
| | | warnNum: [{ required: false, message: "请选择", trigger: "change" }], |
| | | taxInclusiveTotalPrice: [ |
| | | { required: true, message: "请输入", trigger: "blur" }, |
| | | ], |
| | |
| | | }, |
| | | }); |
| | | 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", |
| | |
| | | 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; |
| | |
| | | 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 { |
| | |
| | | // 移除文件 |
| | | 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 = []; |
| | |
| | | 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 { |
| | |
| | | }; |
| | | // 打开产品弹框 |
| | | const openProductForm = (type, row, index) => { |
| | | if(!form.value.supplierId){ |
| | | proxy.$modal.msgWarning("请选择供应商"); |
| | | return; |
| | | } |
| | | productOperationType.value = type; |
| | | productOperationIndex.value = index; |
| | | productForm.value = {}; |
| | |
| | | }); |
| | | }; |
| | | 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); |
| | |
| | | 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字符串) |
| | | } |
| | | } |
| | | } |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | proxy.$modal.msgWarning("二维码未生成"); |
| | | return; |
| | | } |
| | | |
| | | |
| | | const a = document.createElement('a'); |
| | | a.href = qrCodeUrl.value; |
| | | a.download = `采购合同号二维码_${new Date().getTime()}.png`; |
| | |
| | | // 解析扫码内容(模拟解析二维码数据) |
| | | const parseScanContent = (content) => { |
| | | if (!content) return; |
| | | |
| | | |
| | | // 模拟解析二维码内容,这里可以根据实际需求调整解析逻辑 |
| | | // 假设扫码内容格式为:合同号|供应商|项目|金额|付款方式 |
| | | const parts = content.split('|'); |
| | |
| | | remark: scanAddForm.scanRemark, |
| | | type: 2 |
| | | }; |
| | | |
| | | |
| | | // 模拟新增成功 |
| | | proxy.$modal.msgSuccess("扫码新增成功!"); |
| | | closeScanAddDialog(); |
| | | |
| | | |
| | | // 可以选择是否刷新列表 |
| | | // getList(); |
| | | } |