From 8c3f7a5aec296e3679147c0d794ce1efbbb94a44 Mon Sep 17 00:00:00 2001 From: hwj Date: Fri, 8 May 2026 16:29:29 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=8E=92=E4=BA=A7-=E7=94=98?= =?UTF-8?q?=E7=89=B9=E5=9B=BE=E6=B7=BB=E5=8A=A0=E9=94=81=E5=AE=9A=E3=80=81?= =?UTF-8?q?=E6=92=A4=E5=9B=9E=E6=93=8D=E4=BD=9C=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.ts | 13 ++++- src/locales/zh-CN.ts | 13 ++++- .../components/ScheduleGanttPanelEditable.vue | 53 +++++++++++++++++-- .../components/TaskSchedulePreviewDialog.vue | 50 +++++++++++++---- 4 files changed, 112 insertions(+), 17 deletions(-) diff --git a/src/locales/en.ts b/src/locales/en.ts index b8602984..cd6f9386 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -4623,7 +4623,18 @@ export default { statusStarted: 'Started', statusPaused: 'Paused', statusPendingStorage: 'Pending Storage', - statusStored: 'Stored' + statusStored: 'Stored', + dialogTitle: 'Schedule Gantt Preview', + workerLabel: 'Worker', + workerPlaceholder: 'Please select worker', + calcLossLabel: 'Calculate Loss', + lockBtn: 'Lock', + unlockBtn: 'Unlock', + undoBtn: 'Undo', + buttonSave: 'Save', + buttonClose: 'Close', + warningNoPlanData: 'No plan data to save', + saveSuccess: 'Schedule saved successfully' } } } diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 4a57a7a8..32a6129d 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -4834,7 +4834,18 @@ export default { statusStarted: '已开工', statusPaused: '暂停', statusPendingStorage: '待入库', - statusStored: '已入库' + statusStored: '已入库', + dialogTitle: '排产甘特图预览', + workerLabel: '领料人', + workerPlaceholder: '请选择领料人', + calcLossLabel: '是否计算损耗', + lockBtn: '锁定', + unlockBtn: '解锁', + undoBtn: '撤回', + buttonSave: '保存', + buttonClose: '关闭', + warningNoPlanData: '暂无可保存的计划数据', + saveSuccess: '排产计划保存成功' } } } diff --git a/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue b/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue index 1ca54b2c..97d388cd 100644 --- a/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue +++ b/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue @@ -90,9 +90,11 @@ const props = withDefaults( defineProps<{ scheduleList: any[] height?: string + editable?: boolean }>(), { - height: '800px' + height: '800px', + editable: false } ) @@ -109,6 +111,18 @@ const taskAdjustForm = reactive({ endDate: '' }) const editingPlanIdentity = ref(null) +const undoStack = ref([]) + +const saveUndoSnapshot = () => { + const data = previewScheduleList.value.map((device: any) => ({ + deviceId: device.deviceId, + deviceName: device.deviceName, + capacityType: device.capacityType, + ratedCapacity: device.ratedCapacity, + plans: (device.plans ?? []).map((plan: any) => ({ ...plan })) + })) + undoStack.value.push(JSON.stringify(data)) +} const getCapacityTypeLabel = (value: any) => { if (value === undefined || value === null) return '-' @@ -288,7 +302,7 @@ const renderCustomPlanBars = () => { height: ${barHeight}px; border-radius: 6px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); - cursor: ${isCurrent ? 'grab' : 'pointer'}; + cursor: ${isCurrent && props.editable ? 'grab' : 'pointer'}; z-index: 2; display: flex; align-items: center; @@ -352,12 +366,12 @@ const renderCustomPlanBars = () => { }) bar.addEventListener('contextmenu', (e) => { - if (!isCurrent) { e.preventDefault(); return } + if (!isCurrent || !props.editable) { e.preventDefault(); return } e.preventDefault() openTaskAdjustDialogForPlan(plan, task._deviceData) }) - if (isCurrent) { + if (isCurrent && props.editable) { attachPlanBarDrag(bar, plan, task) attachPlanBarResize(bar, plan, task) } @@ -420,6 +434,8 @@ const attachPlanBarDrag = (bar: HTMLElement, plan: any, task: any) => { if (!moved) return moved = false + saveUndoSnapshot() + const dx = e.clientX - startX const newLeft = Math.max(0, startLeft + dx) const newStartDate = gantt.dateFromPos(newLeft) @@ -524,6 +540,8 @@ const attachPlanBarResize = (bar: HTMLElement, plan: any, task: any) => { resizing = false bar.style.zIndex = '2' + saveUndoSnapshot() + const newLeft = parseFloat(bar.style.left) || startLeft const newWidth = parseFloat(bar.style.width) || startWidth const newStartDate = gantt.dateFromPos(newLeft) @@ -562,6 +580,8 @@ const handleTaskAdjustSubmit = () => { } if (!editingPlanIdentity.value) return + saveUndoSnapshot() + const targetDevice = previewScheduleList.value.find( (d: any) => `device-${d.deviceId}` === taskAdjustForm.deviceTaskId ) @@ -719,6 +739,7 @@ const initGanttPreview = () => { } onMounted(async () => { + undoStack.value = [] await nextTick() initGanttPreview() }) @@ -737,6 +758,30 @@ watch( onBeforeUnmount(() => { destroyGantt() }) + +const undo = () => { + if (!undoStack.value.length) return + const saved = JSON.parse(undoStack.value.pop()!) + for (const savedDevice of saved) { + const originalDevice = previewScheduleList.value.find( + (d: any) => d.deviceId === savedDevice.deviceId + ) + if (originalDevice) { + originalDevice.plans.splice(0, originalDevice.plans.length, ...savedDevice.plans) + } + } + initGanttPreview() +} + +watch( + () => props.editable, + async () => { + await nextTick() + renderCustomPlanBars() + } +) + +defineExpose({ undo })