| | |
| | | @confirm="submitForm" |
| | | @cancel="closeDia"> |
| | | <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef"> |
| | | <el-row v-if="operationType !== 'view'"> |
| | | <el-col :span="24" style="display:flex; justify-content:flex-end; gap:10px; margin-bottom: 6px;"> |
| | | <el-button type="primary" plain @click="openQuotationDialog">从审批通过的报价单导入</el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="销售合同号:" prop="salesContractNo"> |
| | |
| | | </el-row> |
| | | </el-form> |
| | | </FormDialog> |
| | | |
| | | <!-- 从报价单导入(仅审批通过) --> |
| | | <el-dialog |
| | | v-model="quotationDialogVisible" |
| | | title="选择审批通过的销售报价单" |
| | | width="80%" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;"> |
| | | <el-input |
| | | v-model="quotationSearchForm.quotationNo" |
| | | placeholder="请输入报价单号" |
| | | clearable |
| | | style="max-width: 260px;" |
| | | @change="fetchQuotationList" |
| | | /> |
| | | <el-input |
| | | v-model="quotationSearchForm.customer" |
| | | placeholder="请输入客户名称" |
| | | clearable |
| | | style="max-width: 260px;" |
| | | @change="fetchQuotationList" |
| | | /> |
| | | <el-button type="primary" @click="fetchQuotationList">搜索</el-button> |
| | | <el-button @click="resetQuotationSearch">重置</el-button> |
| | | </div> |
| | | |
| | | <el-table |
| | | :data="quotationList" |
| | | border |
| | | stripe |
| | | v-loading="quotationLoading" |
| | | height="420px" |
| | | > |
| | | <el-table-column align="center" label="序号" type="index" width="60" /> |
| | | <el-table-column prop="quotationNo" label="报价单号" width="180" show-overflow-tooltip /> |
| | | <el-table-column prop="customer" label="客户名称" min-width="220" show-overflow-tooltip /> |
| | | <el-table-column prop="salesperson" label="业务员" width="120" show-overflow-tooltip /> |
| | | <el-table-column prop="quotationDate" label="报价日期" width="140" /> |
| | | <el-table-column prop="status" label="审批状态" width="120" align="center" /> |
| | | <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right"> |
| | | <template #default="scope"> |
| | | {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="操作" width="120" align="center"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" link @click="applyQuotation(scope.row)">选择</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <template #footer> |
| | | <el-button @click="quotationDialogVisible = false">关闭</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | <FormDialog |
| | | v-model="productFormVisible" |
| | | :title="productOperationType === 'add' ? '新增产品' : '编辑产品'" |
| | |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import FileListDialog from '@/components/Dialog/FileListDialog.vue'; |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { |
| | | ledgerListPage, |
| | | productList, |
| | |
| | | const printPreviewVisible = ref(false); |
| | | const printData = ref([]); |
| | | |
| | | // 报价单导入相关 |
| | | const quotationDialogVisible = ref(false); |
| | | const quotationLoading = ref(false); |
| | | const quotationList = ref([]); |
| | | const quotationSearchForm = reactive({ |
| | | quotationNo: "", |
| | | customer: "", |
| | | }); |
| | | const selectedQuotation = ref(null); |
| | | |
| | | // 发货相关 |
| | | const deliveryFormVisible = ref(false); |
| | | const currentDeliveryRow = ref(null); |
| | |
| | | }; |
| | | // 获取产品大类tree数据 |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | // 返回 Promise,便于在编辑产品时等待加载完成 |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return productOptions.value; |
| | | }); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | |
| | | |
| | | return newItem; |
| | | }); |
| | | } |
| | | // 根据名称反查产品大类 id,便于仅存名称时的反显 |
| | | function findNodeIdByLabel(nodes, label) { |
| | | if (!label) return null; |
| | | for (let i = 0; i < nodes.length; i++) { |
| | | const node = nodes[i]; |
| | | if (node.label === label) return node.value; |
| | | if (node.children && node.children.length > 0) { |
| | | const found = findNodeIdByLabel(node.children, label); |
| | | if (found !== null && found !== undefined) return found; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | // 表格选择数据 |
| | | const handleSelectionChange = (selection) => { |
| | |
| | | operationType.value = type; |
| | | form.value = {}; |
| | | productData.value = []; |
| | | selectedQuotation.value = null; |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | customerList().then((res) => { |
| | |
| | | // }); |
| | | form.value.entryDate = getCurrentDate(); // 设置默认录入日期为当前日期 |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | |
| | | // 打开报价单选择弹窗(仅审批通过) |
| | | const openQuotationDialog = async () => { |
| | | if (operationType.value === "view") return; |
| | | quotationDialogVisible.value = true; |
| | | // 先确保客户列表已加载,便于后续回填 customerId |
| | | if (!customerOption.value || customerOption.value.length === 0) { |
| | | try { |
| | | const res = await customerList(); |
| | | customerOption.value = res; |
| | | } catch (e) { |
| | | // ignore,允许用户后续手动选择客户 |
| | | } |
| | | } |
| | | await fetchQuotationList(); |
| | | }; |
| | | |
| | | const fetchQuotationList = async () => { |
| | | quotationLoading.value = true; |
| | | try { |
| | | const params = { |
| | | // 兼容后端分页字段:这里沿用报价页面已有可用的字段命名 |
| | | currentPage: 1, |
| | | pageSize: 100, |
| | | ...quotationSearchForm, |
| | | status: "通过", |
| | | }; |
| | | const res = await getQuotationList(params); |
| | | quotationList.value = res?.data?.records || []; |
| | | } finally { |
| | | quotationLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const resetQuotationSearch = async () => { |
| | | quotationSearchForm.quotationNo = ""; |
| | | quotationSearchForm.customer = ""; |
| | | await fetchQuotationList(); |
| | | }; |
| | | |
| | | // 选中报价单后回填到台账表单 |
| | | const applyQuotation = (row) => { |
| | | if (!row) return; |
| | | selectedQuotation.value = row; |
| | | |
| | | // 业务员 |
| | | form.value.salesman = row.salesperson || ""; |
| | | |
| | | // 客户名称 -> customerId |
| | | const customer = (customerOption.value || []).find((c) => c.customerName === row.customer); |
| | | if (customer?.id) { |
| | | form.value.customerId = customer.id; |
| | | } else { |
| | | // 如果找不到,保留原值(允许用户手动选择/不打断已有输入) |
| | | form.value.customerId = form.value.customerId || ""; |
| | | } |
| | | |
| | | // 产品信息映射:报价 products -> 台账 productData |
| | | const products = Array.isArray(row.products) ? row.products : []; |
| | | productData.value = products.map((p) => { |
| | | const quantity = Number(p.quantity ?? 0) || 0; |
| | | const unitPrice = Number(p.unitPrice ?? 0) || 0; |
| | | const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改) |
| | | const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2); |
| | | const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate); |
| | | return { |
| | | // 台账字段 |
| | | productCategory: p.product || p.productName || "", |
| | | specificationModel: p.specification || "", |
| | | unit: p.unit || "", |
| | | quantity: quantity, |
| | | taxRate: taxRate, |
| | | taxInclusiveUnitPrice: unitPrice.toFixed(2), |
| | | taxInclusiveTotalPrice: taxInclusiveTotalPrice, |
| | | taxExclusiveTotalPrice: taxExclusiveTotalPrice, |
| | | invoiceType: "增普票", |
| | | }; |
| | | }); |
| | | |
| | | quotationDialogVisible.value = false; |
| | | }; |
| | | function changs(val) { |
| | | console.log(val); |
| | |
| | | |
| | | const productIndex = ref(0); |
| | | // 打开产品弹框 |
| | | const openProductForm = (type, row,index) => { |
| | | const openProductForm = async (type, row, index) => { |
| | | productOperationType.value = type; |
| | | productForm.value = {}; |
| | | proxy.resetForm("productFormRef"); |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | productIndex.value = index; |
| | | // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表 |
| | | try { |
| | | const options = productOptions.value && productOptions.value.length > 0 |
| | | ? productOptions.value |
| | | : await getProductOptions(); |
| | | const categoryId = findNodeIdByLabel(options, productForm.value.productCategory); |
| | | if (categoryId) { |
| | | const models = await modelList({ id: categoryId }); |
| | | modelOptions.value = models || []; |
| | | // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值 |
| | | const currentModel = (modelOptions.value || []).find( |
| | | (m) => m.model === productForm.value.specificationModel |
| | | ); |
| | | if (currentModel) { |
| | | productForm.value.productModelId = currentModel.id; |
| | | } |
| | | } |
| | | } catch (e) { |
| | | // 加载失败时保持可编辑,不中断弹窗 |
| | | console.error("加载产品规格型号失败", e); |
| | | } |
| | | } |
| | | productFormVisible.value = true; |
| | | getProductOptions(); |
| | | }; |
| | | // 提交产品表单 |
| | | const submitProduct = () => { |