From 0ff6eaef69bd055157100e57d79c8eb92d7ee06b Mon Sep 17 00:00:00 2001 From: hwj Date: Fri, 8 May 2026 15:21:57 +0800 Subject: [PATCH 1/5] =?UTF-8?q?style=EF=BC=9A=E7=94=98=E7=89=B9=E5=9B=BE-?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E6=98=8E=E7=BB=86=E5=AD=97=E6=AE=B5=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/components/ScheduleGanttPanel.vue | 2 +- .../mes/tasksummary/components/ScheduleGanttPanelEditable.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/mes/components/ScheduleGanttPanel.vue b/src/views/mes/components/ScheduleGanttPanel.vue index 3a6e624e..1907bb81 100644 --- a/src/views/mes/components/ScheduleGanttPanel.vue +++ b/src/views/mes/components/ScheduleGanttPanel.vue @@ -37,7 +37,7 @@ {{ PLAN_STATUS_COLOR_MAP[plan.planStatus].label }} -
{{ t('GanttChart.GanttPanel.planCodeColon') }}{{ plan.taskCode ?? '-' }}
+
{{ t('GanttChart.GanttPanel.planCodeColon') }}{{ plan.planCode ?? '-' }}
{{ t('GanttChart.GanttPanel.planNumberColon') }}{{ plan.planNumber ?? '-' }}
{{ t('GanttChart.GanttPanel.deliveryDateColon') }}{{ plan.deliveryDateStr ?? '-' }}
{{ t('GanttChart.GanttPanel.startColon') }}{{ plan.planStartTimeStr || '-' }}
diff --git a/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue b/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue index d485110f..19f3d7b1 100644 --- a/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue +++ b/src/views/mes/tasksummary/components/ScheduleGanttPanelEditable.vue @@ -25,7 +25,7 @@
{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }}
-
计划编码:{{ plan.taskCode ?? '-' }}
+
任务编码:{{ plan.taskCode ?? '-' }}
计划数量:{{ plan.planNumber ?? '-' }}
@@ -81,9 +80,12 @@ import dayjs from 'dayjs' import { gantt } from 'dhtmlx-gantt' import 'dhtmlx-gantt/codebase/dhtmlxgantt.css' import { getDictOptions } from '@/utils/dict' +import { useI18n } from '@/hooks/web/useI18n' defineOptions({ name: 'ScheduleGanttPanelEditable' }) +const { t } = useI18n() + const props = withDefaults( defineProps<{ scheduleList: any[] @@ -208,14 +210,14 @@ const clearCustomPlanBars = () => { const buildPlanBarTooltipHtml = (plan: any, device: any) => { return ` -
任务明细
-
设备:${device?.deviceName ?? '-'}
-
任务单:${plan.taskCode ?? '-'}
-
产品:${plan.productCode ?? '-'} / ${plan.productName ?? '-'}
-
计划数量:${plan.planNumber ?? '-'}
-
开始:${formatTooltipDateTime(plan.planStartTimeStr)}
-
结束:${formatTooltipDateTime(plan.planEndTimeStr)}
-
最晚开工:${formatTooltipDateTime(plan.latestStartTimeStr)}
+
${t('GanttChart.GanttPanel.tooltipTaskDetail')}
+
${t('GanttChart.GanttPanel.tooltipDevice')}${device?.deviceName ?? '-'}
+
${t('GanttChart.GanttPanel.tooltipTaskCode')}${plan.taskCode ?? '-'}
+
${t('GanttChart.GanttPanel.tooltipProduct')}${plan.productCode ?? '-'} / ${plan.productName ?? '-'}
+
${t('GanttChart.GanttPanel.tooltipPlanNumber')}${plan.planNumber ?? '-'}
+
${t('GanttChart.GanttPanel.tooltipStart')}${formatTooltipDateTime(plan.planStartTimeStr)}
+
${t('GanttChart.GanttPanel.tooltipEnd')}${formatTooltipDateTime(plan.planEndTimeStr)}
+
${t('GanttChart.GanttPanel.tooltipLatestStart')}${formatTooltipDateTime(plan.latestStartTimeStr)}
` } @@ -555,7 +557,7 @@ const openTaskAdjustDialogForPlan = (plan: any, device: any) => { const handleTaskAdjustSubmit = () => { if (!taskAdjustForm.deviceTaskId || !taskAdjustForm.startDate || !taskAdjustForm.endDate) { - message.warning('请完善设备、计划开始日期和计划结束日期') + message.warning(t('GanttChart.GanttPanel.warningCompleteDeviceDate')) return } if (!editingPlanIdentity.value) return @@ -582,11 +584,11 @@ const handleTaskAdjustSubmit = () => { const newStart = dayjs(taskAdjustForm.startDate) const newEnd = dayjs(taskAdjustForm.endDate) if (!newStart.isValid() || !newEnd.isValid()) { - message.warning('请选择有效的时间') + message.warning(t('GanttChart.GanttPanel.warningValidTime')) return } if (newEnd.isBefore(newStart)) { - message.warning('结束时间不能早于开始时间') + message.warning(t('GanttChart.GanttPanel.warningEndBeforeStart')) return } @@ -639,7 +641,7 @@ const initGanttPreview = () => { gantt.config.columns = [ { name: 'text', - label: '设备名称', + label: t('GanttChart.GanttPanel.columnDeviceName'), tree: false, width: '*', min_width: 100, @@ -647,7 +649,7 @@ const initGanttPreview = () => { }, { name: 'duration', - label: '天数', + label: t('GanttChart.GanttPanel.columnDays'), align: 'center', width: 80, template: (task: any) => String(task.totalDays ?? task.duration ?? 0) @@ -655,7 +657,7 @@ const initGanttPreview = () => { ] gantt.config.scales = [ - { unit: 'month', step: 1, format: (date) => dayjs(date).format('YYYY年M月') }, + { unit: 'month', step: 1, format: (date) => dayjs(date).format(t('GanttChart.GanttPanel.scaleMonthFormat')) }, { unit: 'day', step: 1, format: (date) => dayjs(date).format('MM-DD') } ] From 8c3f7a5aec296e3679147c0d794ce1efbbb94a44 Mon Sep 17 00:00:00 2001 From: hwj Date: Fri, 8 May 2026 16:29:29 +0800 Subject: [PATCH 3/5] =?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 })