| | |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品宽(mm):"> |
| | | <el-input v-model="searchForm.width" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产品高(mm):"> |
| | | <el-input v-model="searchForm.height" |
| | | placeholder="请输入" |
| | | clearable |
| | | prefix-icon="Search" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="录入日期:"> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handleImport">导入</el-button> |
| | | <el-dropdown @command="handleHistoryImportCommand"> |
| | | <el-button type="primary" |
| | | plain> |
| | | 历史迁移<el-icon class="el-icon--right"> |
| | | <ArrowDown /> |
| | | </el-icon> |
| | | </el-button> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | | <el-dropdown-item command="notShipped">未出库</el-dropdown-item> |
| | | <el-dropdown-item command="shipped">已出库</el-dropdown-item> |
| | | </el-dropdown-menu> |
| | | </template> |
| | | </el-dropdown> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | |
| | | style="width: 100%" |
| | | :summary-method="summarizeMainTable" |
| | | @expand-change="expandChange" |
| | | height="calc(100vh - 18.5em)"> |
| | | height="calc(100vh - 22em)"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" |
| | |
| | | width="220" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="面积" |
| | | prop="productTotalArea" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="数量" |
| | | prop="productTotalQuantity" |
| | | width="120" |
| | | show-overflow-tooltip /> |
| | | <el-table-column label="发货状态" |
| | | width="140" |
| | | align="center"> |
| | |
| | | title="选择入库产品" |
| | | width="60%" |
| | | :close-on-click-modal="false"> |
| | | <div style="margin-bottom: 12px;"> |
| | | <el-form> |
| | | <el-form-item required> |
| | | <template #label> |
| | | <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;"> |
| | | <span>审批人选择:</span> |
| | | <el-button type="primary" |
| | | size="small" |
| | | @click="addStockApproverNode" |
| | | icon="Plus">新增节点</el-button> |
| | | </div> |
| | | </template> |
| | | <div class="approver-nodes-container"> |
| | | <div v-for="(node, index) in stockApproverNodes" |
| | | :key="node.id" |
| | | class="approver-node-item"> |
| | | <div class="approver-node-header"> |
| | | <span class="approver-node-label">审批节点 {{ index + 1 }}</span> |
| | | <el-button v-if="stockApproverNodes.length > 1" |
| | | type="danger" |
| | | size="small" |
| | | text |
| | | @click="removeStockApproverNode(index)" |
| | | icon="Delete">删除</el-button> |
| | | </div> |
| | | <el-select v-model="node.userId" |
| | | placeholder="请选择审批人" |
| | | filterable |
| | | clearable |
| | | style="width: 100%;"> |
| | | <el-option v-for="item in stockApproverOptions" |
| | | :key="item.userId" |
| | | :label="item.userName" |
| | | :value="item.userId" /> |
| | | </el-select> |
| | | </div> |
| | | </div> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <el-table :data="stockProductList" |
| | | border |
| | | stripe |
| | |
| | | const selectedStockProductIds = ref([]); |
| | | const stockLoading = ref(false); |
| | | const currentStockLedgerId = ref(null); |
| | | const stockApproverOptions = ref([]); |
| | | const stockApproverNodes = ref([{ id: 1, userId: null }]); |
| | | let nextStockApproverId = 2; |
| | | const addStockApproverNode = () => { |
| | | stockApproverNodes.value.push({ id: nextStockApproverId++, userId: null }); |
| | | }; |
| | | const removeStockApproverNode = index => { |
| | | stockApproverNodes.value.splice(index, 1); |
| | | }; |
| | | |
| | | const ledgerQrDialogVisible = ref(false); |
| | | const ledgerQrCompositeUrl = ref(""); |
| | |
| | | customerName: "", // 客户名称 |
| | | customerId: "", // 客户ID(查询下拉) |
| | | salesContractNo: "", // 销售合同编号 |
| | | width: undefined, // 产品宽(mm) |
| | | height: undefined, // 产品高(mm) |
| | | entryDate: null, // 录入日期 |
| | | entryDateStart: undefined, |
| | | entryDateEnd: undefined, |
| | |
| | | proxy.$modal.msgError("导入失败,请重试"); |
| | | }, |
| | | }); |
| | | const HISTORY_IMPORT_URL_MAP = { |
| | | notShipped: "/sales/ledger/salesHistory/notShippingImport", |
| | | shipped: "/sales/ledger/salesHistory/shippingImport", |
| | | }; |
| | | const HISTORY_IMPORT_TEMPLATE_URL_MAP = { |
| | | notShipped: "/sales/ledger/salesHistory/notShippingImportTemplate", |
| | | shipped: "/sales/ledger/salesHistory/shippingImportTemplate", |
| | | }; |
| | | const HISTORY_IMPORT_TEMPLATE_FILE_NAME_MAP = { |
| | | notShipped: "销售发货历史数据导入模板-未发货.xlsx", |
| | | shipped: "销售发货历史数据导入模板-已发货.xlsx", |
| | | }; |
| | | const currentImportCommand = ref("default"); |
| | | |
| | | const changeDaterange = value => { |
| | | if (value) { |
| | |
| | | delete params.customerName; |
| | | } |
| | | } |
| | | const widthValue = |
| | | params.width != null ? String(params.width).trim() : ""; |
| | | if (widthValue) { |
| | | params.width = widthValue; |
| | | } else { |
| | | delete params.width; |
| | | } |
| | | const heightValue = |
| | | params.height != null ? String(params.height).trim() : ""; |
| | | if (heightValue) { |
| | | params.height = heightValue; |
| | | } else { |
| | | delete params.height; |
| | | } |
| | | const shouldAutoExpandBySize = Boolean(params.width || params.height); |
| | | delete params.customerId; |
| | | return ledgerListPage(params) |
| | | .then(res => { |
| | | .then(async res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.records; |
| | | tableData.value.map(item => { |
| | | item.children = []; |
| | | }); |
| | | if (shouldAutoExpandBySize && tableData.value.length > 0) { |
| | | const loadChildrenTasks = tableData.value.map(item => |
| | | productList({ salesLedgerId: item.id, type: 1 }) |
| | | .then(productRes => { |
| | | item.children = Array.isArray(productRes?.data) |
| | | ? productRes.data |
| | | : []; |
| | | }) |
| | | .catch(() => { |
| | | item.children = []; |
| | | }) |
| | | ); |
| | | await Promise.all(loadChildrenTasks); |
| | | expandedRowKeys.value = tableData.value.map(item => item.id); |
| | | } |
| | | total.value = res.total; |
| | | return res; |
| | | }) |
| | |
| | | currentStockLedgerId.value = id; |
| | | selectedStockProductIds.value = []; |
| | | stockProductList.value = []; |
| | | stockApproverNodes.value = [{ id: 1, userId: null }]; |
| | | nextStockApproverId = 2; |
| | | stockDialogVisible.value = true; |
| | | stockLoading.value = true; |
| | | |
| | | try { |
| | | const approverRes = await approveUserList({ approveType: 9 }); |
| | | stockApproverOptions.value = Array.isArray(approverRes?.data) |
| | | ? approverRes.data.map(item => ({ |
| | | userId: item.userId, |
| | | userName: item.userName, |
| | | })) |
| | | : []; |
| | | const res = await productList({ salesLedgerId: id, type: 1 }); |
| | | stockProductList.value = []; |
| | | stockProductList.value = |
| | | res.data.filter(item => item.productStockStatus == 0 || item.productStockStatus == 1) || []; |
| | | } catch (e) { |
| | | proxy?.$modal?.msgError?.("获取产品列表失败"); |
| | | proxy?.$modal?.msgError?.("获取产品或审批人失败"); |
| | | } finally { |
| | | stockLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const submitStock = async () => { |
| | | const hasEmptyApprover = stockApproverNodes.value.some(node => !node.userId); |
| | | if (hasEmptyApprover) { |
| | | ElMessage.warning("请为所有审批节点选择审批人"); |
| | | return; |
| | | } |
| | | if (selectedStockProductIds.value.length === 0) { |
| | | ElMessage.warning("请选择至少一个产品进行入库"); |
| | | return; |
| | |
| | | |
| | | proxy?.$modal?.loading?.("正在入库,请稍候..."); |
| | | try { |
| | | const approveUserIds = stockApproverNodes.value.map(node => node.userId).join(","); |
| | | const approveUserName = stockApproverNodes.value |
| | | .map(node => stockApproverOptions.value.find(item => String(item.userId) === String(node.userId))?.userName) |
| | | .filter(Boolean) |
| | | .join(","); |
| | | await salesStock({ |
| | | salesLedgerId: currentStockLedgerId.value, |
| | | salesLedgerProducts: selectedStockProductIds.value, |
| | | approveUserIds, |
| | | approveUserName, |
| | | }); |
| | | proxy?.$modal?.msgSuccess?.("入库成功"); |
| | | stockDialogVisible.value = false; |
| | |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable(param, [ |
| | | "contractAmount", |
| | | "productTotalQuantity", |
| | | "productTotalArea", |
| | | "taxInclusiveTotalPrice", |
| | | "taxExclusiveTotalPrice", |
| | | ]); |
| | |
| | | otherAmountAddDialogVisible.value = false; |
| | | otherAmountAddId.value = null; |
| | | }; |
| | | // 导入 |
| | | const handleImport = () => { |
| | | importUpload.title = "导入销售台账"; |
| | | const openImportDialog = (title, url) => { |
| | | importUpload.title = title; |
| | | importUpload.url = import.meta.env.VITE_APP_BASE_API + url; |
| | | importUpload.open = true; |
| | | importUpload.isUploading = false; |
| | | if (importUploadRef.value) { |
| | | importUploadRef.value.clearFiles(); |
| | | } |
| | | }; |
| | | // 导入 |
| | | const handleImport = () => { |
| | | currentImportCommand.value = "default"; |
| | | openImportDialog("导入销售台账", "/sales/ledger/import"); |
| | | }; |
| | | // 历史迁移 |
| | | const handleHistoryImportCommand = command => { |
| | | const url = HISTORY_IMPORT_URL_MAP[command]; |
| | | if (!url) return; |
| | | currentImportCommand.value = command; |
| | | const title = command === "shipped" ? "历史迁移-已出库" : "历史迁移-未出库"; |
| | | openImportDialog(title, url); |
| | | }; |
| | | |
| | | // 下载导入模板 |
| | | const downloadTemplate = () => { |
| | | const command = currentImportCommand.value; |
| | | if (command && command !== "default") { |
| | | const templateUrl = HISTORY_IMPORT_TEMPLATE_URL_MAP[command]; |
| | | const fileName = HISTORY_IMPORT_TEMPLATE_FILE_NAME_MAP[command]; |
| | | if (templateUrl) { |
| | | proxy.download(templateUrl, {}, fileName || "销售发货历史数据导入模板.xlsx"); |
| | | return; |
| | | } |
| | | } |
| | | proxy.download("/sales/ledger/exportTemplate", {}, "销售台账导入模板.xlsx"); |
| | | }; |
| | | const onClose = () => { |
| | |
| | | .ledger-qr-save-btn { |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .approver-nodes-container { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 16px; |
| | | padding: 16px; |
| | | background-color: #f8f9fa; |
| | | border-radius: 4px; |
| | | border: 1px solid #e4e7ed; |
| | | } |
| | | |
| | | .approver-node-item { |
| | | flex: 0 0 calc(33.333% - 12px); |
| | | min-width: 200px; |
| | | padding: 12px; |
| | | background-color: #fff; |
| | | border-radius: 4px; |
| | | border: 1px solid #dcdfe6; |
| | | transition: all 0.3s; |
| | | } |
| | | |
| | | .approver-node-item:hover { |
| | | border-color: #409eff; |
| | | box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1); |
| | | } |
| | | |
| | | .approver-node-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .approver-node-label { |
| | | font-size: 13px; |
| | | font-weight: 500; |
| | | color: #606266; |
| | | } |
| | | |
| | | @media (max-width: 1200px) { |
| | | .approver-node-item { |
| | | flex: 0 0 calc(50% - 8px); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .approver-node-item { |
| | | flex: 0 0 100%; |
| | | } |
| | | } |
| | | </style> |