From 5a1100aed4e7f87f4e78b80e26805cee2aa4fd8a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 26 May 2026 21:18:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(mes):=20=E8=BF=81=E7=A7=BB=E2=80=9C?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E6=8A=A5=E5=B7=A5=EF=BC=88pro=5Ffeedback?= =?UTF-8?q?=EF=BC=89=E2=80=9D=E7=9A=84=20ele=20=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/mes/pro/task/components/index.ts | 1 + .../components/pro-task-select-dialog.vue | 245 ++++++++ .../pro/task/components/pro-task-select.vue | 206 +++--- apps/web-antd/src/views/mes/pro/task/data.ts | 112 ++++ .../web-antd/src/views/mes/utils/constants.ts | 4 +- .../web-ele/src/api/mes/pro/feedback/index.ts | 113 ++++ apps/web-ele/src/api/mes/pro/task/index.ts | 68 ++ .../src/api/mes/wm/itemconsume/line/index.ts | 37 ++ .../api/mes/wm/productproduce/line/index.ts | 37 ++ .../src/views/mes/pro/feedback/data.ts | 589 ++++++++++++++++++ .../src/views/mes/pro/feedback/index.vue | 184 ++++++ .../views/mes/pro/feedback/modules/form.vue | 319 ++++++++++ .../feedback/modules/item-consume-list.vue | 57 ++ .../feedback/modules/product-produce-list.vue | 68 ++ .../views/mes/pro/task/components/index.ts | 2 + .../components/pro-task-select-dialog.vue | 248 ++++++++ .../pro/task/components/pro-task-select.vue | 155 +++++ apps/web-ele/src/views/mes/pro/task/data.ts | 112 ++++ apps/web-ele/src/views/mes/utils/constants.ts | 4 +- 19 files changed, 2451 insertions(+), 110 deletions(-) create mode 100644 apps/web-antd/src/views/mes/pro/task/components/pro-task-select-dialog.vue create mode 100644 apps/web-antd/src/views/mes/pro/task/data.ts create mode 100644 apps/web-ele/src/api/mes/pro/feedback/index.ts create mode 100644 apps/web-ele/src/api/mes/pro/task/index.ts create mode 100644 apps/web-ele/src/api/mes/wm/itemconsume/line/index.ts create mode 100644 apps/web-ele/src/api/mes/wm/productproduce/line/index.ts create mode 100644 apps/web-ele/src/views/mes/pro/feedback/data.ts create mode 100644 apps/web-ele/src/views/mes/pro/feedback/index.vue create mode 100644 apps/web-ele/src/views/mes/pro/feedback/modules/form.vue create mode 100644 apps/web-ele/src/views/mes/pro/feedback/modules/item-consume-list.vue create mode 100644 apps/web-ele/src/views/mes/pro/feedback/modules/product-produce-list.vue create mode 100644 apps/web-ele/src/views/mes/pro/task/components/index.ts create mode 100644 apps/web-ele/src/views/mes/pro/task/components/pro-task-select-dialog.vue create mode 100644 apps/web-ele/src/views/mes/pro/task/components/pro-task-select.vue create mode 100644 apps/web-ele/src/views/mes/pro/task/data.ts diff --git a/apps/web-antd/src/views/mes/pro/task/components/index.ts b/apps/web-antd/src/views/mes/pro/task/components/index.ts index 0642acb68..3847bb912 100644 --- a/apps/web-antd/src/views/mes/pro/task/components/index.ts +++ b/apps/web-antd/src/views/mes/pro/task/components/index.ts @@ -1 +1,2 @@ +export { default as ProTaskSelectDialog } from './pro-task-select-dialog.vue'; export { default as ProTaskSelect } from './pro-task-select.vue'; diff --git a/apps/web-antd/src/views/mes/pro/task/components/pro-task-select-dialog.vue b/apps/web-antd/src/views/mes/pro/task/components/pro-task-select-dialog.vue new file mode 100644 index 000000000..da32b86d3 --- /dev/null +++ b/apps/web-antd/src/views/mes/pro/task/components/pro-task-select-dialog.vue @@ -0,0 +1,245 @@ + + + diff --git a/apps/web-antd/src/views/mes/pro/task/components/pro-task-select.vue b/apps/web-antd/src/views/mes/pro/task/components/pro-task-select.vue index d8edc8e0a..fc7e8dae7 100644 --- a/apps/web-antd/src/views/mes/pro/task/components/pro-task-select.vue +++ b/apps/web-antd/src/views/mes/pro/task/components/pro-task-select.vue @@ -1,21 +1,16 @@ diff --git a/apps/web-antd/src/views/mes/pro/task/data.ts b/apps/web-antd/src/views/mes/pro/task/data.ts new file mode 100644 index 000000000..c29921b81 --- /dev/null +++ b/apps/web-antd/src/views/mes/pro/task/data.ts @@ -0,0 +1,112 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesProTaskApi } from '#/api/mes/pro/task'; + +import { markRaw } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; + +import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; +import { ProProcessSelect } from '#/views/mes/pro/process/components'; +import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; + +/** 任务选择弹窗的搜索表单 */ +export function useTaskSelectGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'workOrderId', + label: '生产工单', + component: markRaw(ProWorkOrderSelect), + componentProps: { + allowClear: true, + placeholder: '请选择生产工单', + }, + }, + { + fieldName: 'processId', + label: '所属工序', + component: markRaw(ProProcessSelect), + componentProps: { + allowClear: true, + placeholder: '请选择工序', + }, + }, + { + fieldName: 'workstationId', + label: '工作站', + component: markRaw(MdWorkstationSelect), + componentProps: { + allowClear: true, + placeholder: '请选择工作站', + }, + }, + { + fieldName: 'code', + label: '任务编号', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入任务编号', + }, + }, + { + fieldName: 'name', + label: '任务名称', + component: 'Input', + componentProps: { + allowClear: true, + placeholder: '请输入任务名称', + }, + }, + ]; +} + +/** 任务选择弹窗的字段 */ +export function useTaskSelectGridColumns( + multiple = false, +): VxeTableGridOptions['columns'] { + return [ + { type: multiple ? 'checkbox' : 'radio', width: 50 }, + { field: 'code', title: '任务编号', width: 180 }, + { field: 'name', title: '任务名称', minWidth: 140 }, + { field: 'workstationCode', title: '工作站编码', width: 140 }, + { field: 'workstationName', title: '工作站名称', width: 140 }, + { field: 'processName', title: '工序', width: 120 }, + { + field: 'checkFlag', + title: '是否质检', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { field: 'itemCode', title: '物料编码', width: 140 }, + { field: 'itemName', title: '物料名称', width: 140 }, + { field: 'itemSpecification', title: '规格型号', width: 120 }, + { field: 'quantity', title: '排产数量', width: 100 }, + { field: 'producedQuantity', title: '已生产数量', width: 110 }, + { + field: 'startTime', + title: '开始生产时间', + width: 170, + formatter: 'formatDateTime', + }, + { field: 'duration', title: '生产时长', width: 100 }, + { + field: 'endTime', + title: '预计完成时间', + width: 170, + formatter: 'formatDateTime', + }, + { + field: 'status', + title: '任务状态', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MES_PRO_TASK_STATUS }, + }, + }, + ]; +} diff --git a/apps/web-antd/src/views/mes/utils/constants.ts b/apps/web-antd/src/views/mes/utils/constants.ts index 2fd061dc6..668cf7240 100644 --- a/apps/web-antd/src/views/mes/utils/constants.ts +++ b/apps/web-antd/src/views/mes/utils/constants.ts @@ -166,10 +166,10 @@ export const MesProTaskStatusEnum = { /** MES 生产报工状态枚举 */ export const MesProFeedbackStatusEnum = { PREPARE: MesOrderStatusConstants.DRAFT, - CONFIRMED: MesOrderStatusConstants.CONFIRMED, APPROVING: MesOrderStatusConstants.APPROVING, + UNCHECK: MesOrderStatusConstants.APPROVED, FINISHED: MesOrderStatusConstants.FINISHED, - CANCELLED: MesOrderStatusConstants.CANCELLED, + CANCELED: MesOrderStatusConstants.CANCELLED, } as const; /** MES 流转卡状态枚举 */ diff --git a/apps/web-ele/src/api/mes/pro/feedback/index.ts b/apps/web-ele/src/api/mes/pro/feedback/index.ts new file mode 100644 index 000000000..21a988ba8 --- /dev/null +++ b/apps/web-ele/src/api/mes/pro/feedback/index.ts @@ -0,0 +1,113 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesProFeedbackApi { + /** MES 生产报工 */ + export interface Feedback { + id?: number; + code?: string; // 报工单编号 + type?: number; // 报工类型 + channel?: string; // 报工途径 + feedbackTime?: number; // 报工时间 + workstationId?: number; // 工作站编号 + workstationCode?: string; // 工作站编码 + workstationName?: string; // 工作站名称 + routeId?: number; // 工艺路线编号 + routeCode?: string; // 工艺路线编码 + processId?: number; // 工序编号 + processCode?: string; // 工序编码 + processName?: string; // 工序名称 + checkFlag?: boolean; // 是否需要检验 + workOrderId?: number; // 生产工单编号 + workOrderCode?: string; // 工单编码 + workOrderName?: string; // 工单名称 + taskId?: number; // 生产任务编号 + taskCode?: string; // 任务编码 + itemId?: number; // 产品物料编号 + itemCode?: string; // 物料编码 + itemName?: string; // 物料名称 + itemSpecification?: string; // 规格型号 + unitMeasureId?: number; // 单位编号 + unitMeasureName?: string; // 单位名称 + expireDate?: number; // 过期日期 + scheduledQuantity?: number; // 排产数量 + feedbackQuantity?: number; // 本次报工数量 + qualifiedQuantity?: number; // 合格品数量 + unqualifiedQuantity?: number; // 不良品数量 + uncheckQuantity?: number; // 待检测数量 + laborScrapQuantity?: number; // 工废数量 + materialScrapQuantity?: number; // 料废数量 + otherScrapQuantity?: number; // 其他废品数量 + feedbackUserId?: number; // 报工用户编号 + feedbackUserNickname?: string; // 报工人昵称 + approveUserId?: number; // 审核用户编号 + approveUserNickname?: string; // 审核人昵称 + status?: number; // 状态 + remark?: string; // 备注 + creator?: string; // 创建人 + createTime?: number; // 创建时间 + } + + /** MES 生产报工分页查询参数 */ + export interface PageParams extends PageParam { + code?: string; + type?: number; + workOrderId?: number; + itemId?: number; + feedbackUserId?: number; + creator?: string; + status?: number; + feedbackTime?: string[]; + } +} + +/** 查询生产报工分页 */ +export function getFeedbackPage(params: MesProFeedbackApi.PageParams) { + return requestClient.get>( + '/mes/pro/feedback/page', + { params }, + ); +} + +/** 查询生产报工详情 */ +export function getFeedback(id: number) { + return requestClient.get( + `/mes/pro/feedback/get?id=${id}`, + ); +} + +/** 新增生产报工 */ +export function createFeedback(data: MesProFeedbackApi.Feedback) { + return requestClient.post('/mes/pro/feedback/create', data); +} + +/** 修改生产报工 */ +export function updateFeedback(data: MesProFeedbackApi.Feedback) { + return requestClient.put('/mes/pro/feedback/update', data); +} + +/** 删除生产报工 */ +export function deleteFeedback(id: number) { + return requestClient.delete(`/mes/pro/feedback/delete?id=${id}`); +} + +/** 导出生产报工 Excel */ +export function exportFeedback(params: Partial) { + return requestClient.download('/mes/pro/feedback/export-excel', { params }); +} + +/** 提交生产报工 */ +export function submitFeedback(id: number) { + return requestClient.put(`/mes/pro/feedback/submit?id=${id}`); +} + +/** 驳回生产报工 */ +export function rejectFeedback(id: number) { + return requestClient.put(`/mes/pro/feedback/reject?id=${id}`); +} + +/** 审批生产报工(返回是否已审批完成) */ +export function approveFeedback(id: number) { + return requestClient.put(`/mes/pro/feedback/approve?id=${id}`); +} diff --git a/apps/web-ele/src/api/mes/pro/task/index.ts b/apps/web-ele/src/api/mes/pro/task/index.ts new file mode 100644 index 000000000..d7bfd2f5e --- /dev/null +++ b/apps/web-ele/src/api/mes/pro/task/index.ts @@ -0,0 +1,68 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesProTaskApi { + /** MES 生产任务 */ + export interface Task { + id?: number; + code?: string; // 任务编码 + name?: string; // 任务名称 + workOrderId?: number; // 生产工单编号 + workOrderCode?: string; // 工单编码 + workOrderName?: string; // 工单名称 + workstationId?: number; // 工作站编号 + workstationCode?: string; // 工作站编码 + workstationName?: string; // 工作站名称 + routeId?: number; // 工艺路线编号 + processId?: number; // 工序编号 + processName?: string; // 工序名称 + itemId?: number; // 产品物料编号 + itemCode?: string; // 产品编码 + itemName?: string; // 产品名称 + itemSpecification?: string; // 规格型号 + unitMeasureId?: number; // 单位编号 + unitMeasureName?: string; // 单位名称 + quantity?: number; // 排产数量 + producedQuantity?: number; // 已生产数量 + qualifyQuantity?: number; // 合格品数量 + unqualifyQuantity?: number; // 不良品数量 + changedQuantity?: number; // 调整数量 + clientId?: number; // 客户编号 + clientName?: string; // 客户名称 + startTime?: number; // 开始生产时间 + endTime?: number; // 结束生产时间 + duration?: number; // 生产时长(工作日,1=8小时) + requestDate?: number; // 需求日期(从工单查) + finishDate?: number; // 完成日期 + cancelDate?: number; // 取消日期 + colorCode?: string; // 甘特图显示颜色 + status?: number; // 任务状态 + checkFlag?: boolean; // 是否质检(派生自工艺路线工序) + remark?: string; // 备注 + } + + /** MES 生产任务分页查询参数 */ + export interface PageParams extends PageParam { + code?: string; + name?: string; + workOrderId?: number; + workstationId?: number; + itemId?: number; + statuses?: number[]; + status?: number; + } +} + +/** 查询生产任务分页 */ +export function getTaskPage(params: MesProTaskApi.PageParams) { + return requestClient.get>( + '/mes/pro/task/page', + { params }, + ); +} + +/** 查询生产任务详情 */ +export function getTask(id: number) { + return requestClient.get(`/mes/pro/task/get?id=${id}`); +} diff --git a/apps/web-ele/src/api/mes/wm/itemconsume/line/index.ts b/apps/web-ele/src/api/mes/wm/itemconsume/line/index.ts new file mode 100644 index 000000000..2a353b766 --- /dev/null +++ b/apps/web-ele/src/api/mes/wm/itemconsume/line/index.ts @@ -0,0 +1,37 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesWmItemConsumeLineApi { + /** MES 物料消耗行 */ + export interface ItemConsumeLine { + id?: number; + feedbackId?: number; // 报工编号 + itemId?: number; // 物料编号 + itemCode?: string; // 物资编码 + itemName?: string; // 物资名称 + specification?: string; // 规格型号 + unitId?: number; // 单位编号 + unitName?: string; // 单位 + quantity?: number; // 消耗数量 + batchCode?: string; // 批次号 + locationId?: number; // 库位编号 + locationName?: string; // 库位名称 + remark?: string; // 备注 + } + + /** MES 物料消耗行分页查询参数 */ + export interface PageParams extends PageParam { + feedbackId?: number; + } +} + +/** 查询物料消耗行分页 */ +export function getItemConsumeLinePage( + params: MesWmItemConsumeLineApi.PageParams, +) { + return requestClient.get>( + '/mes/wm/item-consume-line/page', + { params }, + ); +} diff --git a/apps/web-ele/src/api/mes/wm/productproduce/line/index.ts b/apps/web-ele/src/api/mes/wm/productproduce/line/index.ts new file mode 100644 index 000000000..ab3f49f29 --- /dev/null +++ b/apps/web-ele/src/api/mes/wm/productproduce/line/index.ts @@ -0,0 +1,37 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MesWmProductProduceLineApi { + /** MES 产品产出行 */ + export interface ProductProduceLine { + id?: number; + feedbackId?: number; // 报工编号 + itemId?: number; // 物料编号 + itemCode?: string; // 物资编码 + itemName?: string; // 物资名称 + specification?: string; // 规格型号 + unitMeasureId?: number; // 单位编号 + unitMeasureName?: string; // 单位 + quantity?: number; // 产出数量 + batchCode?: string; // 批次号 + qualityStatus?: number; // 质量状态 + locationId?: number; // 库位编号 + locationName?: string; // 库位名称 + remark?: string; // 备注 + } + + /** MES 产品产出行分页查询参数 */ + export interface PageParams extends PageParam { + feedbackId?: number; + } +} + +/** 查询产品产出行分页 */ +export function getProductProduceLinePage( + params: MesWmProductProduceLineApi.PageParams, +) { + return requestClient.get< + PageResult + >('/mes/wm/product-produce-line/page', { params }); +} diff --git a/apps/web-ele/src/views/mes/pro/feedback/data.ts b/apps/web-ele/src/views/mes/pro/feedback/data.ts new file mode 100644 index 000000000..21a677ff5 --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/feedback/data.ts @@ -0,0 +1,589 @@ +import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesProFeedbackApi } from '#/api/mes/pro/feedback'; +import type { MesProTaskApi } from '#/api/mes/pro/task'; + +import { h, markRaw } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { ElButton } from 'element-plus'; + +import { generateAutoCode } from '#/api/mes/md/autocode/record'; +import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; +import { MdItemSelect } from '#/views/mes/md/item/components'; +import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; +import { ProTaskSelect } from '#/views/mes/pro/task/components'; +import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; +import { + MesAutoCodeRuleCode, + MesProTaskStatusEnum, + MesProWorkOrderStatusEnum, +} from '#/views/mes/utils/constants'; + +/** 生产报工表单类型 */ +export type FormType = 'approve' | 'create' | 'detail' | 'submit' | 'update'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '报工单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入报工单号', + }, + }, + { + fieldName: 'type', + label: '报工类型', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.MES_PRO_FEEDBACK_TYPE, 'number'), + placeholder: '请选择报工类型', + }, + }, + { + fieldName: 'workOrderId', + label: '生产工单', + component: markRaw(ProWorkOrderSelect), + componentProps: { + clearable: true, + placeholder: '请选择工单', + }, + }, + { + fieldName: 'itemId', + label: '产品物料', + component: markRaw(MdItemSelect), + componentProps: { + clearable: true, + placeholder: '请选择产品物料', + }, + }, + { + fieldName: 'feedbackUserId', + label: '报工人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + clearable: true, + labelField: 'nickname', + placeholder: '请选择报工人', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '记录人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + clearable: true, + labelField: 'nickname', + placeholder: '请选择记录人', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.MES_PRO_FEEDBACK_STATUS, 'number'), + placeholder: '请选择状态', + }, + }, + { + fieldName: 'feedbackTime', + label: '报工时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'code', + title: '报工单号', + width: 160, + slots: { default: 'code' }, + }, + { + field: 'type', + title: '报工类型', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MES_PRO_FEEDBACK_TYPE }, + }, + }, + { field: 'workstationName', title: '工作站', width: 120 }, + { field: 'processName', title: '工序', width: 100 }, + { field: 'workOrderCode', title: '生产工单编码', width: 160 }, + { field: 'itemCode', title: '产品物料编码', width: 120 }, + { field: 'itemName', title: '产品物料名称', minWidth: 140 }, + { field: 'itemSpecification', title: '规格型号', width: 120 }, + { field: 'unitMeasureName', title: '单位', width: 80 }, + { field: 'feedbackQuantity', title: '报工数量', width: 100 }, + { field: 'feedbackUserNickname', title: '报工人', width: 100 }, + { + field: 'feedbackTime', + title: '报工时间', + width: 180, + formatter: 'formatDateTime', + }, + { field: 'approveUserNickname', title: '审核人', width: 100 }, + { + field: 'status', + title: '状态', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MES_PRO_FEEDBACK_STATUS }, + }, + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** + * 新增/编辑/提交/审批/详情生产报工的表单 + * + * - create / update:录入报工主体信息和数量 + * - submit / approve / detail:主体字段只读 + * + * 数量区域根据 `checkFlag` 和 `unqualifiedQuantity` 动态显示: + * - 非质检工序:报工数量 = 合格 + 不良;不良 > 0 时再展开工废/料废/其他废品 + * - 质检工序:只填报工数量(视为待检数量) + */ +export function useFormSchema( + formType: FormType, + formApi?: VbenFormApi, +): VbenFormSchema[] { + const isHeaderReadonly = ['approve', 'detail', 'submit'].includes(formType); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { triggerFields: [''], show: () => false }, + }, + { + fieldName: 'checkFlag', + component: 'Input', + dependencies: { triggerFields: [''], show: () => false }, + defaultValue: true, + }, + { + fieldName: 'routeId', + component: 'Input', + dependencies: { triggerFields: [''], show: () => false }, + }, + { + fieldName: 'processId', + component: 'Input', + dependencies: { triggerFields: [''], show: () => false }, + }, + { + fieldName: 'itemId', + component: 'Input', + dependencies: { triggerFields: [''], show: () => false }, + }, + { + fieldName: 'code', + label: '报工单号', + component: 'Input', + componentProps: { + disabled: isHeaderReadonly, + placeholder: '请输入报工单号', + }, + rules: 'required', + suffix: () => + h( + ElButton, + { + disabled: isHeaderReadonly, + type: 'default', + onClick: async () => { + const code = await generateAutoCode( + MesAutoCodeRuleCode.PRO_FEEDBACK_CODE, + ); + await formApi?.setFieldValue('code', code); + }, + }, + { default: () => '生成' }, + ), + }, + { + fieldName: 'type', + label: '报工类型', + component: 'Select', + componentProps: { + disabled: isHeaderReadonly, + options: getDictOptions(DICT_TYPE.MES_PRO_FEEDBACK_TYPE, 'number'), + placeholder: '请选择报工类型', + }, + rules: 'required', + }, + { + fieldName: 'workOrderId', + label: '生产工单', + component: markRaw(ProWorkOrderSelect), + componentProps: { + disabled: isHeaderReadonly, + placeholder: '请选择工单', + status: MesProWorkOrderStatusEnum.CONFIRMED, + // 工单变更:清空任务及任务带出的产品信息、数量区域控制位 + onChange: async () => { + await formApi?.setValues({ + checkFlag: true, + itemCode: undefined, + itemId: undefined, + itemName: undefined, + itemSpecification: undefined, + processId: undefined, + routeId: undefined, + taskId: undefined, + unitMeasureName: undefined, + workstationId: undefined, + }); + }, + }, + rules: 'selectRequired', + }, + { + fieldName: 'taskId', + label: '生产任务', + component: markRaw(ProTaskSelect), + dependencies: { + triggerFields: ['workOrderId', 'workstationId'], + componentProps: (values) => ({ + disabled: isHeaderReadonly || !values.workOrderId, + placeholder: values.workOrderId ? '请选择任务' : '请先选择工单', + statuses: [MesProTaskStatusEnum.PREPARE], + workOrderId: values.workOrderId, + workstationId: values.workstationId, + }), + }, + // 任务变更:自动填充关联字段、产品信息、checkFlag + componentProps: { + onChange: async (task?: MesProTaskApi.Task) => { + if (!task) { + return; + } + await formApi?.setValues({ + itemCode: task.itemCode, + itemId: task.itemId, + itemName: task.itemName, + itemSpecification: task.itemSpecification, + processId: task.processId, + routeId: task.routeId, + unitMeasureName: task.unitMeasureName, + workstationId: task.workstationId, + }); + // 工艺路线工序的 checkFlag 决定数量区域展示 + if (task.routeId && task.processId) { + try { + const routeProcess = await getRouteProcessByRouteAndProcess( + task.routeId, + task.processId, + ); + await formApi?.setFieldValue( + 'checkFlag', + routeProcess?.checkFlag ?? false, + ); + } catch { + await formApi?.setFieldValue('checkFlag', true); + } + } + }, + }, + rules: 'selectRequired', + }, + { + fieldName: 'workstationId', + label: '工作站', + component: markRaw(MdWorkstationSelect), + componentProps: { + disabled: isHeaderReadonly, + placeholder: '请选择工作站', + }, + rules: 'selectRequired', + }, + { + fieldName: 'itemCode', + label: '产品编码', + component: 'Input', + componentProps: { disabled: true }, + dependencies: { + triggerFields: ['itemCode'], + show: (values) => !!values.itemCode, + }, + }, + { + fieldName: 'itemName', + label: '产品名称', + component: 'Input', + componentProps: { disabled: true }, + dependencies: { + triggerFields: ['itemCode'], + show: (values) => !!values.itemCode, + }, + }, + { + fieldName: 'unitMeasureName', + label: '单位', + component: 'Input', + componentProps: { disabled: true }, + dependencies: { + triggerFields: ['itemCode'], + show: (values) => !!values.itemCode, + }, + }, + { + fieldName: 'itemSpecification', + label: '规格', + component: 'Input', + componentProps: { disabled: true }, + dependencies: { + triggerFields: ['itemCode'], + show: (values) => !!values.itemCode, + }, + }, + { + fieldName: 'feedbackQuantity', + label: '报工数量', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + precision: 2, + }, + dependencies: { + triggerFields: ['checkFlag'], + // 非质检工序时,报工数量 = 合格 + 不良,禁用直接编辑 + componentProps: (values) => ({ + class: '!w-full', + controlsPosition: 'right', + disabled: !values.checkFlag, + min: 0, + placeholder: '请输入报工数量', + precision: 2, + }), + }, + rules: 'required', + }, + { + fieldName: 'qualifiedQuantity', + label: '合格品数量', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + precision: 2, + // 合格/不良变更,自动累计为报工数量 + onChange: async () => { + const values = await formApi?.getValues(); + await formApi?.setFieldValue( + 'feedbackQuantity', + (values?.qualifiedQuantity || 0) + + (values?.unqualifiedQuantity || 0), + ); + }, + }, + defaultValue: 0, + dependencies: { + triggerFields: ['checkFlag'], + show: (values) => !values.checkFlag, + }, + }, + { + fieldName: 'unqualifiedQuantity', + label: '不良品数量', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + precision: 2, + // 合格/不良变更,自动累计为报工数量 + onChange: async () => { + const values = await formApi?.getValues(); + await formApi?.setFieldValue( + 'feedbackQuantity', + (values?.qualifiedQuantity || 0) + + (values?.unqualifiedQuantity || 0), + ); + }, + }, + defaultValue: 0, + dependencies: { + triggerFields: ['checkFlag'], + show: (values) => !values.checkFlag, + }, + }, + { + fieldName: 'laborScrapQuantity', + label: '工废数量', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + precision: 2, + // 废品分类变更,自动累计为不良品数量及报工数量 + onChange: async () => { + const values = await formApi?.getValues(); + const unqualified = + (values?.laborScrapQuantity || 0) + + (values?.materialScrapQuantity || 0) + + (values?.otherScrapQuantity || 0); + await formApi?.setValues({ + feedbackQuantity: + (values?.qualifiedQuantity || 0) + unqualified, + unqualifiedQuantity: unqualified, + }); + }, + }, + defaultValue: 0, + dependencies: { + triggerFields: ['checkFlag', 'unqualifiedQuantity'], + show: (values) => + !values.checkFlag && (values.unqualifiedQuantity || 0) > 0, + }, + }, + { + fieldName: 'materialScrapQuantity', + label: '料废数量', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + precision: 2, + // 废品分类变更,自动累计为不良品数量及报工数量 + onChange: async () => { + const values = await formApi?.getValues(); + const unqualified = + (values?.laborScrapQuantity || 0) + + (values?.materialScrapQuantity || 0) + + (values?.otherScrapQuantity || 0); + await formApi?.setValues({ + feedbackQuantity: + (values?.qualifiedQuantity || 0) + unqualified, + unqualifiedQuantity: unqualified, + }); + }, + }, + defaultValue: 0, + dependencies: { + triggerFields: ['checkFlag', 'unqualifiedQuantity'], + show: (values) => + !values.checkFlag && (values.unqualifiedQuantity || 0) > 0, + }, + }, + { + fieldName: 'otherScrapQuantity', + label: '其他废品', + component: 'InputNumber', + componentProps: { + class: '!w-full', + controlsPosition: 'right', + min: 0, + precision: 2, + // 废品分类变更,自动累计为不良品数量及报工数量 + onChange: async () => { + const values = await formApi?.getValues(); + const unqualified = + (values?.laborScrapQuantity || 0) + + (values?.materialScrapQuantity || 0) + + (values?.otherScrapQuantity || 0); + await formApi?.setValues({ + feedbackQuantity: + (values?.qualifiedQuantity || 0) + unqualified, + unqualifiedQuantity: unqualified, + }); + }, + }, + defaultValue: 0, + dependencies: { + triggerFields: ['checkFlag', 'unqualifiedQuantity'], + show: (values) => + !values.checkFlag && (values.unqualifiedQuantity || 0) > 0, + }, + }, + { + fieldName: 'feedbackUserId', + label: '报工人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + clearable: true, + disabled: isHeaderReadonly, + labelField: 'nickname', + placeholder: '请选择报工人', + valueField: 'id', + }, + rules: 'selectRequired', + }, + { + fieldName: 'feedbackTime', + label: '报工时间', + component: 'DatePicker', + componentProps: { + class: '!w-full', + disabled: isHeaderReadonly, + format: 'YYYY-MM-DD HH:mm:ss', + placeholder: '请选择报工时间', + type: 'datetime', + valueFormat: 'x', + }, + rules: 'required', + }, + { + fieldName: 'approveUserId', + label: '审核人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + clearable: true, + disabled: isHeaderReadonly, + labelField: 'nickname', + placeholder: '请选择审核人', + valueField: 'id', + }, + rules: 'selectRequired', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + formItemClass: 'col-span-3', + componentProps: { + autosize: { maxRows: 3, minRows: 2 }, + disabled: formType === 'detail', + placeholder: '请输入备注', + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mes/pro/feedback/index.vue b/apps/web-ele/src/views/mes/pro/feedback/index.vue new file mode 100644 index 000000000..ef65e4629 --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/feedback/index.vue @@ -0,0 +1,184 @@ + + + diff --git a/apps/web-ele/src/views/mes/pro/feedback/modules/form.vue b/apps/web-ele/src/views/mes/pro/feedback/modules/form.vue new file mode 100644 index 000000000..da61a777f --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/feedback/modules/form.vue @@ -0,0 +1,319 @@ + + + diff --git a/apps/web-ele/src/views/mes/pro/feedback/modules/item-consume-list.vue b/apps/web-ele/src/views/mes/pro/feedback/modules/item-consume-list.vue new file mode 100644 index 000000000..5aa78254f --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/feedback/modules/item-consume-list.vue @@ -0,0 +1,57 @@ + + + diff --git a/apps/web-ele/src/views/mes/pro/feedback/modules/product-produce-list.vue b/apps/web-ele/src/views/mes/pro/feedback/modules/product-produce-list.vue new file mode 100644 index 000000000..17ac88995 --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/feedback/modules/product-produce-list.vue @@ -0,0 +1,68 @@ + + + diff --git a/apps/web-ele/src/views/mes/pro/task/components/index.ts b/apps/web-ele/src/views/mes/pro/task/components/index.ts new file mode 100644 index 000000000..3847bb912 --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/task/components/index.ts @@ -0,0 +1,2 @@ +export { default as ProTaskSelectDialog } from './pro-task-select-dialog.vue'; +export { default as ProTaskSelect } from './pro-task-select.vue'; diff --git a/apps/web-ele/src/views/mes/pro/task/components/pro-task-select-dialog.vue b/apps/web-ele/src/views/mes/pro/task/components/pro-task-select-dialog.vue new file mode 100644 index 000000000..3e43fc9ad --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/task/components/pro-task-select-dialog.vue @@ -0,0 +1,248 @@ + + + diff --git a/apps/web-ele/src/views/mes/pro/task/components/pro-task-select.vue b/apps/web-ele/src/views/mes/pro/task/components/pro-task-select.vue new file mode 100644 index 000000000..99af6c290 --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/task/components/pro-task-select.vue @@ -0,0 +1,155 @@ + + + diff --git a/apps/web-ele/src/views/mes/pro/task/data.ts b/apps/web-ele/src/views/mes/pro/task/data.ts new file mode 100644 index 000000000..1e1cc70a2 --- /dev/null +++ b/apps/web-ele/src/views/mes/pro/task/data.ts @@ -0,0 +1,112 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MesProTaskApi } from '#/api/mes/pro/task'; + +import { markRaw } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; + +import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; +import { ProProcessSelect } from '#/views/mes/pro/process/components'; +import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; + +/** 任务选择弹窗的搜索表单 */ +export function useTaskSelectGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'workOrderId', + label: '生产工单', + component: markRaw(ProWorkOrderSelect), + componentProps: { + clearable: true, + placeholder: '请选择生产工单', + }, + }, + { + fieldName: 'processId', + label: '所属工序', + component: markRaw(ProProcessSelect), + componentProps: { + clearable: true, + placeholder: '请选择工序', + }, + }, + { + fieldName: 'workstationId', + label: '工作站', + component: markRaw(MdWorkstationSelect), + componentProps: { + clearable: true, + placeholder: '请选择工作站', + }, + }, + { + fieldName: 'code', + label: '任务编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入任务编号', + }, + }, + { + fieldName: 'name', + label: '任务名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入任务名称', + }, + }, + ]; +} + +/** 任务选择弹窗的字段 */ +export function useTaskSelectGridColumns( + multiple = false, +): VxeTableGridOptions['columns'] { + return [ + { type: multiple ? 'checkbox' : 'radio', width: 50 }, + { field: 'code', title: '任务编号', width: 180 }, + { field: 'name', title: '任务名称', minWidth: 140 }, + { field: 'workstationCode', title: '工作站编码', width: 140 }, + { field: 'workstationName', title: '工作站名称', width: 140 }, + { field: 'processName', title: '工序', width: 120 }, + { + field: 'checkFlag', + title: '是否质检', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { field: 'itemCode', title: '物料编码', width: 140 }, + { field: 'itemName', title: '物料名称', width: 140 }, + { field: 'itemSpecification', title: '规格型号', width: 120 }, + { field: 'quantity', title: '排产数量', width: 100 }, + { field: 'producedQuantity', title: '已生产数量', width: 110 }, + { + field: 'startTime', + title: '开始生产时间', + width: 170, + formatter: 'formatDateTime', + }, + { field: 'duration', title: '生产时长', width: 100 }, + { + field: 'endTime', + title: '预计完成时间', + width: 170, + formatter: 'formatDateTime', + }, + { + field: 'status', + title: '任务状态', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MES_PRO_TASK_STATUS }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mes/utils/constants.ts b/apps/web-ele/src/views/mes/utils/constants.ts index 2fd061dc6..668cf7240 100644 --- a/apps/web-ele/src/views/mes/utils/constants.ts +++ b/apps/web-ele/src/views/mes/utils/constants.ts @@ -166,10 +166,10 @@ export const MesProTaskStatusEnum = { /** MES 生产报工状态枚举 */ export const MesProFeedbackStatusEnum = { PREPARE: MesOrderStatusConstants.DRAFT, - CONFIRMED: MesOrderStatusConstants.CONFIRMED, APPROVING: MesOrderStatusConstants.APPROVING, + UNCHECK: MesOrderStatusConstants.APPROVED, FINISHED: MesOrderStatusConstants.FINISHED, - CANCELLED: MesOrderStatusConstants.CANCELLED, + CANCELED: MesOrderStatusConstants.CANCELLED, } as const; /** MES 流转卡状态枚举 */