From 14817696a26eb142ccae03fa7c43050840e057f5 Mon Sep 17 00:00:00 2001
From: hsy <1029150275@qq.com>
Date: 星期一, 17 八月 2026 09:35:03 +0800
Subject: [PATCH] feat(mes): 优化生产订单与排产任务列表显示,新增启停工及校验逻辑 1. 生产订单列表展示优化 (WorkOrder) - 新增「生产状态」列,全局注册 `ORDER_PRODUCTION_STATUS` 常量并绑定系统动态字典。 - 彻底重构「工序生产进度」单元格显示:弃用臃肿的 Steps 组件,改为极简横向布局(彩色状态圆点 + 进度百分比),解决表格行高被强行撑开及内容截断问题,支持横向滚动。 - 优化「完成进度」列宽及展示样式,将百分比数值外置同行显示,更加直观。 2. 排产任务列表改造 (Task) - 顶部表头重构,由单一标题重构为「全部 / 未完成 / 已完成」的 Tabs 标签页切换结构。 - 重写数据过滤逻辑,剥离写死的 status 查询,改为使用 `scheduleStatus` 字段动态响应 Tabs 切换 (0: 未完成, 1: 已完成)。 3. 工单业务逻辑增强与状态流转 - 落实「工单查询逻辑设计方案」,优化底层的工单条件检索逻辑。 - 实现「添加工单启停功能」与「停工状态」,支持工单在执行过程中的暂停与恢复业务流转。 - 强化容错与防呆设计:操作「完成订单」时增加前置校验 `handleCheckFinish`,若生产状态为未完成,强制弹出二次确认 Modal,避免误操作。 # 相关讨论/参考方案 # - 添加工单启停功能 # - 停工状态功能设计方案 # - 工单查询逻辑设计方案 # - 工单完成进度显示优化
---
src/views/mes/pro/task/data.ts | 95 ++++++++++++++++++++++++++++++++++-------------
1 files changed, 69 insertions(+), 26 deletions(-)
diff --git a/src/views/mes/pro/task/data.ts b/src/views/mes/pro/task/data.ts
index 605cd9c..c5fec1a 100644
--- a/src/views/mes/pro/task/data.ts
+++ b/src/views/mes/pro/task/data.ts
@@ -3,12 +3,17 @@
import type { MesProTaskApi } from '#/api/mes/pro/task';
import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
+import type { Ref } from 'vue';
+
import { markRaw } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
+import { message } from 'ant-design-vue';
+
import CrmCustomerSelect from '#/components/crm-customer-select.vue';
+import { calculateTaskDuration } from '#/api/mes/pro/task';
import { getRangePickerDefaultProps } from '#/utils';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
@@ -125,15 +130,33 @@
width: 80,
},
{
+ field: 'status',
+ title: '璁㈠崟鐘舵��',
+ width: 100,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.MES_PRO_WORK_ORDER_STATUS },
+ },
+ },
+ {
+ field: 'productionStatus',
+ title: '鐢熶骇鐘舵��',
+ width: 100,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.ORDER_PRODUCTION_STATUS },
+ },
+ },
+ {
field: 'quantity',
title: '璁㈠崟鏁伴噺',
width: 100,
},
- {
- field: 'quantityChanged',
- title: '璋冩暣鏁伴噺',
- width: 100,
- },
+ // {
+ // field: 'quantityChanged',
+ // title: '璋冩暣鏁伴噺',
+ // width: 100,
+ // },
{
field: 'quantityProduced',
title: '宸茬敓浜ф暟閲�',
@@ -155,18 +178,10 @@
width: 120,
formatter: 'formatDate',
},
- {
- field: 'status',
- title: '鎺掍骇鐘舵��',
- width: 100,
- cellRender: {
- name: 'CellDict',
- props: { type: DICT_TYPE.MES_PRO_WORK_ORDER_STATUS },
- },
- },
+
{
title: '鎿嶄綔',
- width: 100,
+ width: 160,
fixed: 'right',
slots: { default: 'actions' },
},
@@ -279,7 +294,7 @@
class: '!w-full',
disabled: true,
format: 'YYYY-MM-DD',
- valueFormat: 'x',
+ valueFormat: 'YYYY-MM-DD',
},
},
{
@@ -376,7 +391,7 @@
}
/** 鐢熶骇浠诲姟鏂板/淇敼鐨勮〃鍗� */
-export function useTaskFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
+export function useTaskFormSchema(formApi?: VbenFormApi, workOrderQuantity?: Ref<number | undefined>): VbenFormSchema[] {
return [
{
fieldName: 'workstationId',
@@ -384,6 +399,7 @@
component: markRaw(MdWorkstationSelect),
componentProps: {
placeholder: '璇烽�夋嫨宸ヤ綔绔�',
+ onChange: () => recalcDuration(formApi),
},
rules: 'selectRequired',
},
@@ -391,11 +407,13 @@
fieldName: 'quantity',
label: '鎺掍骇鏁伴噺',
component: 'InputNumber',
+ description: () => workOrderQuantity?.value != null ? `璁㈠崟鏁伴噺锛�${workOrderQuantity.value}` : '',
componentProps: {
class: '!w-full',
min: 0.01,
placeholder: '璇疯緭鍏ユ帓浜ф暟閲�',
precision: 2,
+ onChange: () => recalcDuration(formApi),
},
rules: 'required',
},
@@ -410,11 +428,17 @@
component: 'DatePicker',
componentProps: {
class: '!w-full',
- placeholder: '璇烽�夋嫨寮�濮嬫椂闂�',
+ placeholder: '璇峰厛閫夋嫨宸ヤ綔绔�',
showTime: true,
- valueFormat: 'x',
- // 寮�濮嬫椂闂村彉鏇达細閲嶆柊璁$畻缁撴潫鏃堕棿
+ valueFormat: 'YYYY-MM-DD HH:mm:ss',
onChange: () => recalcEndTime(formApi),
+ },
+ dependencies: {
+ triggerFields: ['workstationId'],
+ componentProps: (values) => ({
+ disabled: !values.workstationId,
+ placeholder: values.workstationId ? '璇烽�夋嫨寮�濮嬫椂闂�' : '璇峰厛閫夋嫨宸ヤ綔绔�',
+ }),
},
rules: 'required',
},
@@ -440,7 +464,7 @@
class: '!w-full',
disabled: true,
showTime: true,
- valueFormat: 'x',
+ valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
},
{
@@ -456,6 +480,24 @@
];
}
+/** 閫夋嫨宸ヤ綔绔欐垨淇敼鏁伴噺鍚庯紝鑷姩璁$畻鐢熶骇鏃堕暱 */
+async function recalcDuration(formApi?: VbenFormApi) {
+ if (!formApi) return;
+ const values = await formApi.getValues();
+ if (!values.workstationId || !values.quantity) return;
+ try {
+ const res = await calculateTaskDuration(values.workstationId, values.quantity);
+ await formApi.setFieldValue('duration', res.duration);
+ if (res.duration === 0) {
+ message.warning('璇ュ伐浣滅珯鏈粦瀹氳澶囷紝鎴栫粦瀹氳澶囨湭閰嶇疆鐢熶骇鏁堢巼');
+ }
+ // 閲嶆柊璁$畻缁撴潫鏃堕棿
+ await recalcEndTime(formApi);
+ } catch {
+ // 璁$畻澶辫触涓嶉樆濉炵敤鎴锋搷浣�
+ }
+}
+
/** 璁$畻缁撴潫鏃堕棿锛氬紑濮嬫椂闂� + 鐢熶骇鏃堕暱锛堝皬鏃讹級 */
async function recalcEndTime(formApi?: VbenFormApi) {
if (!formApi) {
@@ -463,11 +505,12 @@
}
const values = await formApi.getValues();
if (values.startTime && values.duration) {
- const start = Number(values.startTime);
- await formApi.setFieldValue(
- 'endTime',
- start + values.duration * 3600 * 1000,
- );
+ const start = new Date(values.startTime).getTime();
+ const endTime = start + values.duration * 3600 * 1000;
+ const pad = (n: number) => String(n).padStart(2, '0');
+ const d = new Date(endTime);
+ const formatted = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
+ await formApi.setFieldValue('endTime', formatted);
}
}
--
Gitblit v1.9.3