From 889d8ddf4c13c1b1bdbabd04c2d80b904020151e Mon Sep 17 00:00:00 2001
From: hsy <1029150275@qq.com>
Date: 星期三, 19 八月 2026 12:49:24 +0800
Subject: [PATCH] 生产订单与报工模块改造汇总 (Git Commit 文档) feat(mes-pro): 生产订单及报工模块审批流与批次填报改造 1. 生产订单功能增强 (Work Order)
---
src/router/routes/modules/mes.ts | 10 +
src/views/mes/workbench/index.vue | 29 ++
src/views/mes/pro/feedback/detail/index.vue | 67 +++++++
src/views/mes/pro/task/data.ts | 4
.idea/mom-pro2-before.iml | 2
src/views/mes/pro/feedback/modules/form.vue | 63 ++++++
src/views/mes/pro/workorder/data.ts | 53 +++++
src/views/mes/workbench/types.ts | 1
src/api/system/user/index.ts | 4
src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue | 32 +++
src/views/mes/pro/feedback/index.vue | 30 ++
src/api/mes/pro/workorder/index.ts | 10 +
src/views/mes/pro/workorder/modules/form.vue | 21 +
src/views/mes/workbench/components/tabs/FeedbackTab.vue | 68 ++++++
src/views/mes/pro/feedback/data.ts | 33 +++
src/api/mes/pro/feedback/index.ts | 23 ++
src/views/mes/pro/task/modules/task-form.vue | 48 +++-
17 files changed, 442 insertions(+), 56 deletions(-)
diff --git a/.idea/mom-pro2-before.iml b/.idea/mom-pro2-before.iml
index 179693c..f93e5a6 100644
--- a/.idea/mom-pro2-before.iml
+++ b/.idea/mom-pro2-before.iml
@@ -7,6 +7,8 @@
<excludeFolder url="file://$MODULE_DIR$/.cache" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/build" />
+ <excludeFolder url="file://$MODULE_DIR$/.agents" />
+ <excludeFolder url="file://$MODULE_DIR$/.codex" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
diff --git a/src/api/mes/pro/feedback/index.ts b/src/api/mes/pro/feedback/index.ts
index 7666071..934eba8 100644
--- a/src/api/mes/pro/feedback/index.ts
+++ b/src/api/mes/pro/feedback/index.ts
@@ -1,8 +1,23 @@
-锘縤mport type { PageParam, PageResult } from '@vben/request';
+import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
export namespace MesProFeedbackApi {
+ export interface FeedbackSubmitReqVO {
+ id: number;
+ produceDate?: string;
+ expireDate?: string;
+ receiptDate?: string;
+ vendorId?: number;
+ clientId?: number;
+ purchaseOrderCode?: string;
+ salesOrderCode?: string;
+ toolId?: number;
+ moldId?: number;
+ lotNumber?: string;
+ qualityStatus?: number;
+ }
+
/** MES 鐢熶骇鎶ュ伐 */
export interface Feedback {
id?: number;
@@ -47,6 +62,8 @@
remark?: string; // 澶囨敞
creator?: string; // 鍒涘缓浜�
createTime?: number; // 鍒涘缓鏃堕棿
+ processInstanceId?: string; // BPM 娴佺▼瀹炰緥缂栧彿
+ processDefinitionKey?: string; // BPM 娴佺▼瀹氫箟 Key
}
/** MES 鐢熶骇鎶ュ伐鍒嗛〉鏌ヨ鍙傛暟 */
@@ -100,8 +117,8 @@
}
/** 鎻愪氦鐢熶骇鎶ュ伐 */
-export function submitFeedback(id: number) {
- return requestClient.put(`/mes/pro/feedback/submit?id=${id}`);
+export function submitFeedback(data: MesProFeedbackApi.FeedbackSubmitReqVO) {
+ return requestClient.put(`/mes/pro/feedback/submit`, data);
}
/** 椹冲洖鐢熶骇鎶ュ伐 */
diff --git a/src/api/mes/pro/workorder/index.ts b/src/api/mes/pro/workorder/index.ts
index 39bcf69..68b8037 100644
--- a/src/api/mes/pro/workorder/index.ts
+++ b/src/api/mes/pro/workorder/index.ts
@@ -40,6 +40,9 @@
completionProgress?: number; // 瀹屾垚杩涘害
remark?: string; // 澶囨敞
createTime?: number; // 鍒涘缓鏃堕棿
+ feedbackApprove?: boolean; // 鏄惁鎶ュ伐瀹℃壒
+ processDefinitionKey?: string; // 鎶ュ伐瀹℃壒娴佺▼瀹氫箟 Key
+ feedbackRole?: string; // 鎶ュ伐瑙掕壊
}
/** MES 鐢熶骇璁㈠崟鍒嗛〉鏌ヨ鍙傛暟 */
@@ -159,3 +162,10 @@
`/mes/pro/work-order/check-auto-finish/${id}`,
);
}
+
+/** 鑾峰緱鐢熶骇璁㈠崟瀹℃壒娴佺▼鍒楄〃 */
+export function getWorkOrderApproveProcessList() {
+ return requestClient.get<Array<{ id: string; name: string; key: string }>>(
+ '/mes/pro/work-order/approve-process-list',
+ );
+}
diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts
index cc7ce13..208d4b9 100644
--- a/src/api/system/user/index.ts
+++ b/src/api/system/user/index.ts
@@ -116,8 +116,8 @@
}
/** 閫氳繃瑙掕壊鏍囪瘑鑾峰彇鐢ㄦ埛绮剧畝淇℃伅鍒楄〃 */
-export function getSimpleUserListByRoleCode(code: string) {
+export function getSimpleUserListByRoleCode(code: Object) {
return requestClient.get<SystemUserApi.UserSimple[]>('/system/user/list-by-role-code', {
- params: { code },
+ params: code ,
});
}
diff --git a/src/router/routes/modules/mes.ts b/src/router/routes/modules/mes.ts
index de6c512..54d4328 100644
--- a/src/router/routes/modules/mes.ts
+++ b/src/router/routes/modules/mes.ts
@@ -29,6 +29,16 @@
},
component: () => import('#/views/mes/pd/project/detail/index.vue'),
},
+ {
+ path: 'pro/feedback/detail/:id',
+ name: 'MesProFeedbackDetail',
+ meta: {
+ title: '鐢熶骇鎶ュ伐璇︽儏',
+ activePath: '/mes/pro/feedback',
+ },
+ props: true,
+ component: () => import('#/views/mes/pro/feedback/detail/index.vue'),
+ },
],
},
];
diff --git a/src/views/mes/pro/feedback/data.ts b/src/views/mes/pro/feedback/data.ts
index 29b244a..8e2580a 100644
--- a/src/views/mes/pro/feedback/data.ts
+++ b/src/views/mes/pro/feedback/data.ts
@@ -2,6 +2,7 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesProFeedbackApi } from '#/api/mes/pro/feedback';
import type { MesProTaskApi } from '#/api/mes/pro/task';
+import type { DescriptionItemSchema } from '#/components/description';
import { h, markRaw } from 'vue';
@@ -23,6 +24,7 @@
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 { DictTag } from '#/components/dict-tag';
/** 鐢熶骇鎶ュ伐琛ㄥ崟绫诲瀷 */
export type FormType = 'approve' | 'create' | 'detail' | 'submit' | 'update';
@@ -601,3 +603,34 @@
},
];
}
+
+/** 鐢熶骇鎶ュ伐璇︽儏鎽樿 */
+export function useDetailSchema(): DescriptionItemSchema[] {
+ return [
+ { field: 'code', label: '鎶ヨ鍗曞彿' },
+ {
+ field: 'type',
+ label: '鎶ュ伐绫诲瀷',
+ render: (val) =>
+ h(DictTag, { type: DICT_TYPE.MES_PRO_FEEDBACK_TYPE, value: val }),
+ },
+ { field: 'workOrderId', label: '鐢熶骇璁㈠崟', render: (_val, data) => data.workOrderCode },
+ { field: 'taskId', label: '鐢熶骇浠诲姟', render: (_val, data) => data.taskCode },
+ { field: 'workstationName', label: '宸ヤ綔绔�' },
+ { field: 'itemCode', label: '浜у搧缂栫爜' },
+ { field: 'itemName', label: '浜у搧鍚嶇О' },
+ { field: 'unitMeasureName', label: '鍗曚綅' },
+ { field: 'itemSpecification', label: '瑙勬牸' },
+ { field: 'reportableQuantity', label: '鍙姤宸ユ暟閲�' },
+ { field: 'feedbackQuantity', label: '鎶ュ伐鏁伴噺' },
+ { field: 'qualifiedQuantity', label: '鍚堟牸鍝佹暟閲�' },
+ { field: 'unqualifiedQuantity', label: '涓嶈壇鍝佹暟閲�' },
+ { field: 'laborScrapQuantity', label: '宸ュ簾鏁伴噺' },
+ { field: 'materialScrapQuantity', label: '鏂欏簾鏁伴噺' },
+ { field: 'otherScrapQuantity', label: '鍏朵粬搴熷搧' },
+ { field: 'feedbackUserNickname', label: '鎶ュ伐浜�' },
+ { field: 'feedbackTime', label: '鎶ュ伐鏃堕棿' },
+ { field: 'approveUserNickname', label: '瀹℃牳浜�' },
+ { field: 'remark', label: '澶囨敞' },
+ ];
+}
diff --git a/src/views/mes/pro/feedback/detail/index.vue b/src/views/mes/pro/feedback/detail/index.vue
new file mode 100644
index 0000000..e3ecf9c
--- /dev/null
+++ b/src/views/mes/pro/feedback/detail/index.vue
@@ -0,0 +1,67 @@
+<script lang="ts" setup>
+import type { MesProFeedbackApi } from '#/api/mes/pro/feedback';
+
+import { onMounted, ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+import { Page } from '@vben/common-ui';
+import { useTabs } from '@vben/hooks';
+
+import { Card, Tabs } from 'ant-design-vue';
+
+import { TableAction } from '#/adapter/vxe-table';
+import { getFeedback } from '#/api/mes/pro/feedback';
+import { useDescription } from '#/components/description';
+
+import { useDetailSchema } from '../data';
+import ItemConsumeList from '../modules/item-consume-list.vue';
+import ProductProduceList from '../modules/product-produce-list.vue';
+
+const props = defineProps<{ id: string }>();
+
+const router = useRouter();
+const tabs = useTabs();
+
+const loading = ref(false);
+const feedbackId = ref(0);
+const feedback = ref<MesProFeedbackApi.Feedback>({});
+const activeTab = ref('itemConsume');
+
+const [Descriptions] = useDescription({
+ bordered: false,
+ column: 4,
+ schema: useDetailSchema(),
+});
+
+async function loadFeedbackDetail() {
+ loading.value = true;
+ try {
+ feedback.value = await getFeedback(feedbackId.value);
+ } finally {
+ loading.value = false;
+ }
+}
+
+function handleBack() {
+ tabs.closeCurrentTab();
+ router.push({ path: '/mes/pro/feedback' });
+}
+
+onMounted(() => {
+ feedbackId.value = Number(props.id);
+ loadFeedbackDetail();
+});
+</script>
+
+<template>
+ <Page
+ auto-content-height
+ :loading="loading"
+ :title="feedback.code ? `鐢熶骇鎶ュ伐璇︽儏 - ${feedback.code}` : '鐢熶骇鎶ュ伐璇︽儏'"
+ >
+ <Card class="min-h-[10%]">
+ <Descriptions :data="feedback" />
+ </Card>
+
+ </Page>
+</template>
diff --git a/src/views/mes/pro/feedback/index.vue b/src/views/mes/pro/feedback/index.vue
index adb6ae1..c56983a 100644
--- a/src/views/mes/pro/feedback/index.vue
+++ b/src/views/mes/pro/feedback/index.vue
@@ -19,6 +19,7 @@
import { $t } from '#/locales';
import { ref } from 'vue';
+import { useRouter } from 'vue-router';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
@@ -26,6 +27,7 @@
const userStore = useUserStore();
const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+const router = useRouter();
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
@@ -57,6 +59,15 @@
/** 瀹℃壒鐢熶骇鎶ュ伐 */
function handleApprove(row: MesProFeedbackApi.Feedback) {
formModalApi.setData({ formType: 'approve', id: row.id }).open();
+}
+
+/** 鏌ョ湅瀹℃壒璇︽儏 */
+function handleViewProcess(row: MesProFeedbackApi.Feedback) {
+ if (!row.processInstanceId) {
+ message.warning('璇ユ姤宸ュ皻鏈彁浜ゅ鎵�');
+ return;
+ }
+ router.push({ path: '/bpm/process-instance/detail', query: { id: row.processInstanceId } });
}
/** 璇︽儏鐢熶骇鎶ュ伐 */
@@ -165,14 +176,21 @@
ifShow: () => row.status === MesProFeedbackStatusEnum.PREPARE,
onClick: handleSubmit.bind(null, row),
},
+ // {
+ // label: $t('common.approve'),
+ // type: 'link',
+ // auth: ['mes:pro-feedback:approve'],
+ // ifShow: () =>
+ // row.status === MesProFeedbackStatusEnum.APPROVING &&
+ // row.approveUserId === currentUserId,
+ // onClick: handleApprove.bind(null, row),
+ // },
{
- label: $t('common.approve'),
+ label: '鏌ョ湅瀹℃壒',
type: 'link',
- auth: ['mes:pro-feedback:approve'],
- ifShow: () =>
- row.status === MesProFeedbackStatusEnum.APPROVING &&
- row.approveUserId === currentUserId,
- onClick: handleApprove.bind(null, row),
+ icon: ACTION_ICON.VIEW,
+ ifShow: () => row.status === MesProFeedbackStatusEnum.APPROVING,
+ onClick: handleViewProcess.bind(null, row),
},
{
label: $t('common.delete'),
diff --git a/src/views/mes/pro/feedback/modules/form.vue b/src/views/mes/pro/feedback/modules/form.vue
index d0e4edf..92bc22f 100644
--- a/src/views/mes/pro/feedback/modules/form.vue
+++ b/src/views/mes/pro/feedback/modules/form.vue
@@ -1,4 +1,4 @@
-锘�<script lang="ts" setup>
+<script lang="ts" setup>
import type { FormType } from '../data';
import type { MesProFeedbackApi } from '#/api/mes/pro/feedback';
@@ -25,6 +25,7 @@
updateFeedback,
} from '#/api/mes/pro/feedback';
import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
+import { getWorkOrder } from '#/api/mes/pro/workorder';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
@@ -142,6 +143,23 @@
}
}
+const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>();
+const batchActionType = ref<'submit' | 'approve'>('');
+
+/** 鎵ц鎻愪氦锛堝惈鎵规灞炴�э級 */
+async function doSubmit(batchFields?: Record<string, unknown>) {
+ if (!formData.value?.id) return;
+ modalApi.lock();
+ try {
+ await submitFeedback({ id: formData.value.id, ...batchFields });
+ await modalApi.close();
+ emit('success');
+ message.success('鎶ュ伐鍗曞凡鎻愪氦');
+ } finally {
+ modalApi.unlock();
+ }
+}
+
/** 鎻愪氦锛氫粎褰撹〃鍗曡緝蹇収鏈夊彉鏇存椂鎵嶅厛淇濆瓨锛屽啀璋冪敤鎻愪氦鎺ュ彛 */
async function handleSubmit() {
const { valid } = await formApi.validate();
@@ -156,16 +174,40 @@
await updateFeedback(data);
originalSnapshot.value = JSON.stringify(data);
}
- await submitFeedback(formData.value!.id!);
- await modalApi.close();
- emit('success');
- message.success('鎶ヨ鍗曞凡鎻愪氦');
+
+ // 妫�鏌ユ槸鍚﹂渶瑕佸鎵癸紝鑻ラ渶瑕佸鎵癸紝鍒欎笉闇�瑕佸湪姝ら樁娈靛~鍐欐壒娆″睘鎬э紝鐩存帴鎻愪氦
+ let feedbackApprove = false;
+ if (formData.value?.workOrderId) {
+ try {
+ const workOrder = await getWorkOrder(formData.value.workOrderId);
+ feedbackApprove = workOrder?.feedbackApprove ?? false;
+ } catch {
+ // ignore
+ }
+ }
+
+ if (feedbackApprove) {
+ await doSubmit();
+ return;
+ }
+
+ let checkResult;
+ try {
+ checkResult = await getBatchRequirements(formData.value!.id!);
+ } catch {
+ // ignore
+ }
+
+ if (checkResult?.needBatchInput) {
+ batchActionType.value = 'submit';
+ batchDialogRef.value?.open({ checkResult });
+ } else {
+ await doSubmit();
+ }
} finally {
modalApi.unlock();
}
}
-
-const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>();
/** 鎵ц瀹℃壒锛堝惈鎵规灞炴�э級 */
async function doApprove(batchFields?: Record<string, unknown>) {
@@ -195,6 +237,7 @@
const checkResult = await getBatchRequirements(formData.value.id);
if (checkResult?.needBatchInput) {
// 闇�瑕佸~鍐欐壒娆″睘鎬э紝寮圭獥鏀堕泦
+ batchActionType.value = 'approve';
batchDialogRef.value?.open({ checkResult });
} else {
await doApprove();
@@ -207,7 +250,11 @@
/** 鎵规寮圭獥纭鍥炶皟 */
function handleBatchConfirm(batchData: Record<string, unknown>) {
- doApprove(batchData);
+ if (batchActionType.value === 'submit') {
+ doSubmit(batchData);
+ } else {
+ doApprove(batchData);
+ }
}
/** 瀹℃壒涓嶉�氳繃 */
diff --git a/src/views/mes/pro/task/data.ts b/src/views/mes/pro/task/data.ts
index aeb9868..12cfcb1 100644
--- a/src/views/mes/pro/task/data.ts
+++ b/src/views/mes/pro/task/data.ts
@@ -393,7 +393,7 @@
}
/** 鐢熶骇浠诲姟鏂板/淇敼鐨勮〃鍗� */
-export function useTaskFormSchema(formApi?: VbenFormApi, workOrderQuantity?: Ref<number | undefined>): VbenFormSchema[] {
+export function useTaskFormSchema(formApi?: VbenFormApi, workOrderQuantity?: Ref<number | undefined>, feedbackRole?: Ref<string | undefined>): VbenFormSchema[] {
return [
{
fieldName: 'workstationId',
@@ -476,7 +476,7 @@
componentProps: {
placeholder: '璇烽�夋嫨鎶ュ伐浜�',
api: getSimpleUserListByRoleCode,
- params: { code: 'production_submit_work' },
+ params: { code: feedbackRole?.value || 'production_submit_work' },
labelField: 'nickname',
valueField: 'id',
mode: 'multiple',
diff --git a/src/views/mes/pro/task/modules/task-form.vue b/src/views/mes/pro/task/modules/task-form.vue
index fd83dcd..5b64e26 100644
--- a/src/views/mes/pro/task/modules/task-form.vue
+++ b/src/views/mes/pro/task/modules/task-form.vue
@@ -9,6 +9,7 @@
import { useVbenForm } from '#/adapter/form';
import { createTask, getTask, updateTask } from '#/api/mes/pro/task';
+import { getWorkOrder } from '#/api/mes/pro/workorder';
import { $t } from '#/locales';
import { useTaskFormSchema } from '../data';
@@ -23,6 +24,7 @@
routeId?: number;
workOrderId?: number;
}>({}); // 鏂板浠诲姟鏃剁殑璁㈠崟 / 宸ュ簭涓婁笅鏂�
+const feedbackRole = ref<string>();
const getTitle = computed(() => {
return formData.value?.id
@@ -72,8 +74,6 @@
context.value = {};
return;
}
- formApi.setState({ schema: useTaskFormSchema(formApi, workOrderQuantity) });
- // 鍔犺浇鏁版嵁
const data = modalApi.getData<{
colorCode?: string;
id?: number;
@@ -83,30 +83,46 @@
routeId?: number;
workOrderId?: number;
}>();
+
+ let wid: number | undefined;
+
if (data?.id) {
modalApi.lock();
try {
formData.value = await getTask(data.id);
+ wid = formData.value.workOrderId;
// 璁剧疆鍒� values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
- return;
+ } else {
+ context.value = {
+ colorCode: data.colorCode,
+ itemId: data.itemId,
+ processId: data.processId,
+ routeId: data.routeId,
+ workOrderId: data.workOrderId,
+ };
+ wid = data.workOrderId;
+ workOrderQuantity.value = data.quantity;
+ await formApi.setValues({
+ colorCode: data.colorCode || '#00AEF3',
+ duration: 1,
+ quantity: data.quantity,
+ });
}
- context.value = {
- colorCode: data.colorCode,
- itemId: data.itemId,
- processId: data.processId,
- routeId: data.routeId,
- workOrderId: data.workOrderId,
- };
- workOrderQuantity.value = data.quantity;
- await formApi.setValues({
- colorCode: data.colorCode || '#00AEF3',
- duration: 1,
- quantity: data.quantity,
- });
+
+ if (wid) {
+ try {
+ const wo = await getWorkOrder(wid);
+ feedbackRole.value = wo.feedbackRole;
+ } catch {
+ // ignore
+ }
+ }
+
+ formApi.setState({ schema: useTaskFormSchema(formApi, workOrderQuantity, feedbackRole) });
},
});
</script>
diff --git a/src/views/mes/pro/workorder/data.ts b/src/views/mes/pro/workorder/data.ts
index f62e323..85e98cf 100644
--- a/src/views/mes/pro/workorder/data.ts
+++ b/src/views/mes/pro/workorder/data.ts
@@ -1,6 +1,7 @@
import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
-import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
+import { type MesProWorkOrderApi, getWorkOrderApproveProcessList } from '#/api/mes/pro/workorder';
+import { getSimpleRoleList } from '#/api/system/role';
import type { MesProWorkOrderBomApi } from '#/api/mes/pro/workorder/bom';
import type { MesProWorkOrderProcessApi } from '#/api/mes/pro/workorder/process';
@@ -237,6 +238,56 @@
rules: 'required',
},
{
+ fieldName: 'feedbackRole',
+ label: '鎶ュ伐瑙掕壊',
+ component: 'ApiSelect',
+ componentProps: {
+ api: getSimpleRoleList,
+ disabled: headerReadonly,
+ labelField: 'name',
+ valueField: 'code',
+ mode: 'multiple',
+ placeholder: '璇烽�夋嫨鎶ュ伐瑙掕壊',
+ },
+ rules: 'selectRequired',
+ },
+ {
+ fieldName: 'feedbackApprove',
+ label: '鏄惁鎶ュ伐瀹℃壒',
+ component: 'Switch',
+ componentProps: {
+ class: '!w-auto',
+ checkedChildren: '鏄�',
+ unCheckedChildren: '鍚�',
+ disabled: headerReadonly,
+ },
+ defaultValue: false,
+ rules: 'required',
+ },
+ {
+ fieldName: 'processDefinitionKey',
+ label: '瀹℃壒娴佺▼',
+ component: 'ApiSelect',
+ componentProps: {
+ api: getWorkOrderApproveProcessList,
+ disabled: headerReadonly,
+ labelField: 'name',
+ valueField: 'key',
+ placeholder: '璇烽�夋嫨瀹℃壒娴佺▼',
+ },
+ dependencies: {
+ triggerFields: ['feedbackApprove'],
+ show: (values) => !!values.feedbackApprove,
+ rules(values) {
+ if (values.feedbackApprove) {
+ return 'selectRequired';
+ }
+ return undefined;
+ },
+ },
+ },
+
+ {
fieldName: 'remark',
label: '澶囨敞',
component: 'Textarea',
diff --git a/src/views/mes/pro/workorder/modules/form.vue b/src/views/mes/pro/workorder/modules/form.vue
index 2a51ec9..5cca3a7 100644
--- a/src/views/mes/pro/workorder/modules/form.vue
+++ b/src/views/mes/pro/workorder/modules/form.vue
@@ -127,7 +127,10 @@
if (!valid || !formData.value?.id) return;
modalApi.lock();
try {
- const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
+ const data = (await formApi.getValues()) as any;
+ if (Array.isArray(data.feedbackRole)) {
+ data.feedbackRole = data.feedbackRole.join(',');
+ }
await updateWorkOrder({ ...formData.value, ...data });
if (tempProcessList.value.length > 0) {
const processList = tempProcessList.value.map(item => ({
@@ -180,7 +183,10 @@
if (isEditable.value) {
const current = JSON.stringify(await formApi.getValues());
if (current !== originalSnapshot.value) {
- const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
+ const data = (await formApi.getValues()) as any;
+ if (Array.isArray(data.feedbackRole)) {
+ data.feedbackRole = data.feedbackRole.join(',');
+ }
await updateWorkOrder({ ...formData.value, ...data });
originalSnapshot.value = current;
}
@@ -242,7 +248,10 @@
const { valid } = await formApi.validate();
if (!valid) return;
modalApi.lock();
- const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
+ const data = (await formApi.getValues()) as any;
+ if (Array.isArray(data.feedbackRole)) {
+ data.feedbackRole = data.feedbackRole.join(',');
+ }
try {
if (formData.value?.id) {
// 缂栬緫妯″紡
@@ -306,7 +315,11 @@
modalApi.lock();
try {
formData.value = await getWorkOrder(data.id);
- await formApi.setValues(formData.value);
+ const formValues: any = { ...formData.value };
+ if (formValues.feedbackRole && typeof formValues.feedbackRole === 'string') {
+ formValues.feedbackRole = formValues.feedbackRole.split(',');
+ }
+ await formApi.setValues(formValues);
currentProductId.value = formData.value.productId;
} finally {
modalApi.unlock();
diff --git a/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue b/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue
index 8620c70..0372990 100644
--- a/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue
+++ b/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue
@@ -3,12 +3,14 @@
import type { MesProFeedbackApi } from '#/api/mes/pro/feedback';
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
+import { useRouter } from 'vue-router';
import {
DICT_TYPE,
MesProFeedbackStatusEnum,
} from '@vben/constants';
import { useVbenModal } from '@vben/common-ui';
+import { useAccess } from '@vben/access';
import { Button, message, Popconfirm, Space } from 'ant-design-vue';
@@ -40,6 +42,9 @@
connectedComponent: FeedbackForm,
destroyOnClose: true,
});
+
+const { hasAccessByCodes } = useAccess();
+const router = useRouter();
const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>();
const approvingRow = ref<MesProFeedbackApi.Feedback>();
@@ -74,7 +79,7 @@
/** 鎻愪氦锛堜粎鑽夌锛� */
async function handleSubmit(row: MesProFeedbackApi.Feedback) {
- await submitFeedback(row.id!);
+ await submitFeedback({ id: row.id! });
message.success('鎶ュ伐鍗曞凡鎻愪氦');
refreshGrid();
emit('reported');
@@ -108,6 +113,15 @@
message.success('鎶ュ伐鍗曞凡椹冲洖');
refreshGrid();
emit('reported');
+}
+
+/** 鏌ョ湅瀹℃壒璇︽儏 */
+function handleViewProcess(row: MesProFeedbackApi.Feedback) {
+ if (!row.processInstanceId) {
+ message.warning('璇ユ姤宸ュ皻鏈彁浜ゅ鎵�');
+ return;
+ }
+ router.push({ path: '/bpm/process-instance/detail', query: { id: row.processInstanceId } });
}
/** 鐘舵�佸父閲忎緵妯℃澘浣跨敤 */
@@ -274,9 +288,18 @@
鍒犻櫎
</Button>
</Popconfirm>
- <!-- 瀹℃壒涓細瀹℃壒閫氳繃銆侀┏鍥� -->
- <Popconfirm
+ <!-- 瀹℃壒涓細鏌ョ湅瀹℃壒 -->
+ <Button
v-if="row.status === STATUS_APPROVING"
+ type="link"
+ size="small"
+ @click="handleViewProcess(row)"
+ >
+ 鏌ョ湅瀹℃壒
+ </Button>
+ <!--
+ <Popconfirm
+ v-if="row.status === STATUS_APPROVING && hasAccessByCodes(['mes:pro-feedback:approve'])"
title="纭瀹℃壒閫氳繃璇ユ姤宸ュ崟锛�"
@confirm="handleApprove(row)"
>
@@ -285,7 +308,7 @@
</Button>
</Popconfirm>
<Popconfirm
- v-if="row.status === STATUS_APPROVING"
+ v-if="row.status === STATUS_APPROVING && hasAccessByCodes(['mes:pro-feedback:approve'])"
title="纭椹冲洖璇ユ姤宸ュ崟锛�"
@confirm="handleReject(row)"
>
@@ -293,6 +316,7 @@
椹冲洖
</Button>
</Popconfirm>
+ -->
</Space>
</template>
</Grid>
diff --git a/src/views/mes/workbench/components/tabs/FeedbackTab.vue b/src/views/mes/workbench/components/tabs/FeedbackTab.vue
index 8439b83..f867d68 100644
--- a/src/views/mes/workbench/components/tabs/FeedbackTab.vue
+++ b/src/views/mes/workbench/components/tabs/FeedbackTab.vue
@@ -18,7 +18,7 @@
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import {
approveFeedback,
- createFeedback,
+ createFeedback, getBatchRequirements,
getFeedback,
getFeedbackPage,
rejectFeedback,
@@ -29,7 +29,10 @@
import { $t } from '#/locales';
import { useFormSchema } from '../../../pro/feedback/data';
-
+import BatchInputDialog from '../../../pro/feedback/modules/batch-input-dialog.vue';
+const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>();
+const batchActionType = ref<'submit' | 'approve'>('');
+const pendingSubmitMode = ref<'saveAndSubmit' | 'submit'>('submit');
defineOptions({ name: 'WorkbenchFeedbackTab' });
const props = defineProps<{
@@ -131,9 +134,9 @@
itemSpecification: task.itemSpecification,
unitMeasureName: task.unitMeasureName,
scheduledQuantity: task.quantity,
+ feedbackApprove: task.feedbackApprove,
checkFlag,
});
-
originalSnapshot.value = JSON.stringify(await getAlignedData());
}
@@ -168,6 +171,7 @@
if (props.task) {
await resetForTask(props.task);
}
+
emit('reported');
}
@@ -203,8 +207,12 @@
return;
}
+ pendingSubmitMode.value = 'saveAndSubmit';
+
// 4. 璋冪敤鎻愪氦鎺ュ彛
- await submitFeedback(record.id!);
+ const isDone = await handleOnSubmit(feedbackId.value!,props.task.feedbackApprove);
+ if (!isDone) return;
+
feedbackId.value = record.id;
feedbackStatus.value = record.status;
@@ -232,13 +240,60 @@
await updateFeedback(data);
originalSnapshot.value = JSON.stringify(data);
}
- await submitFeedback(feedbackId.value!);
- message.success('鎶ヨ鍗曞凡鎻愪氦');
+
+ pendingSubmitMode.value = 'submit';
+ const isDone = await handleOnSubmit(feedbackId.value!,props.task.feedbackApprove);
+ if (!isDone) return;
+
+ message.success('鎶ュ伐鍗曞凡鎻愪氦');
// 閲嶇疆琛ㄥ崟涓烘柊寤烘ā寮�
if (props.task) {
await resetForTask(props.task);
}
emit('reported');
+}
+
+
+/** 鎻愪氦鏂规硶 */
+async function handleOnSubmit(id:any,feedbackRate:any): Promise<boolean> {
+ if (feedbackRate&&feedbackRate==true){
+ await submitFeedback({ id: feedbackId.value! });
+ return true;
+ }
+
+ const checkResult = await getBatchRequirements(id);
+ if (checkResult?.needBatchInput) {
+ batchActionType.value = 'submit';
+ batchDialogRef.value?.open({ checkResult });
+ return false;
+ } else {
+ await submitFeedback({ id: feedbackId.value! });
+ return true;
+ }
+}
+
+/** 鎵规纭鍥炶皟 */
+async function handleBatchConfirm(batchData: Record<string, unknown>) {
+ if (batchActionType.value === 'submit') {
+ await submitFeedback({ id: feedbackId.value!, ...batchData });
+
+ if (pendingSubmitMode.value === 'saveAndSubmit') {
+ const data = await getAlignedData();
+ const code = data.code as string;
+ Modal.success({
+ title: '淇濆瓨骞舵彁浜ゆ垚鍔�',
+ content: `鎶ュ伐鍗� ${code} 宸叉彁浜,
+ centered: true,
+ });
+ } else {
+ message.success('鎶ュ伐鍗曞凡鎻愪氦');
+ }
+
+ if (props.task) {
+ await resetForTask(props.task);
+ }
+ emit('reported');
+ }
}
/** 瀹℃壒閫氳繃 */
@@ -294,6 +349,7 @@
<template>
<div class="feedback-tab">
+ <BatchInputDialog ref="batchDialogRef" @confirm="handleBatchConfirm" />
<div v-if="!task" class="feedback-tab__empty">
璇蜂粠宸︿晶宸ュ崟鍒楄〃閫夋嫨涓�鏉′换鍔�
</div>
diff --git a/src/views/mes/workbench/index.vue b/src/views/mes/workbench/index.vue
index b49191b..3661475 100644
--- a/src/views/mes/workbench/index.vue
+++ b/src/views/mes/workbench/index.vue
@@ -17,6 +17,7 @@
} from '#/api/mes/pro/workrecord';
import { getPendingFeedbackPage } from '#/api/mes/pro/task';
import { router } from '#/router';
+import { useAccess } from '@vben/access';
import TaskListPanel from './components/TaskListPanel.vue';
import WorkstationPanel from './components/WorkstationPanel.vue';
@@ -33,16 +34,36 @@
// --- Tab 閰嶇疆 ---
const tabItems: WorkbenchTabItem[] = [
{ key: 'detail', label: '宸ュ崟璇︽儏', icon: 'lucide:file-text' },
- { key: 'feedback', label: '鎶ュ伐', icon: 'lucide:edit-3' },
- { key: 'history', label: '鎶ュ伐璁板綍', icon: 'lucide:history' },
+ { key: 'feedback', label: '鎶ュ伐', icon: 'lucide:edit-3', auth: ['mes:pro-feedback:create'] },
+ { key: 'history', label: '鎶ュ伐璁板綍', icon: 'lucide:history' ,auth: ['mes:pro-feedback:query']},
{ key: 'record-param', label: '鐢熶骇璁板綍鍙傛暟', icon: 'lucide:file-cog' },
{ key: 'product-issue', label: '棰嗘枡', icon: 'lucide:clipboard-list' },
{ key: 'return-issue', label: '閫�鏂�', icon: 'lucide:rotate-ccw' },
];
+const { hasAccessByCodes } = useAccess();
+
+const availableTabs = computed(() => {
+ return tabItems.filter((tab) => {
+ if (!tab.auth || tab.auth.length === 0) return true;
+ return hasAccessByCodes(tab.auth);
+ });
+});
+
const activeTab = ref<WorkbenchTabKey>('detail');
const activeTabLabel = computed(
- () => tabItems.find((t) => t.key === activeTab.value)?.label ?? '',
+ () => availableTabs.value.find((t) => t.key === activeTab.value)?.label ?? '',
+);
+
+// 褰撳彲鐢� tab 鍙樺寲鏃讹紝濡傛灉褰撳墠閫変腑鐨� tab 涓嶅彲鐢紝鍒欒嚜鍔ㄩ�夋嫨绗竴涓彲鐢ㄧ殑 tab
+watch(
+ availableTabs,
+ (tabs) => {
+ if (tabs.length > 0 && !tabs.find((t) => t.key === activeTab.value)) {
+ activeTab.value = tabs[0]!.key;
+ }
+ },
+ { immediate: true },
);
// --- 宸ヤ綔绔� ---
@@ -176,7 +197,7 @@
<nav class="workbench-nav">
<button
- v-for="tab in tabItems"
+ v-for="tab in availableTabs"
:key="tab.key"
type="button"
class="workbench-nav__item"
diff --git a/src/views/mes/workbench/types.ts b/src/views/mes/workbench/types.ts
index 8f74ad3..af0dd1c 100644
--- a/src/views/mes/workbench/types.ts
+++ b/src/views/mes/workbench/types.ts
@@ -8,6 +8,7 @@
key: WorkbenchTabKey;
label: string;
icon: string;
+ auth?: string[];
}
/** 寰呮姤宸ヤ换鍔★紙浠� API 绫诲瀷閲嶅鍑猴紝渚夸簬缁勪欢寮曠敤锛� */
--
Gitblit v1.9.3