| | |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <el-button type="danger" plain @click="handleDelete">删除</el-button> |
| | | <el-button type="primary" plain @click="handlePrint">打印</el-button> |
| | | <el-button type="primary" plain @click="handleExport">导出送货单</el-button> |
| | | </div> |
| | | </div> |
| | | <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column align="center" label="序号" type="index" width="60" /> |
| | | <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip /> |
| | | <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="型号" prop="model" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="电压" prop="voltage" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="qty" prop="qty" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip /> |
| | | <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip /> |
| | | <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip /> |
| | | <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip /> |
| | | <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip /> |
| | | <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="操作" min-width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="openForm('edit', scope.row)" |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="是否生产:" prop="produce"> |
| | | <el-radio-group v-model="form.produce"> |
| | | <el-radio-group v-model="form.produce" :disabled="operationType !== 'add'"> |
| | | <el-radio :label="true">是</el-radio> |
| | | <el-radio :label="false">否</el-radio> |
| | | </el-radio-group> |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="规格型号:" prop="productModelId"> |
| | | <el-select v-model="productForm.productModelId" placeholder="请选择" clearable |
| | | <el-form-item label="电压:" prop="stockId"> |
| | | <el-select v-model="productForm.stockId" placeholder="请选择" clearable |
| | | @change="getProductModel" filterable> |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" |
| | | <el-option v-for="item in modelOptions" :key="item.id" :label="item.label" |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | 暂无打印数据 |
| | | </div> |
| | | <div v-else |
| | | style="text-align: center; padding: 10px; color: #666; font-size: 16px; background: #e8f4fd; margin-bottom: 10px;"> |
| | | style="text-align: center; padding: 10px; color: #666; font-size: 18px; background: #e8f4fd; margin-bottom: 10px;"> |
| | | 共 {{ printData.length }} 条数据待打印 |
| | | </div> |
| | | <div v-for="(item, index) in printData" :key="index" class="print-page"> |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <span style="font-size: 20px; margin-top: 5px;">货物详细信息:</span> |
| | | <span style="font-size: 18px; margin-top: 5px;">货物详细信息:</span> |
| | | <div class="table-section"> |
| | | <table class="product-table"> |
| | | <thead> |
| | |
| | | <td>{{ item.salesContractNo || '' }}</td> |
| | | </tr> |
| | | <!-- 添加空白行以确保至少5行 --> |
| | | <tr v-for="n in Math.max(0, 5 - (item.products ? item.products.length : 0))" :key="'empty-' + n" style="height: 25px;"> |
| | | <td style="height: 35px;"></td> |
| | | <td style="height: 35px;"></td> |
| | | <td style="height: 35px;"></td> |
| | | <td style="height: 35px;"></td> |
| | | <td style="height: 35px;"></td> |
| | | <td style="height: 35px;"></td> |
| | | <td style="height: 35px;"></td> |
| | | <tr v-for="n in Math.max(0, 5 - (item.products ? item.products.length : 0))" :key="'empty-' + n"> |
| | | <td> </td> |
| | | <td> </td> |
| | | <td> </td> |
| | | <td> </td> |
| | | <td> </td> |
| | | <td> </td> |
| | | <td> </td> |
| | | </tr> |
| | | </tbody> |
| | | <tfoot> |
| | |
| | | </table> |
| | | |
| | | <div style="width: 80px; display: flex; flex-direction: column;"> |
| | | <div style="height: 35px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">共四联</div> |
| | | <div style="height: 35px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">存根</div> |
| | | <div style="height: 35px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">客户</div> |
| | | <div style="height: 35px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">记账</div> |
| | | <div style="height: 35px; text-align: center; display: flex; align-items: center; justify-content: center;">仓库</div> |
| | | <div style="height: 33px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">共四联</div> |
| | | <div style="height: 28px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">存根</div> |
| | | <div style="height: 29px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">客户</div> |
| | | <div style="height: 29px; border-bottom: 1px dashed #000; text-align: center; display: flex; align-items: center; justify-content: center;">记账</div> |
| | | <div style="height: 28px; text-align: center; display: flex; align-items: center; justify-content: center;">仓库</div> |
| | | </div> |
| | | </div> |
| | | <div style="margin: 10px 0; font-size: 20px;"> |
| | | <div style="margin: 10px 0; font-size: 16px;"> |
| | | <span>备注: 贵公司在收货后请即刻核实数量及品质,若有异议,请在3日内提出,否则视为收妥。</span> |
| | | </div> |
| | | <div class="footer"> |
| | |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue'; |
| | | import { getQuotationList } from "@/api/salesManagement/salesQuotation.js"; |
| | | import { |
| | | ledgerListPage, |
| | | productList, |
| | | customerList, |
| | | addOrUpdateSalesLedger, |
| | | getSalesLedgerWithProducts, |
| | | delLedger, |
| | | addOrUpdateSalesLedgerProduct, |
| | | delProduct, |
| | | delLedgerFile, getProductInventory, |
| | | ledgerListPage, |
| | | productList, |
| | | customerList, |
| | | addOrUpdateSalesLedger, |
| | | getSalesLedgerWithProducts, |
| | | delLedger, |
| | | addOrUpdateSalesLedgerProduct, |
| | | delProduct, |
| | | delLedgerFile, getProductInventory, |
| | | } from "@/api/salesManagement/salesLedger.js"; |
| | | import { modelList, productTreeList } from "@/api/basicData/product.js"; |
| | | import { finishedProductList } from "@/api/basicData/product.js"; |
| | | import useFormData from "@/hooks/useFormData.js"; |
| | | import dayjs from "dayjs"; |
| | | import { getCurrentDate } from "@/utils/index.js"; |
| | |
| | | const userList = ref([]); |
| | | const customerOption = ref([]); |
| | | const productOptions = ref([]); |
| | | const productOptionsRaw = ref([]); // 保存原始数据(包含 children) |
| | | const modelOptions = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ |
| | |
| | | // 获取产品大类tree数据 |
| | | const getProductOptions = () => { |
| | | // 返回 Promise,便于在编辑产品时等待加载完成 |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | return finishedProductList({}).then((res) => { |
| | | // 保存原始数据(包含 children)用于后续提取电压列表 |
| | | productOptionsRaw.value = res || []; |
| | | // 产品大类下拉选项 |
| | | productOptions.value = (res || []).map(item => ({ |
| | | ...item, |
| | | value: item.productId, // 用 productId 作为 value |
| | | children: undefined // 去掉下拉箭头 |
| | | })); |
| | | return productOptions.value; |
| | | }); |
| | | }; |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | // 获取tree子数据 |
| | | // 获取tree子数据(从 children 中提取电压列表) |
| | | const getModels = (value) => { |
| | | productForm.value.productCategory = findNodeById(productOptions.value, value); |
| | | modelList({ id: value }).then((res) => { |
| | | modelOptions.value = res; |
| | | }); |
| | | // 从原始数据中找到选中的节点(包含 children) |
| | | const selectedNode = productOptionsRaw.value.find(item => item.productId === value); |
| | | if (selectedNode && selectedNode.children && selectedNode.children.length > 0) { |
| | | // 提取 children 中的型号数据,拼接显示文本 |
| | | modelOptions.value = selectedNode.children |
| | | .map(item => { |
| | | // 拼接显示文本:model - processCategory - voltage(过滤空字符串) |
| | | const parts = []; |
| | | if (item.model && item.model.trim()) parts.push(item.model); |
| | | if (item.processCategory && item.processCategory.trim()) parts.push(item.processCategory); |
| | | if (item.voltage && item.voltage.trim()) parts.push(item.voltage); |
| | | return { |
| | | ...item, |
| | | id: item.stockId, // 用 stockId 作为唯一标识 |
| | | productModelId: item.productModelId, // 保存 productModelId 用于提交 |
| | | label: parts.join('-') |
| | | }; |
| | | }) |
| | | .filter(item => item.label); // 过滤掉 label 为空的选项 |
| | | } else { |
| | | modelOptions.value = []; |
| | | } |
| | | }; |
| | | const getProductModel = (value) => { |
| | | const index = modelOptions.value.findIndex((item) => item.id === value); |
| | | if (index !== -1) { |
| | | productForm.value.specificationModel = modelOptions.value[index].model; |
| | | productForm.value.unit = modelOptions.value[index].unit; |
| | | productForm.value.materialCode = modelOptions.value[index].materialCode; |
| | | const selectedItem = modelOptions.value[index]; |
| | | // 规格型号存储拼接值,用于表格显示 |
| | | productForm.value.specificationModel = selectedItem.label; |
| | | productForm.value.unit = selectedItem.unit; |
| | | productForm.value.materialCode = selectedItem.materialCode; |
| | | // 保存电压信息 |
| | | productForm.value.voltage = selectedItem.voltage; |
| | | // 保存工序类别 |
| | | productForm.value.processCategory = selectedItem.processCategory; |
| | | // 保存 productModelId 用于提交 |
| | | productForm.value.productModelId = selectedItem.productModelId; |
| | | // 保存 stockId 用于提交 |
| | | productForm.value.stockId = selectedItem.stockId; |
| | | } else { |
| | | productForm.value.specificationModel = null; |
| | | productForm.value.unit = null; |
| | | productForm.value.materialCode = null; |
| | | productForm.value.voltage = null; |
| | | productForm.value.processCategory = null; |
| | | productForm.value.productModelId = null; |
| | | productForm.value.stockId = null; |
| | | } |
| | | }; |
| | | const findNodeById = (nodes, productId) => { |
| | |
| | | }; |
| | | function convertIdToValue(data) { |
| | | return data.map((item) => { |
| | | const { id, children, ...rest } = item; |
| | | const { productId, children, ...rest } = item; |
| | | const newItem = { |
| | | ...rest, |
| | | value: id, // 将 id 改为 value |
| | | value: productId, // 产品大类用 productId 作为 value |
| | | }; |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children); |
| | | newItem.children = children; // children 保持原样 |
| | | } |
| | | |
| | | return newItem; |
| | |
| | | if (type === "edit") { |
| | | productForm.value = { ...row }; |
| | | productIndex.value = index; |
| | | // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表 |
| | | // 编辑时根据产品大类名称反查 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; |
| | | // 从原始数据中找到选中的节点,提取 children |
| | | const selectedNode = productOptionsRaw.value.find(item => item.productId === categoryId); |
| | | if (selectedNode && selectedNode.children && selectedNode.children.length > 0) { |
| | | modelOptions.value = selectedNode.children |
| | | .map(item => { |
| | | // 拼接显示文本:model - processCategory - voltage(过滤空字符串) |
| | | const parts = []; |
| | | if (item.model && item.model.trim()) parts.push(item.model); |
| | | if (item.processCategory && item.processCategory.trim()) parts.push(item.processCategory); |
| | | if (item.voltage && item.voltage.trim()) parts.push(item.voltage); |
| | | return { |
| | | ...item, |
| | | id: item.stockId, // 用 stockId 作为唯一标识 |
| | | productModelId: item.productModelId, |
| | | label: parts.join('-') |
| | | }; |
| | | }) |
| | | .filter(item => item.label); // 过滤掉 label 为空的选项 |
| | | } |
| | | // 根据当前 stockId 设置选中值(用于下拉框回显) |
| | | productForm.value.stockId = row.stockId; |
| | | // 同时保存 productModelId |
| | | productForm.value.productModelId = row.productModelId; |
| | | } |
| | | } catch (e) { |
| | | // 加载失败时保持可编辑,不中断弹窗 |
| | |
| | | }); |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | if (selectedRows.value.length === 0) { |
| | | proxy.$modal.msgWarning("请选择要导出送货单的数据"); |
| | | return; |
| | | } |
| | | |
| | | ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/sales/ledger/exportShippingNote", {ids: selectedRows.value.map((item) => item.id).join(",")}, "送货单.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | | }); |
| | | } |
| | | |
| | | // 打印功能 |
| | | const handlePrint = async () => { |
| | | if (selectedRows.value.length === 0) { |
| | |
| | | width: 100%; |
| | | height: 100%; |
| | | font-size: 18px; |
| | | line-height: 1.5; |
| | | line-height: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | color: #000; |
| | | } |
| | | .header { |
| | | text-align: center; |
| | | margin-bottom: 8px; |
| | | } |
| | | .company-name { |
| | | font-size: 32px; |
| | | font-size: 30px; |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | .document-title { |
| | | font-size: 28px; |
| | | font-size: 25px; |
| | | font-weight: bold; |
| | | letter-spacing: 10px; |
| | | } |
| | |
| | | align-items: center; |
| | | } |
| | | .info-row { |
| | | line-height: 26px; |
| | | line-height: 20px; |
| | | } |
| | | .label { |
| | | font-weight: bold; |
| | | min-width: 60px; |
| | | font-size: 20px; |
| | | font-size: 18px; |
| | | } |
| | | .value { |
| | | margin-right: 20px; |
| | | min-width: 80px; |
| | | font-size: 20px; |
| | | font-size: 18px; |
| | | } |
| | | .table-section { |
| | | margin-bottom: 4px; |
| | |
| | | } |
| | | .product-table th, .product-table td { |
| | | border: 1px solid #000; |
| | | padding: 6px; |
| | | padding: 3px; |
| | | text-align: center; |
| | | font-size: 20px; |
| | | line-height: 1.4; |
| | | font-size: 18px; |
| | | line-height: 1.2; |
| | | } |
| | | .product-table th { |
| | | font-size: 20px; |
| | | font-size: 19px; |
| | | font-weight: bold; |
| | | } |
| | | .total-value { |
| | |
| | | margin: 10px 0; |
| | | } |
| | | .sign-section { |
| | | margin-top: 15px; |
| | | margin-top: 10px; |
| | | } |
| | | .footer { |
| | | display: flex; |
| | |
| | | width: 50%; |
| | | } |
| | | .info-row { |
| | | line-height: 26px; |
| | | line-height: 20px; |
| | | } |
| | | .label { |
| | | font-weight: bold; |
| | | min-width: 60px; |
| | | font-size: 20px; |
| | | font-size: 18px; |
| | | } |
| | | .value { |
| | | margin-right: 20px; |
| | | min-width: 80px; |
| | | font-size: 20px; |
| | | font-size: 18px; |
| | | } |
| | | } |
| | | @media print { |
| | |
| | | padding-right: 10mm; |
| | | page-break-inside: avoid; |
| | | page-break-after: always; |
| | | font-size: 20px; |
| | | font-size: 18px; |
| | | } |
| | | .print-page:last-child { |
| | | page-break-after: avoid; |
| | |
| | | th, |
| | | td { |
| | | border: 1px solid #000; |
| | | padding: 6px; |
| | | padding: 3px; |
| | | text-align: center; |
| | | font-size: 16px; |
| | | line-height: 1.4; |
| | |
| | | page-break-after: avoid; |
| | | } |
| | | } |
| | | </style> |
| | | </style> |