diff --git a/src/api/basicProcessPlan.ts b/src/api/basicProcessPlan.ts index 03ae68f..5a8cc78 100644 --- a/src/api/basicProcessPlan.ts +++ b/src/api/basicProcessPlan.ts @@ -22,6 +22,14 @@ export interface BasicProcessPlan { departmentName?: string + sectionId?: number | null + + workShopId?: number | null + + sectionName?: string + + workShopName?: string + operDescription?: string createBy?: number diff --git a/src/api/device.ts b/src/api/device.ts index 7324e36..ba220eb 100644 --- a/src/api/device.ts +++ b/src/api/device.ts @@ -31,15 +31,51 @@ export interface DeviceRealtimeVO { displayName: string synEquipId: number bound: boolean + /** connected:机台是否在线 */ connected?: number | null + /** cnc_status:控制器状态 */ cncStatus?: number | null cncStatusText?: string + /** part_count:目前工件数 */ partCount?: number | null + /** part_count_required:需求工件数 */ + partCountRequired?: number | null + /** main_program:各轴群的当前加工号码 */ mainProgram?: string | null + /** feedrate_act:轴群的实际进给速率 */ + feedrateAct?: number | null + /** feedrate_override:轴群的实际进给倍率 */ + feedrateOverride?: number | null + /** speed_act:轴群的加工主轴转速 */ + speedAct?: number | null + /** speed_cmd:轴群的加工主轴转速命令 */ + speedCmd?: number | null + /** load:轴群的加工主轴负载率 */ + load?: number | null + /** speed_override:轴群的加工主轴速度百分比 */ + speedOverride?: number | null + /** tool_id:各轴群的加工主轴刀号 */ + toolId?: string | null + /** torque_load:轴向负载率 */ + torqueLoad?: number | null + /** span_cutting_cycle:单件加工时间 */ + spanCuttingCycle?: number | null + /** span_cutting_acc:累计加工时间 */ + spanCuttingAcc?: number | null + /** span_power_on_acc:累计开机时间 */ + spanPowerOnAcc?: number | null + /** span_cutting_install:安装累计加工时间 */ + spanCuttingInstall?: number | null + /** span_power_on:开机时间 */ + spanPowerOn?: number | null + /** alarm_current:警报相关 */ alarmCount?: number alarmMessages?: string[] dataTimestamp?: number + sectionId?: number | null sectionName?: string + workshopId?: number | null + workshopName?: string } diff --git a/src/api/orderProcessPlan.ts b/src/api/orderProcessPlan.ts index f62c023..ec1f2ec 100644 --- a/src/api/orderProcessPlan.ts +++ b/src/api/orderProcessPlan.ts @@ -70,6 +70,9 @@ export interface ProcessPlanItemVO { workOrderCode?: string + /** 拆单来源工序计划ID */ + splitFromId?: number | null + processCode?: string operNumber?: number @@ -150,6 +153,9 @@ export interface OrderProcessPlanVO { orderItemId?: number + /** 工单号(列表按工单分组) */ + workOrderCode?: string + mainCode?: string materialCode?: string @@ -290,7 +296,7 @@ export function toOrderProcessPlanPayload(data: Record): OrderP const displayKeys = [ - 'mainCode', 'materialCode', 'materialName', 'orderCode', 'routeCode', 'workshopName', + 'mainCode', 'materialCode', 'materialName', 'orderCode', 'routeCode', 'workshopName', 'workOrderCode', 'actualStartTime', 'actualEndTime', 'completedQty', 'orderStatus', 'currentProcessName', @@ -302,7 +308,7 @@ export function toOrderProcessPlanPayload(data: Record): OrderP 'transferOutQty', 'scrapQty', 'reworkQty', 'assign', 'assignByName', 'assignTime', - 'processStatus', 'prevPlanId', 'nextPlanId', 'assignWorkList', + 'processStatus', 'prevPlanId', 'nextPlanId', 'assignWorkList', 'splitFromId', ] @@ -354,9 +360,9 @@ export const orderProcessPlanApi = { - // 获取详情(路径参数为生产订单 id) + // 获取详情(路径参数为生产订单 id;可选 workOrderCode 隔离拆单工单) - detail(orderItemId: number | string) { + detail(orderItemId: number | string, workOrderCode?: string) { return request({ @@ -364,6 +370,8 @@ export const orderProcessPlanApi = { method: 'get', + params: workOrderCode ? { workOrderCode } : undefined, + }) }, @@ -493,6 +501,81 @@ export const orderProcessPlanApi = { }) }, + /** 工序工单拆单预览 */ + splitPreview(planId: number | string) { + return request({ + url: `/biz/orderProcessPlan/split/preview/${planId}`, + method: 'get', + }) + }, + + /** 工序工单拆单 */ + split(data: ProcessPlanSplitPayload) { + return request({ + url: '/biz/orderProcessPlan/split', + method: 'post', + data, + }) + }, + +} + +/** 拆单预览 */ +export interface ProcessPlanSplitPreview { + planId?: number + workOrderCode?: string + orderItemId?: number + orderItemName?: string + /** 物料编码,用于查金蝶工艺路线 */ + materialCode?: string + processCount?: number + processNames?: string[] + /** 工单计划数量 */ + quantity?: number + /** 已进入生产数量(不可拆)= MAX(各工序投入) */ + investedQty?: number + /** 可拆 = 计划 − 已进入生产数量 */ + splittableQty?: number + canSplit?: boolean + reason?: string | null +} + +/** 拆单请求:quantities[0]=原单保留(≥不可拆),其余=新工单;新工艺二选一 */ +export interface ProcessPlanSplitPayload { + planId: number + quantities: number[] + /** kingdee=金蝶工艺路线;assemble=工序表组装 */ + routeMode: 'kingdee' | 'assemble' + /** routeMode=kingdee:金蝶工艺路线编码 FNumber */ + routingNo?: string | null + /** routeMode=assemble:完整工序明细(推荐) */ + assembleSteps?: ProcessPlanSplitStep[] + /** 兼容旧版:仅工序 id */ + processIds?: number[] +} + +/** 拆单组装工序行(对齐金蝶工艺路线主要字段) */ +export interface ProcessPlanSplitStep { + processId?: number | null + operNumber?: number | null + processCode?: string | null + processName?: string | null + operDescription?: string | null + sectionId?: number | null + workshopId?: number | null + activityUnit?: string | null + optCtrlCodeName?: string | null + activityQty?: number | null + qualityInspection?: number | null + storageEntry?: number | null + procurement?: number | null + isOutsource?: number | null +} + +/** 拆单结果 */ +export interface ProcessPlanSplitResult { + source?: ProcessPlanItemVO + created?: ProcessPlanItemVO[] } /** 委外可选工序项 */ diff --git a/src/api/processRoute.ts b/src/api/processRoute.ts index 1cd883a..74d27c7 100644 --- a/src/api/processRoute.ts +++ b/src/api/processRoute.ts @@ -88,5 +88,43 @@ export const processRouteApi = { // 下载导入模板 downloadTemplate() { return request({ url: `/biz/processRoute/template`, method: 'get', responseType: 'blob' }) - } + }, + + /** 按物料编码查询金蝶工艺路线选项(含工艺路线编码) */ + kingdeeSelect(materialCode: string) { + return request({ + url: '/biz/processRoute/kingdee/select', + method: 'get', + params: { materialCode }, + }) + }, +} + +/** 金蝶工艺路线下拉选项 */ +export interface ProcessRouteSelectOption { + routingNo?: string + materialCode?: string + materialName?: string + stepCount?: number + processNames?: string[] + /** 工序明细 */ + steps?: KingdeeProcessRouteStep[] +} + +/** 金蝶工艺路线工序行 */ +export interface KingdeeProcessRouteStep { + routingNo?: string + materialCode?: string + materialName?: string + operNumber?: number + processCode?: string + processName?: string + operDescription?: string + workCenterName?: string + departmentName?: string + optCtrlCodeName?: string + activityUnit?: string + activityQty?: number + qualityInspection?: number + storageEntry?: number } diff --git a/src/router/index.ts b/src/router/index.ts index 71163c7..da9b3f5 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -208,6 +208,13 @@ const routes: RouteRecordRaw[] = [ component: () => import('@/views/biz/maintenanceRecord/index.vue'), meta: { title: '设备维修保养记录', icon: 'BuildOutline' } }, + // 设备状态监控(独立页) + { + path: 'biz/device/board', + name: 'deviceRealtimeBoard', + component: () => import('@/views/biz/device/board.vue'), + meta: { title: '设备状态监控', icon: 'PulseOutline' } + }, // 开发工具 { path: 'tool/gen', diff --git a/src/views/biz/device/board.vue b/src/views/biz/device/board.vue index 13ea600..b40db81 100644 --- a/src/views/biz/device/board.vue +++ b/src/views/biz/device/board.vue @@ -1,26 +1,31 @@ diff --git a/src/views/biz/orderProcessPlan/board.vue b/src/views/biz/orderProcessPlan/board.vue index 855d582..87b565e 100644 --- a/src/views/biz/orderProcessPlan/board.vue +++ b/src/views/biz/orderProcessPlan/board.vue @@ -62,7 +62,6 @@
-
产品 / 工单编号
数量
模式
@@ -80,29 +79,13 @@
-
+
-
- - - -
{{ order.materialName || '-' }}
@@ -153,43 +136,6 @@
-
-
-
- 返工单 - 创建返工单 -
-
暂无返工单
-
-
- {{ item.code }} - {{ item.quantity ?? '-' }} · {{ item.statusLabel }} -
-
-
-
-
- 拆分工单 - 拆分工单 -
-
暂无拆分工单
-
-
- {{ item.code }} - {{ item.quantity ?? '-' }} · {{ item.statusLabel }} -
-
-
-
-
暂无工序明细
@@ -221,8 +167,6 @@ - -
+ + + @@ -664,13 +615,13 @@ import { ref, reactive, onMounted, h, computed, type Component } from 'vue' import { NButton, NSpace, NIcon, NTag, NSpin, NModal, NForm, NFormItem, NGrid, NGi, NInput, NInputNumber, NDatePicker, NCheckbox, NEmpty, NPagination, NDropdown, NDescriptions, - NDescriptionsItem, NAlert, NUpload, NText, NCard, NRadioGroup, NRadio, useMessage, useDialog, + NDescriptionsItem, NAlert, NUpload, NText, NCard, NRadioGroup, NRadio, NDivider, useMessage, useDialog, type UploadCustomRequestOptions, type FormRules, } from 'naive-ui' import { - AddOutline, RemoveOutline, SearchOutline, RefreshOutline, CloudUploadOutline, + AddOutline, SearchOutline, RefreshOutline, CloudUploadOutline, DownloadOutline, ChevronDownOutline, CheckmarkCircleOutline, CheckmarkCircle, - EllipseOutline, TimeOutline, PrintOutline, SyncOutline, PeopleOutline, + EllipseOutline, TimeOutline, PrintOutline, PeopleOutline, GitBranchOutline, CloseCircleOutline, LockClosedOutline, TrashOutline, EyeSharp, MenuOutline, } from '@vicons/ionicons5' @@ -688,6 +639,7 @@ import { import { useUserStore } from '@/stores/user' import ProcessFlowStepper from './components/ProcessFlowStepper.vue' import ProcessCard from './components/ProcessCard.vue' +import SplitProcessDialog from './components/SplitProcessDialog.vue' import PlmModelDrawer from '@/components/PlmModelDrawer.vue' import type { PlmModelOpenContext } from '@/api/plmModel' import Pgitem from './pgitem.vue' @@ -706,9 +658,8 @@ const searchForm = reactive({ }) const tableData = ref([]) -const expandedIds = ref>(new Set()) -const derivedExpandedIds = ref>(new Set()) -const selectedOrderIds = ref([]) +const expandedIds = ref>(new Set()) +const selectedOrderIds = ref([]) const loading = ref(false) const pagination = reactive({ page: 1, pageSize: 10, itemCount: 0 }) @@ -716,6 +667,10 @@ const detailVisible = ref(false) const detailLoading = ref(false) const detailData = ref(null) +/** 工序工单拆单 */ +const splitVisible = ref(false) +const splitPlanId = ref(null) + /** 订单级工序重排弹窗状态 */ const reorderVisible = ref(false) const reorderLoading = ref(false) @@ -835,7 +790,6 @@ const moreOptions = [ label: '派生操作', key: 'group-derived', children: [ - { label: '创建返工单', key: 'createRework', icon: dropdownIcon(SyncOutline) }, { label: '创建工序委外', key: 'createOutsource', icon: dropdownIcon(PeopleOutline) }, { label: '拆分工单', key: 'splitWorkOrder', icon: dropdownIcon(GitBranchOutline) }, { label: '汇报详情', key: 'reportDetailView', icon: dropdownIcon(EyeSharp) }, @@ -860,57 +814,78 @@ const moreOptions = [ }, ] +/** 列表行主键:按工单号(拆单后同订单多工单分行) */ function orderRowKey(row: OrderProcessPlanVO) { - return row.orderItemId ?? `order-${row.mainCode}-${formatWorkOrderCodes(row)}` + if (row.workOrderCode) return row.workOrderCode + return `oi-${row.orderItemId ?? 'x'}` } -/** 汇总展示订单下各道工序的主工单号 */ +/** 订单级工单号展示 */ function formatWorkOrderCodes(order?: OrderProcessPlanVO | null) { - const codes = (order?.processList ?? []).map((p) => p.workOrderCode).filter(Boolean) - return codes.length ? codes.join('、') : '-' + if (order?.workOrderCode) return order.workOrderCode + const codes = (order?.processList ?? []) + .map((p) => p.workOrderCode) + .filter((c): c is string => !!c) + const unique = [...new Set(codes)] + return unique.length ? unique.join('、') : '-' } function sortedProcessList(list?: ProcessPlanItemVO[]) { - return [...(list ?? [])].sort((a, b) => (a.operNumber ?? 0) - (b.operNumber ?? 0)) + return [...(list ?? [])].sort((a, b) => { + const oa = a.operNumber ?? 0 + const ob = b.operNumber ?? 0 + if (oa !== ob) return oa - ob + return (a.planId ?? 0) - (b.planId ?? 0) + }) } function isExpanded(order: OrderProcessPlanVO) { - return order.orderItemId != null && expandedIds.value.has(order.orderItemId) -} - -function isDerivedExpanded(order: OrderProcessPlanVO) { - return order.orderItemId != null && derivedExpandedIds.value.has(order.orderItemId) + return expandedIds.value.has(orderRowKey(order)) } function toggleExpand(order: OrderProcessPlanVO) { - if (order.orderItemId == null) return + const key = orderRowKey(order) const next = new Set(expandedIds.value) - if (next.has(order.orderItemId)) next.delete(order.orderItemId) - else next.add(order.orderItemId) + if (next.has(key)) next.delete(key) + else next.add(key) expandedIds.value = next } -function toggleDerivedExpand(order: OrderProcessPlanVO) { - if (order.orderItemId == null) return - const next = new Set(derivedExpandedIds.value) - if (next.has(order.orderItemId)) next.delete(order.orderItemId) - else next.add(order.orderItemId) - derivedExpandedIds.value = next +/** 整单拆分入口:有计划数量且未全部完工即可点开 */ +function canSplitWorkOrder(order: OrderProcessPlanVO) { + const qty = order.quantity ?? 0 + if (qty <= 0) return false + if (order.orderStatus === 2) return false + return (order.processList?.length ?? 0) > 0 } -/** 派生工单(返工 / 拆分),待后端字段接入后替换占位逻辑 */ -function getDerivedOrders(order: OrderProcessPlanVO) { - const raw = order as OrderProcessPlanVO & { - reworkOrders?: Array<{ id: string | number; code?: string; quantity?: number; statusLabel?: string }> - splitOrders?: Array<{ id: string | number; code?: string; quantity?: number; statusLabel?: string }> +function openSplitFromOrder(order: OrderProcessPlanVO) { + if (!hasPermission('biz:orderProcessPlan:edit')) { + message.warning('无拆单权限') + return } - return { - rework: raw.reworkOrders ?? [], - split: raw.splitOrders ?? [], + const list = sortedProcessList(order.processList) + const seed = list.find((p) => p.planId != null) + if (!seed?.planId) { + message.warning('当前工单暂无工序,无法拆单') + return + } + if (!canSplitWorkOrder(order)) { + message.warning('该工单不可拆(无数量或已完工)') + return + } + splitPlanId.value = seed.planId + splitVisible.value = true +} + +function onSplitSuccess() { + loadData() + if (detailVisible.value && detailData.value?.orderItemId) { + openDetail(detailData.value) } } -function toggleSelect(id: number, checked: boolean) { +function toggleSelect(id: string, checked: boolean) { if (checked) selectedOrderIds.value = [...selectedOrderIds.value, id] else selectedOrderIds.value = selectedOrderIds.value.filter(v => v !== id) } @@ -982,7 +957,7 @@ function handlePageSizeChange(pageSize: number) { function onBatchSelect(key: string) { if (key === 'expandAll') { - expandedIds.value = new Set(tableData.value.map(o => o.orderItemId!).filter(Boolean)) + expandedIds.value = new Set(tableData.value.map(o => orderRowKey(o))) } else if (key === 'collapseAll') { expandedIds.value = new Set() } @@ -997,11 +972,13 @@ function onMoreSelect(key: string, order: OrderProcessPlanVO) { openReportDetail(order) return; } + if (key === 'splitWorkOrder') { + openSplitFromOrder(order) + return + } const labels: Record = { print: '打印', - createRework: '创建返工单', - splitWorkOrder: '拆分工单', withdraw: '撤回', freeze: '冻结工单', delete: '删除', @@ -1117,7 +1094,7 @@ async function openDetail(order: OrderProcessPlanVO) { detailLoading.value = true detailData.value = null try { - detailData.value = await orderProcessPlanApi.detail(order.orderItemId) + detailData.value = await orderProcessPlanApi.detail(order.orderItemId, order.workOrderCode) } catch { detailData.value = order } finally { @@ -1137,12 +1114,12 @@ async function openReorder(order: OrderProcessPlanVO) { reorderOrderMeta.value = order reorderList.value = [] try { - const detail = await orderProcessPlanApi.detail(order.orderItemId) + const detail = await orderProcessPlanApi.detail(order.orderItemId, order.workOrderCode) reorderOrderMeta.value = detail - // 与看板展开区一致:按 operNumber(后端 sort)升序 + // 仅当前工单工序,按 operNumber 升序 reorderList.value = sortedProcessList(detail.processList).map((p) => ({ ...p })) if (!reorderList.value.length) { - message.warning('当前订单暂无工序计划') + message.warning('当前工单暂无工序计划') } } catch { // 详情失败时降级用列表行数据,仍可本地排序后提交 @@ -1393,9 +1370,8 @@ onMounted(loadData) .board-table-head, .order-row { display: grid; - /* 勾选 | 展开 | 产品工单 | 数量 | 模式 | 齐套率 | 工序轴 | 计划时间 | 生命周期 | 操作 */ + /* 勾选 | 产品工单 | 数量 | 模式 | 齐套率 | 工序轴 | 计划时间 | 生命周期 | 操作 */ grid-template-columns: - 40px 40px minmax(220px, 1.6fr) 64px @@ -1408,7 +1384,7 @@ onMounted(loadData) align-items: center; gap: 8px; padding: 0 16px; - min-width: 1320px; + min-width: 1280px; } .board-table-head { @@ -1438,16 +1414,7 @@ onMounted(loadData) background: rgba(32, 128, 240, 0.04); } -.order-row.derived-expanded { - background: rgba(24, 160, 88, 0.04); -} - -.order-row.expanded.derived-expanded { - background: linear-gradient(90deg, rgba(24, 160, 88, 0.04) 0%, rgba(32, 128, 240, 0.04) 100%); -} - -.col-check, -.col-expand { +.col-check { display: flex; justify-content: center; flex-shrink: 0; @@ -1530,74 +1497,11 @@ onMounted(loadData) } .order-expand { - padding: 12px 16px 16px 68px; + padding: 12px 16px 16px 56px; background: linear-gradient(180deg, rgba(32, 128, 240, 0.03) 0%, #fff 100%); border-top: 1px dashed #e8eaed; } -.order-derived-expand { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 12px; - padding: 12px 16px 12px 68px; - background: linear-gradient(180deg, rgba(24, 160, 88, 0.03) 0%, #fff 100%); - border-top: 1px dashed #e8eaed; -} - -.derived-section { - min-width: 0; - padding: 10px 12px; - background: #fff; - border: 1px solid #eef0f3; - border-radius: 8px; -} - -.derived-section-head { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 8px; -} - -.derived-section-title { - font-size: 13px; - font-weight: 600; - color: #1a1a1a; -} - -.derived-empty { - font-size: 12px; - color: #8c8c8c; - padding: 4px 0; -} - -.derived-list { - display: flex; - flex-direction: column; - gap: 6px; -} - -.derived-item { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 6px 8px; - background: #fafbfc; - border-radius: 6px; - font-size: 12px; -} - -.derived-code { - font-weight: 500; - color: #333; -} - -.derived-meta { - color: #8c8c8c; - white-space: nowrap; -} - .card-flow-scroll, .detail-cards { display: flex; diff --git a/src/views/biz/orderProcessPlan/components/ProcessCard.vue b/src/views/biz/orderProcessPlan/components/ProcessCard.vue index 2ce3555..8fe8511 100644 --- a/src/views/biz/orderProcessPlan/components/ProcessCard.vue +++ b/src/views/biz/orderProcessPlan/components/ProcessCard.vue @@ -79,6 +79,13 @@ ghost @click="emit('assign', process)" >派工 + 拆单 编辑 模型 - 返工 () @@ -142,6 +149,7 @@ const props = defineProps<{ const emit = defineEmits<{ edit: [process: ProcessPlanItemVO] assign: [process: ProcessPlanItemVO] + split: [process: ProcessPlanItemVO] viewModel: [process: ProcessPlanItemVO] outsource: [process: ProcessPlanItemVO] submitDetail: [process: ProcessPlanItemVO] diff --git a/src/views/biz/orderProcessPlan/components/ProcessFlowStepper.vue b/src/views/biz/orderProcessPlan/components/ProcessFlowStepper.vue index 585f1b4..b890fae 100644 --- a/src/views/biz/orderProcessPlan/components/ProcessFlowStepper.vue +++ b/src/views/biz/orderProcessPlan/components/ProcessFlowStepper.vue @@ -39,7 +39,12 @@ const emit = defineEmits<{ }>() const steps = computed(() => { - const list = [...(props.list ?? [])].sort((a, b) => (a.operNumber ?? 0) - (b.operNumber ?? 0)) + const list = [...(props.list ?? [])].sort((a, b) => { + const oa = a.operNumber ?? 0 + const ob = b.operNumber ?? 0 + if (oa !== ob) return oa - ob + return (a.planId ?? 0) - (b.planId ?? 0) + }) return list.map((item, idx) => { const label = item.processName || item.processCode || `工序${idx + 1}` const state = item.processStatus === 4 ? 'done' : item.processStatus != null && item.processStatus >= 2 ? 'active' : 'pending' diff --git a/src/views/biz/orderProcessPlan/components/SplitProcessDialog.vue b/src/views/biz/orderProcessPlan/components/SplitProcessDialog.vue new file mode 100644 index 0000000..bc3f73c --- /dev/null +++ b/src/views/biz/orderProcessPlan/components/SplitProcessDialog.vue @@ -0,0 +1,929 @@ + + + + + diff --git a/src/views/biz/orderProcessPlan/index.vue b/src/views/biz/orderProcessPlan/index.vue index fde0fba..ce16d69 100644 --- a/src/views/biz/orderProcessPlan/index.vue +++ b/src/views/biz/orderProcessPlan/index.vue @@ -206,6 +206,13 @@ + + + userStore.hasPermission(permission const message = useMessage() const dialog = useDialog() +const splitVisible = ref(false) +const splitPlanId = ref(null) + +/** 列表拆单入口:是否可拆以后端预览为准,这里只做粗过滤 */ +function canSplitProcess(proc: ProcessPlanItemVO) { + const qty = proc.quantity ?? 0 + return qty > 0 +} + +function openSplit(process: ProcessPlanItemVO) { + if (!process.planId) { + message.warning('工序计划无效') + return + } + // 后端按工单号拆整单:任意工序 id 均可定位,拆出工单带全部工序 + splitPlanId.value = process.planId + splitVisible.value = true +} + // 搜索表单 const searchForm = reactive({ id: '', @@ -450,6 +477,7 @@ const orderScrollX = 2090 const processScrollX = 2788 function orderRowKey(row: OrderProcessPlanVO) { + if (row.workOrderCode) return row.workOrderCode return row.orderItemId ?? `order-${row.mainCode}-${row.orderCode}` } @@ -490,7 +518,7 @@ function buildProcessActionColumn(order: OrderProcessPlanVO): DataTableColumns

disphand(row, order), }, { default: () => '派工' })) } + if (hasPermission('biz:orderProcessPlan:edit') && canSplitProcess(row)) { + buttons.push(h(NButton, { + size: 'small', + type: 'info', + ghost: true, + onClick: () => openSplit(row), + }, { default: () => '拆单' })) + } return buttons.length > 0 ? h(NSpace, { justify: 'center' }, { default: () => buttons }) : '-' }, } @@ -602,8 +638,12 @@ const orderColumns: DataTableColumns = [ align: 'center', ellipsis: { tooltip: true }, render: (row) => { - const codes = (row.processList ?? []).map((p) => p.workOrderCode).filter(Boolean) - return codes.length ? codes.join('、') : '-' + if (row.workOrderCode) return row.workOrderCode + const codes = (row.processList ?? []) + .map((p) => p.workOrderCode) + .filter((c): c is string => !!c) + const unique = [...new Set(codes)] + return unique.length ? unique.join('、') : '-' }, }, { title: '数量', key: 'quantity', width: 72, align: 'center' },