From 01abeb122cf6b7ebc1c644b0d675d8e9f7de4861 Mon Sep 17 00:00:00 2001 From: wzt <3847407356@qq.com> Date: Wed, 5 Aug 2026 09:16:54 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=BB=B4=E4=BF=AE=E4=BF=9D=E5=85=BB=E8=AE=B0=E5=BD=95404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/api/maintenanceRecord.ts | 8 ++-- src/views/biz/maintenanceRecord/index.vue | 28 +++--------- src/views/biz/maintenanceRecord/simple.vue | 52 ++++++++++++++++++++++ 4 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 src/views/biz/maintenanceRecord/simple.vue diff --git a/.gitignore b/.gitignore index ef30b45..478ee37 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ AGENTS.md *.njsproj *.sln *.sw? +/src/views/biz/tool/usage.vue diff --git a/src/api/maintenanceRecord.ts b/src/api/maintenanceRecord.ts index 3218c32..bf2fb57 100644 --- a/src/api/maintenanceRecord.ts +++ b/src/api/maintenanceRecord.ts @@ -14,15 +14,15 @@ export interface MaintenanceRecord { executorId?: number - startTime?: string + startTime?: string | number - endTime?: string + endTime?: string | number recordStatus?: string - createTime?: string + createTime?: string | number - updateTime?: string + updateTime?: string | number } diff --git a/src/views/biz/maintenanceRecord/index.vue b/src/views/biz/maintenanceRecord/index.vue index 75f3095..1238dc2 100644 --- a/src/views/biz/maintenanceRecord/index.vue +++ b/src/views/biz/maintenanceRecord/index.vue @@ -50,7 +50,7 @@ :data="tableData" :loading="loading" :pagination="pagination" - :row-key="(row) => row.recordId" + :row-key="(row: MaintenanceRecord) => row.recordId" :scroll-x="1200" @update:page="handlePageChange" @update:page-size="handlePageSizeChange" @@ -60,7 +60,7 @@ - + @@ -145,7 +145,7 @@ const searchForm = reactive({ // 表格数据 const tableData = ref([]) const loading = ref(false) -const selectedIds = ref([]) +const selectedIds = ref([]) const pagination = reactive({ page: 1, pageSize: 10, @@ -229,8 +229,8 @@ async function loadData() { pageSize: pagination.pageSize, recordId: searchForm.recordId || undefined, }) - tableData.value = res.list - pagination.itemCount = res.total + tableData.value = res.records || res.list || [] + pagination.itemCount = res.total || 0 } finally { loading.value = false } @@ -263,7 +263,7 @@ function handlePageSizeChange(pageSize: number) { // 选择 function handleCheck(keys: Array) { - selectedIds.value = keys as number[] + selectedIds.value = keys.map(String) } // 新增 @@ -438,20 +438,4 @@ onMounted(() => { .table-toolbar { margin-bottom: 16px; } - - \ No newline at end of file diff --git a/src/views/biz/maintenanceRecord/simple.vue b/src/views/biz/maintenanceRecord/simple.vue new file mode 100644 index 0000000..a07c7ca --- /dev/null +++ b/src/views/biz/maintenanceRecord/simple.vue @@ -0,0 +1,52 @@ + + + From 2a982f12d0ac369dea3557bb1d28d6e8e3afcbe3 Mon Sep 17 00:00:00 2001 From: wzt <3847407356@qq.com> Date: Wed, 5 Aug 2026 09:31:46 +0800 Subject: [PATCH 2/9] 1 --- src/router/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/router/index.ts b/src/router/index.ts index 8143e1e..71163c7 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -201,6 +201,13 @@ const routes: RouteRecordRaw[] = [ component: () => import('@/views/test/test/index.vue'), meta: { title: '测试菜单', icon: 'StarOutline' } }, + // 设备维修保养记录 + { + path: 'biz/maintenanceRecord', + name: 'MaintenanceRecord', + component: () => import('@/views/biz/maintenanceRecord/index.vue'), + meta: { title: '设备维修保养记录', icon: 'BuildOutline' } + }, // 开发工具 { path: 'tool/gen', From b6ede42e5d3ff816c1d9f80467dbd18b66c38fc5 Mon Sep 17 00:00:00 2001 From: wzt <3847407356@qq.com> Date: Wed, 5 Aug 2026 09:50:48 +0800 Subject: [PATCH 3/9] 1 --- src/views/biz/maintenanceRecord/index.vue | 81 ++++++++++++----------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/views/biz/maintenanceRecord/index.vue b/src/views/biz/maintenanceRecord/index.vue index 1238dc2..c1fa9c8 100644 --- a/src/views/biz/maintenanceRecord/index.vue +++ b/src/views/biz/maintenanceRecord/index.vue @@ -59,31 +59,31 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + \ No newline at end of file From f22754edbf564f00a08d6a429a09944d3eb89d4c Mon Sep 17 00:00:00 2001 From: tzy <1042411602@qq.com> Date: Fri, 7 Aug 2026 14:18:18 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E5=B7=A5=E5=BA=8F=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E6=8B=86=E5=88=86=E9=A2=84=E8=A7=88/=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E5=96=84=E5=B7=A5=E8=89=BA=E8=B7=AF=E7=BA=BF?= =?UTF-8?q?=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/basicProcessPlan.ts | 8 + src/api/device.ts | 36 + src/api/orderProcessPlan.ts | 91 +- src/api/processRoute.ts | 40 +- src/router/index.ts | 7 + src/views/biz/device/board.vue | 1104 +++++++++++++---- src/views/biz/orderProcessPlan/board.vue | 260 ++-- .../components/ProcessCard.vue | 10 +- .../components/ProcessFlowStepper.vue | 7 +- .../components/SplitProcessDialog.vue | 929 ++++++++++++++ src/views/biz/orderProcessPlan/index.vue | 46 +- 11 files changed, 2113 insertions(+), 425 deletions(-) create mode 100644 src/views/biz/orderProcessPlan/components/SplitProcessDialog.vue 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' }, From d95678841ce415e90fb28288562aa716a214446b Mon Sep 17 00:00:00 2001 From: andy <1042025947@qq.com> Date: Fri, 7 Aug 2026 14:54:04 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9D=83=E9=99=90?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/permissionConfig.ts | 72 ++++ src/router/index.ts | 8 +- src/views/biz/permissionConfig/index.vue | 452 +++++++++++++++++++++++ 3 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 src/api/permissionConfig.ts create mode 100644 src/views/biz/permissionConfig/index.vue diff --git a/src/api/permissionConfig.ts b/src/api/permissionConfig.ts new file mode 100644 index 0000000..c38cc5a --- /dev/null +++ b/src/api/permissionConfig.ts @@ -0,0 +1,72 @@ +import { request } from '@/utils/request' + +// 数据权限配置 类型定义 +export interface PermissionConfig { + id?: number + + mapperKey?: string + + controlMethod?: string + + permissionControl?: string + +} + +// 数据权限配置 API +export const permissionConfigApi = { + // 分页查询 + page(params: { page: number; pageSize: number; mapperKey?: string; controlMethod?: string }) { + return request({ url: '/biz/permissionConfig/page', method: 'get', params }) + }, + + // 新增 + options() { + return request({ url: '/biz/permissionConfig/options', method: 'get' }) + }, + + // 获取详情 + detail(id: string) { + return request({ url: `/biz/permissionConfig/${id}`, method: 'get' }) + }, + + // 新增 + create(data: PermissionConfig) { + return request({ url: '/biz/permissionConfig', method: 'post', data }) + }, + + // 修改 + update(data: PermissionConfig) { + return request({ url: '/biz/permissionConfig', method: 'put', data }) + }, + + // 删除 + delete(ids: string[]) { + return request({ url: `/biz/permissionConfig/${ids.join(',')}`, method: 'delete' }) + }, + + // 导出 + export(params?: { ids?: string[]; mapperKey?: string; controlMethod?: string }) { + const p: Record = {} + if (params?.ids?.length) p.ids = params.ids.join(',') + if (params?.mapperKey !== undefined && params?.mapperKey !== null) p.mapperKey = params.mapperKey + if (params?.controlMethod !== undefined && params?.controlMethod !== null) p.controlMethod = params.controlMethod + return request({ url: `/biz/permissionConfig/export`, method: 'get', params: p, responseType: 'blob' }) + }, + + // 导入 + importData(file: File) { + const formData = new FormData() + formData.append('file', file) + return request<{ success: number; fail: number; errors: string[] }>({ + url: `/biz/permissionConfig/import`, + method: 'post', + data: formData, + headers: { 'Content-Type': 'multipart/form-data' } + }) + }, + + // 下载导入模板 + downloadTemplate() { + return request({ url: `/biz/permissionConfig/template`, method: 'get', responseType: 'blob' }) + } +} diff --git a/src/router/index.ts b/src/router/index.ts index da9b3f5..8347676 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -216,6 +216,12 @@ const routes: RouteRecordRaw[] = [ meta: { title: '设备状态监控', icon: 'PulseOutline' } }, // 开发工具 + { + path: 'biz/permissionConfig', + name: 'permissionConfig', + component: () => import('@/views/biz/permissionConfig/index.vue'), + meta: { title: '数据权限配置', icon: 'ListOutline' } + }, { path: 'tool/gen', name: 'ToolGen', @@ -404,4 +410,4 @@ router.beforeEach(async (to, _from, next) => { }) -export default router \ No newline at end of file +export default router diff --git a/src/views/biz/permissionConfig/index.vue b/src/views/biz/permissionConfig/index.vue new file mode 100644 index 0000000..44a3874 --- /dev/null +++ b/src/views/biz/permissionConfig/index.vue @@ -0,0 +1,452 @@ + + + + +