From 40a68e3c1f70a23fa376cd46c9149127eb3498a5 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 15 Apr 2026 09:27:13 +0800 Subject: [PATCH 1/9] =?UTF-8?q?style=EF=BC=9A=E7=94=98=E7=89=B9=E5=9B=BEto?= =?UTF-8?q?oltip=E9=9A=90=E8=97=8F=E9=80=BB=E8=BE=91=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mes/components/ScheduleGanttPanel.vue | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/views/mes/components/ScheduleGanttPanel.vue b/src/views/mes/components/ScheduleGanttPanel.vue index 79cc0e64..bb19e14c 100644 --- a/src/views/mes/components/ScheduleGanttPanel.vue +++ b/src/views/mes/components/ScheduleGanttPanel.vue @@ -209,7 +209,26 @@ const buildPreviewGanttData = (scheduleList: any[]) => { return { data: tasks, links } } +const cleanupTaskTooltips = () => { + const tooltipsExt = (gantt.ext as any)?.tooltips + const tooltip = tooltipsExt?.tooltip + if (tooltipsExt?.detach) { + tooltipsExt.detach('.gantt_task_line') + tooltipsExt.detach('.gantt_task_content') + } + if (tooltip?.hide) { + tooltip.hide() + } + const node = tooltip?.getNode?.() + if (node) { + node.style.display = 'none' + node.style.visibility = 'hidden' + node.style.opacity = '0' + } +} + const destroyGantt = () => { + cleanupTaskTooltips() ganttEventIds.value.forEach((eventId) => gantt.detachEvent(eventId)) ganttEventIds.value = [] gantt.clearAll() From 4183ccda7c8f3e3eb5e6b8f943933b1dea190865 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 15 Apr 2026 09:40:21 +0800 Subject: [PATCH 2/9] =?UTF-8?q?style=EF=BC=9A=E4=BB=BB=E5=8A=A1=E5=8D=95?= =?UTF-8?q?=E6=8E=92=E4=BA=A7-=E6=96=B0=E5=A2=9E=E8=AE=A1=E5=88=92-?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E5=80=BC=E6=B7=BB=E5=8A=A0=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=EF=BC=88=E4=B8=8D=E8=83=BD=E5=A4=A7=E4=BA=8E=E6=9C=AA=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E6=95=B0=E9=87=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/plan/PlanForm.vue | 42 ++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/views/mes/plan/PlanForm.vue b/src/views/mes/plan/PlanForm.vue index 91182959..61433502 100644 --- a/src/views/mes/plan/PlanForm.vue +++ b/src/views/mes/plan/PlanForm.vue @@ -188,6 +188,7 @@ const taskList = ref([]) // 用户列表 const taskDetailList = ref([]) // 用户列表 const deviceSelectDialogRef = ref() const selectedDeviceRows = ref([]) +const maxUnplannedNumber = ref(undefined) const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -306,10 +307,45 @@ const validatePlanEndTime = (_rule: any, value: any, callback: (error?: Error) = } callback() } +const getSelectedTaskDetailUnplanned = () => { + const detail = taskDetailList.value.find( + (item: any) => Number(item.id) === Number(formData.value.taskDetailId) + ) as any + if (!detail) return undefined + const total = Number(detail.number) + const planned = Number(detail.planNumber) + if (!Number.isFinite(total) || !Number.isFinite(planned)) return undefined + return Math.max(total - planned, 0) +} +const getPlanNumberLimit = () => { + const detailUnplanned = getSelectedTaskDetailUnplanned() + if (detailUnplanned !== undefined) return detailUnplanned + return maxUnplannedNumber.value +} +const validatePlanNumber = (_rule: any, value: any, callback: (error?: Error) => void) => { + if (value === undefined || value === null || value === '') { + callback() + return + } + const currentPlanNumber = Number(value) + if (!Number.isFinite(currentPlanNumber)) { + callback(new Error('计划数量必须为数字')) + return + } + const planNumberLimit = getPlanNumberLimit() + if (planNumberLimit !== undefined && currentPlanNumber > planNumberLimit) { + callback(new Error(`计划数量不能大于未计划数量(${planNumberLimit})`)) + return + } + callback() +} const formRules = reactive({ taskDetailId: [{ required: true, message: t('ProductionPlan.Plan.validatorTaskDetailRequired'), trigger: 'blur' }], taskId: [{ required: true, message: t('ProductionPlan.Plan.validatorTaskRequired'), trigger: 'blur' }], - planNumber: [{ required: true, message: t('ProductionPlan.Plan.validatorPlanNumberRequired'), trigger: 'blur' }], + planNumber: [ + { required: true, message: t('ProductionPlan.Plan.validatorPlanNumberRequired'), trigger: 'blur' }, + { validator: validatePlanNumber, trigger: ['blur', 'change'] } + ], // reyaNumber: [{ required: true, message: t('ProductionPlan.Plan.validatorReyaNumberRequired'), trigger: 'blur' }], planStartTime: [ { required: true, message: t('ProductionPlan.Plan.validatorPlanStartRequired'), trigger: 'blur' }, @@ -374,6 +410,7 @@ const open = async (type: string, id?: number, dialogTitle.value = t('action.' + type) formType.value = type resetForm() + maxUnplannedNumber.value = number !== undefined ? Math.max(Number(number), 0) : undefined formData.value.deliveryDate = taskDeliveryDate || undefined if(taskId) { formData.value.taskId = taskId @@ -465,6 +502,7 @@ const resetForm = () => { finishDate: undefined, deliveryDate: undefined } + maxUnplannedNumber.value = undefined selectedDeviceRows.value = [] formRef.value?.resetFields() } @@ -474,10 +512,12 @@ const handleTaskChange = async() => { taskDetailList.value =await TaskApi.getTaskDetailListByTaskId(formData.value.taskId) formData.value.taskDetailId = undefined formData.value.finishDate = undefined + formRef.value?.validateField('planNumber') } /** 明细变化 */ const handleTaskDetailChange = async() => { syncFinishDateByTaskDetail() + formRef.value?.validateField('planNumber') } const handlePlanStartTimeChange = () => { formRef.value?.validateField(['planStartTime', 'planEndTime']) From 159b24755f3e2eb6594bf305108184bde8ba5698 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 15 Apr 2026 10:31:06 +0800 Subject: [PATCH 3/9] =?UTF-8?q?style=EF=BC=9A=E4=BB=BB=E5=8A=A1=E5=88=B6?= =?UTF-8?q?=E5=8D=95-tabs=E9=A1=B9=E6=9B=B4=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.ts | 5 +++++ src/locales/zh-CN.ts | 5 +++++ src/views/mes/task/index.vue | 8 ++++---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/locales/en.ts b/src/locales/en.ts index 1cd31433..7d9cae55 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -3398,6 +3398,10 @@ export default { tabAllLabel: 'All', tabIssuedLabel: 'Issued', + tabPartialSchedulingLabel: 'Partial Scheduling', + tabPendingProductionLabel: 'Pending Production', + tabInProductionLabel: 'In Production', + tabCompletedLabel: 'Completed', tabPlanLabel: 'Planned', tabStartLabel: 'Started', tabFinishedLabel: 'Finished', @@ -3408,6 +3412,7 @@ export default { tableDeliveryDateColumn: 'Delivery Date', tableStatusColumn: 'Status', tableScheduleCompletedColumn: 'Schedule Completed', + tableProductionProgressColumn: 'Production Progress', tableRemarkColumn: 'Remark', tableOperateColumn: 'Operate', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index fed55e5d..ca09da25 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -3244,6 +3244,10 @@ export default { tabAllLabel: '所有', tabIssuedLabel: '下达', + tabPartialSchedulingLabel: '部分排产', + tabPendingProductionLabel: '待生产', + tabInProductionLabel: '生产中', + tabCompletedLabel: '已完成', tabPlanLabel: '计划', tabStartLabel: '开工', tabFinishedLabel: '完工', @@ -3254,6 +3258,7 @@ export default { tableDeliveryDateColumn: '交货日期', tableStatusColumn: '状态', tableScheduleCompletedColumn: '是否完成排产', + tableProductionProgressColumn: '生产进度', tableRemarkColumn: '备注', tableOperateColumn: '操作', diff --git a/src/views/mes/task/index.vue b/src/views/mes/task/index.vue index d15fdc8e..187ba439 100644 --- a/src/views/mes/task/index.vue +++ b/src/views/mes/task/index.vue @@ -94,10 +94,10 @@ - - - - + + + + Date: Wed, 15 Apr 2026 10:34:32 +0800 Subject: [PATCH 4/9] =?UTF-8?q?style=EF=BC=9A=E4=BB=BB=E5=8A=A1=E5=8D=95?= =?UTF-8?q?=E6=8E=92=E4=BA=A7-=E6=B7=BB=E5=8A=A0=E7=94=9F=E4=BA=A7?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/tasksummary/index.vue | 54 ++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/views/mes/tasksummary/index.vue b/src/views/mes/tasksummary/index.vue index fe250da1..59d7ff77 100644 --- a/src/views/mes/tasksummary/index.vue +++ b/src/views/mes/tasksummary/index.vue @@ -58,10 +58,10 @@ v-model="queryParams.createTime" @change="handleQuery" value-format="YYYY-MM-DD - - - - + + + + {{ scope.row.isScheduled ? '是' : '否' }} + + + @@ -162,6 +175,19 @@ const deliveryDateFormatter = (_row: any, _column: any, value: any) => { if (value) return dateFormatter2(_row, _column, value) return dateFormatter2(_row, _column, _row?.finishDate) } +const getProductionProgressPercent = (row: any) => { + const storedPlanNumber = Number(row?.storedPlanNumber ?? 0) + const totalNumber = Number(row?.totalNumber ?? row?.number ?? 0) + if (!Number.isFinite(storedPlanNumber) || !Number.isFinite(totalNumber) || totalNumber <= 0) return 0 + const rawPercent = (storedPlanNumber / totalNumber) * 100 + return Math.max(0, Math.min(100, Number(rawPercent.toFixed(2)))) +} +const getProductionProgressText = (row: any) => { + const storedPlanNumber = Number(row?.storedPlanNumber ?? 0) + const totalNumber = Number(row?.totalNumber ?? row?.number ?? 0) + if (!Number.isFinite(storedPlanNumber) || !Number.isFinite(totalNumber) || totalNumber <= 0) return '0/0' + return `${storedPlanNumber}/${totalNumber}` +} /** 查询列表 */ const getList = async () => { loading.value = true @@ -241,3 +267,23 @@ const handleTabClick = (tab: TabsPaneContext) => { handleQuery() } + + From b452e320ef1f81d4f6b477255930ad8505d29d7b Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 15 Apr 2026 10:58:06 +0800 Subject: [PATCH 5/9] =?UTF-8?q?style=EF=BC=9A=E4=BB=BB=E5=8A=A1=E5=88=B6?= =?UTF-8?q?=E5=8D=95-=E6=96=B0=E5=A2=9E-=E4=BA=A4=E8=B4=A7=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E4=B8=8D=E8=83=BD=E9=80=89=E5=BE=80=E6=97=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/task/TaskForm.vue | 22 ++++++++++++++++++- .../mes/task/components/TaskDetailForm.vue | 20 ++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/views/mes/task/TaskForm.vue b/src/views/mes/task/TaskForm.vue index 8eafd6e8..d4b78df9 100644 --- a/src/views/mes/task/TaskForm.vue +++ b/src/views/mes/task/TaskForm.vue @@ -39,6 +39,7 @@ v-model="formData.deliveryDate" type="date" value-format="x" + :disabled-date="disablePastDate" :placeholder="t('ProductionPlan.Task.dialogDeliveryDatePlaceholder')" class="!w-full" /> @@ -107,8 +108,27 @@ const formData = ref({ isEnable: undefined, isCode: undefined }) +const disablePastDate = (date: Date) => { + return date.getTime() < new Date(new Date().setHours(0, 0, 0, 0)).getTime() +} +const validateDeliveryDate = (_rule: any, value: any, callback: (error?: Error) => void) => { + if (!value) { + callback() + return + } + const selected = Number(value) + const todayStart = new Date(new Date().setHours(0, 0, 0, 0)).getTime() + if (Number.isFinite(selected) && selected < todayStart) { + callback(new Error('交货日期不能早于今天')) + return + } + callback() +} const formRules = reactive({ - deliveryDate: [{ required: true, message: t('ProductionPlan.Task.validatorDeliveryDateRequired'), trigger: 'blur' }], + deliveryDate: [ + { required: true, message: t('ProductionPlan.Task.validatorDeliveryDateRequired'), trigger: 'blur' }, + { validator: validateDeliveryDate, trigger: ['change', 'blur'] } + ], taskType: [{ required: true, message: t('ProductionPlan.Task.validatorTaskTypeRequired'), trigger: 'blur' }], isUrgent: [{ required: true, message: t('ProductionPlan.Task.validatorIsUrgentRequired'), trigger: 'change' }] }) diff --git a/src/views/mes/task/components/TaskDetailForm.vue b/src/views/mes/task/components/TaskDetailForm.vue index 55836d0c..bd65e5ea 100644 --- a/src/views/mes/task/components/TaskDetailForm.vue +++ b/src/views/mes/task/components/TaskDetailForm.vue @@ -89,6 +89,7 @@ v-model="formData.finishDate" type="date" value-format="x" + :disabled-date="disablePastDate" :placeholder="t('ProductionPlan.Task.detailFormDeliveryDatePlaceholder')" /> @@ -158,12 +159,29 @@ const formData = ref({ boxingDate: undefined, arriveDate: undefined, }) +const disablePastDate = (date: Date) => { + return date.getTime() < new Date(new Date().setHours(0, 0, 0, 0)).getTime() +} +const validateFinishDate = (_rule: any, value: any, callback: (error?: Error) => void) => { + if (!value) { + callback() + return + } + const selected = Number(value) + const todayStart = new Date(new Date().setHours(0, 0, 0, 0)).getTime() + if (Number.isFinite(selected) && selected < todayStart) { + callback(new Error('交货日期不能早于今天')) + return + } + callback() +} const formRules = reactive({ productId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailProductIdRequired'), trigger: 'blur' }], unitId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailUnitIdRequired'), trigger: 'blur' }], taskId: [{ required: true, message: t('ProductionPlan.Task.validatorDetailTaskIdRequired'), trigger: 'blur' }], number: [{ required: true, message: t('ProductionPlan.Task.validatorDetailNumberRequired'), trigger: 'blur' }], - packageSize: [{ required: true, message: t('ProductionPlan.Task.validatorDetailPackageSizeRequired'), trigger: 'blur' }] + packageSize: [{ required: true, message: t('ProductionPlan.Task.validatorDetailPackageSizeRequired'), trigger: 'blur' }], + finishDate: [{ validator: validateFinishDate, trigger: ['change', 'blur'] }] }) const formRef = ref() // 表单 Ref From 02ba88a454bf2ff6d6208080462d99bdde09d39e Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 15 Apr 2026 14:09:32 +0800 Subject: [PATCH 6/9] =?UTF-8?q?style=EF=BC=9A=E4=BB=BB=E5=8A=A1=E5=8D=95?= =?UTF-8?q?=E6=8E=92=E4=BA=A7-=E6=96=B0=E5=A2=9E-=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E5=BC=80=E5=A7=8B=E3=80=81=E8=AE=A1=E5=88=92=E7=BB=93=E6=9D=9F?= =?UTF-8?q?=E3=80=81=E6=9C=80=E6=99=9A=E5=BC=80=E5=B7=A5=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=B6=E5=88=86=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/plan/PlanForm.vue | 20 +++----------------- src/views/mes/plan/components/PlanDetail.vue | 4 ++-- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/views/mes/plan/PlanForm.vue b/src/views/mes/plan/PlanForm.vue index 61433502..7f1ab8c4 100644 --- a/src/views/mes/plan/PlanForm.vue +++ b/src/views/mes/plan/PlanForm.vue @@ -99,24 +99,13 @@ - - - - {{ dict.label }} - - - - + Date: Wed, 15 Apr 2026 15:58:50 +0800 Subject: [PATCH 7/9] =?UTF-8?q?style=EF=BC=9A=E7=94=98=E7=89=B9=E5=9B=BE?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E6=A0=B7=E5=BC=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mes/components/ScheduleGanttPanel.vue | 225 +++++++++++++++--- 1 file changed, 191 insertions(+), 34 deletions(-) diff --git a/src/views/mes/components/ScheduleGanttPanel.vue b/src/views/mes/components/ScheduleGanttPanel.vue index bb19e14c..b6f96de6 100644 --- a/src/views/mes/components/ScheduleGanttPanel.vue +++ b/src/views/mes/components/ScheduleGanttPanel.vue @@ -3,31 +3,33 @@
计划信息
- - {{ activePreviewDevice.deviceName }} - {{ activePreviewDevice.deviceId }} - {{ activePreviewDevice.ratedCapacity ?? '-' }} - {{ activePreviewDevice.plans?.length ?? 0 }} - - -
计划明细
-
-
-
- {{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }} + +
@@ -56,6 +58,24 @@ 确定 + + + + + + + + + + +