| src/views/salesManagement/salesLedger/components/ProcessFlowConfigSelectDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/salesManagement/salesLedger/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/views/salesManagement/salesLedger/components/ProcessFlowConfigSelectDialog.vue
@@ -8,10 +8,14 @@ > <el-row :gutter="20"> <el-col :span="24"> <div style="font-weight: 600; margin-bottom: 8px;">配置</div> <div style="font-size: 12px; margin-bottom: 8px;"> <span v-if="boundRouteName" style="color: #67c23a;">已绑定:{{ boundRouteName }}</span> <span v-else style="color: #e6a23c;">未绑定</span> <div class="dialog-topbar"> <div> <div style="font-weight: 600; margin-bottom: 8px;">配置</div> <div style="font-size: 12px; margin-bottom: 8px;"> <span v-if="boundRouteName" style="color: #67c23a;">已绑定:{{ boundRouteName }}</span> <span v-else style="color: #e6a23c;">未绑定</span> </div> </div> </div> <el-select v-model="selectedRouteId" @@ -33,7 +37,7 @@ <div style="font-weight: 600; margin-bottom: 8px;">步骤预览</div> <div style="font-size: 12px; color: #909399; margin-bottom: 10px;"> 根据所选配置展示流程图 根据所选配置展示流程图,勾选表示该工序已完成 </div> </el-col> @@ -46,8 +50,16 @@ class="process-diagram-segment" > <div class="process-diagram-node"> <el-checkbox v-model="step.isCompleted" class="process-diagram-checkbox" @change="() => handleStepCompletedChange(step)" /> <div class="process-diagram-index">{{ idx + 1 }}</div> <div class="process-diagram-name">{{ step.processName }}</div> <div class="process-diagram-status" :class="{ 'is-done': Number(step.isCompleted) === 1 }"> {{ Number(step.isCompleted) === 1 ? "已完成" : "未完成" }} </div> </div> <div v-if="idx < steps.length - 1" class="process-diagram-arrow">→</div> </div> @@ -79,6 +91,8 @@ visible: { type: Boolean, default: false }, // 打开弹窗时的回显:若业务已绑定工艺路线则传入该 routeId;否则默认展示列表第一条 defaultRouteId: { type: [Number, String, null], default: null }, // 打开弹窗时的工序完成记录回显 defaultRecordList: { type: Array, default: () => [] }, // 页面提示:订单已绑定的工艺路线名称 boundRouteName: { type: String, default: "" }, }); @@ -103,9 +117,11 @@ if (!Array.isArray(list)) return []; return list.map((s, idx) => ({ stepId: s.stepId ?? s.id ?? null, processRouteItemId: s.processRouteItemId ?? s.process_route_item_id ?? s.id ?? null, processId: s.processId ?? s.process_id ?? s.id ?? null, processName: s.processName ?? s.process_name ?? s.name ?? "", sortNo: s.sortNo ?? idx + 1, isCompleted: Boolean(Number(s.isCompleted ?? s.completed ?? 0)), })); }; @@ -116,6 +132,27 @@ processRouteName: r.processRouteName ?? r.routeName ?? r.name ?? "", isDefault: Boolean(r.isDefault), })); }; const applyRecordListToSteps = (stepList, recordList) => { if (!Array.isArray(stepList) || stepList.length === 0) return stepList; if (!Array.isArray(recordList) || recordList.length === 0) return stepList; const recordMap = new Map( recordList .filter((item) => item && item.processRouteItemId !== null && item.processRouteItemId !== undefined) .map((item) => [String(item.processRouteItemId), item]) ); return stepList.map((step) => { const matched = recordMap.get(String(step.processRouteItemId)); if (!matched) return step; return { ...step, isCompleted: Boolean(Number(matched.isCompleted ?? 0)), completedTime: matched.completedTime ?? matched.completed_time ?? null, }; }); }; const fetchRouteList = async () => { @@ -132,7 +169,12 @@ } const res = await salesProcessFlowConfigItemList(routeId); const raw = res?.data ?? res ?? []; steps.value = normalizeStepsFromApi(raw); const normalizedSteps = normalizeStepsFromApi(raw); if (String(routeId) === String(props.defaultRouteId)) { steps.value = applyRecordListToSteps(normalizedSteps, props.defaultRecordList); return; } steps.value = normalizedSteps; }; watch( @@ -163,6 +205,10 @@ await fetchRouteSteps(selectedRouteId.value); }; const handleStepCompletedChange = (step) => { step.isCompleted = Boolean(step.isCompleted); }; const handleClose = () => { emit("update:visible", false); saving.value = false; @@ -176,7 +222,13 @@ } saving.value = true; try { emit("confirm", selectedRouteId.value); emit("confirm", { routeId: selectedRouteId.value, recordList: steps.value.map((step) => ({ processRouteItemId: step.processRouteItemId, isCompleted: Number(step.isCompleted ?? 0), })), }); } catch (e) { proxy?.$modal?.msgError?.("确认失败,请稍后重试"); } finally { @@ -213,6 +265,13 @@ padding: 10px 12px; margin-right: 10px; box-sizing: border-box; position: relative; } .process-diagram-checkbox { position: absolute; top: 8px; right: 8px; } .process-diagram-index { @@ -228,6 +287,17 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .process-diagram-status { margin-top: 4px; font-size: 12px; color: #909399; } .process-diagram-status.is-done { color: #67c23a; font-weight: 600; } .process-diagram-arrow { @@ -251,5 +321,11 @@ justify-content: flex-end; gap: 10px; } </style> .dialog-topbar { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; } </style> src/views/salesManagement/salesLedger/index.vue
@@ -832,6 +832,7 @@ </div> <ProcessFlowConfigSelectDialog v-model:visible="processFlowSelectDialogVisible" :default-route-id="processFlowSelectDefaultRouteId" :default-record-list="processFlowSelectDefaultRecordList" :bound-route-name="processFlowSelectBoundRouteName" @confirm="handleProcessFlowSelectConfirm" /> <div class="sales-ledger-toolbar-actions"> @@ -866,6 +867,17 @@ <el-button type="primary" @click="handleBulkDelivery" :disabled="isBatchButtonDisabled('delivery')">发货</el-button> <el-date-picker v-model="processRouteExportDateRange" type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss" clearable style="width: 340px;" /> <el-button @click="handleExportProcessRoute" :disabled="isBatchButtonDisabled('export')">导出工艺路线</el-button> </el-space> <el-space v-else-if="activeStatusTab === 'stocked'" wrap @@ -2609,11 +2621,13 @@ }); const total = ref(0); const fileList = ref([]); const processRouteExportDateRange = ref([]); // 工艺路线配置选择弹窗(绑定到台账产品) const processFlowSelectDialogVisible = ref(false); const processFlowSelectLedgerRow = ref(null); const processFlowSelectDefaultRouteId = ref(null); const processFlowSelectDefaultRecordList = ref([]); const processFlowSelectBoundRouteId = ref(null); const processFlowSelectBoundRouteName = ref(""); @@ -4282,6 +4296,7 @@ processFlowSelectLedgerRow.value = ledgerRow; processFlowSelectDefaultRouteId.value = null; processFlowSelectDefaultRecordList.value = []; processFlowSelectBoundRouteId.value = null; processFlowSelectBoundRouteName.value = ""; @@ -4291,25 +4306,29 @@ const boundId = info?.processRouteId ?? info?.routeId ?? info?.id ?? null; const boundName = info?.processRouteName ?? info?.routeName ?? info?.name ?? ""; const recordList = Array.isArray(info?.recordList) ? info.recordList : []; processFlowSelectBoundRouteId.value = boundId; processFlowSelectBoundRouteName.value = boundName; processFlowSelectDefaultRouteId.value = boundId; processFlowSelectDefaultRecordList.value = recordList; } catch (e) { // 查询失败时按未绑定处理,不阻塞弹窗 processFlowSelectBoundRouteId.value = null; processFlowSelectBoundRouteName.value = ""; processFlowSelectDefaultRouteId.value = null; processFlowSelectDefaultRecordList.value = []; } processFlowSelectDialogVisible.value = true; }; // 绑定工艺路线到当前台账数据 const handleProcessFlowSelectConfirm = async routeId => { const handleProcessFlowSelectConfirm = async payload => { const ledgerRow = processFlowSelectLedgerRow.value; if (!ledgerRow?.id) return; const finalRouteId = routeId ?? null; const finalRouteId = payload?.routeId ?? payload ?? null; const recordList = Array.isArray(payload?.recordList) ? payload.recordList : []; if (!finalRouteId) return; const oldRouteId = processFlowSelectBoundRouteId.value; @@ -4339,6 +4358,7 @@ await saleProcessBind({ salesLedgerId: ledgerRow.id, processRouteId: finalRouteId, recordList, }); proxy?.$modal?.msgSuccess?.("工艺路线绑定成功"); @@ -5263,30 +5283,65 @@ proxy.$refs["importUploadRef"].submit(); }; // 导出 // 导出(按当前查询条件导出) const handleOut = () => { if (selectedRows.value.length === 0) { proxy.$modal.msgWarning("请至少选择一条数据进行导出"); return; // 构建查询参数(与 getList 保持一致) const { entryDate, ...rest } = searchForm; const params = { ...rest }; // 处理录入日期范围 if (entryDate && entryDate.length === 2) { params.entryDateStart = entryDate[0]; params.entryDateEnd = entryDate[1]; } const hasUnapproved = selectedRows.value.some( row => Number(row.reviewStatus) !== 1 // 处理客户名称查询 const selectedCustomer = (customerOption.value || []).find( item => String(item?.id ?? "") === String(params.customerId ?? "") ); if (hasUnapproved) { proxy.$modal.msgWarning("选中的数据中包含未审核项,无法导出"); if (selectedCustomer?.customerName) { params.customerName = String(selectedCustomer.customerName).trim(); } delete params.customerId; // 处理产品宽高查询参数 const widthValue = params.width != null ? String(params.width).trim() : ""; const heightValue = params.height != null ? String(params.height).trim() : ""; if (!widthValue) delete params.width; if (!heightValue) delete params.height; proxy.download("/sales/ledger/exportWithProducts", params, "销售台账.xlsx"); }; const handleExportProcessRoute = () => { if (selectedRows.value.length === 0) { proxy?.$modal?.msgWarning?.("请选择要导出的销售台账"); return; } ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", { confirmButtonText: "确认", cancelButtonText: "取消", type: "warning", }) .then(() => { proxy.download("/sales/ledger/export", {}, "销售台账.xlsx"); }) .catch(() => { proxy.$modal.msg("已取消"); }); const salesLedgerIds = selectedRows.value .map(item => item.id) .filter(id => id !== null && id !== undefined && id !== ""); if (salesLedgerIds.length === 0) { proxy?.$modal?.msgWarning?.("请选择要导出的销售台账"); return; } const params = { salesLedgerIds: salesLedgerIds.join(","), }; if ( Array.isArray(processRouteExportDateRange.value) && processRouteExportDateRange.value.length === 2 ) { params.completedTimeStart = processRouteExportDateRange.value[0]; params.completedTimeEnd = processRouteExportDateRange.value[1]; } proxy.download( "/sales/ledger/exportProcessRoute", params, "销售台账工艺路线导出.xlsx" ); }; /** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */ const isProductShipped = product => {