From 5f3dea3fd930afde720677fba10b6bfb2e836a18 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期五, 24 七月 2026 09:54:17 +0800
Subject: [PATCH] Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0
---
vite.config.ts | 15 ++
pnpm-workspace.yaml | 8
src/views/mes/pro/workorder/modules/form.vue | 6
package.json | 1
src/views/mes/pro/workorder/modules/process-list.vue | 201 ++++++++++++++++++++++++++++++++-------
src/views/mes/pro/workorder/data.ts | 4
pnpm-lock.yaml | 12 +
7 files changed, 196 insertions(+), 51 deletions(-)
diff --git a/package.json b/package.json
index 4dbcf6f..bbc683e 100644
--- a/package.json
+++ b/package.json
@@ -63,6 +63,7 @@
"highlight.js": "catalog:",
"livekit-client": "^2.20.0",
"pinia": "catalog:",
+ "sortablejs": "catalog:",
"steady-xml": "catalog:",
"tinymce": "catalog:",
"tyme4ts": "catalog:",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e317c5f..b02d913 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -647,6 +647,9 @@
pinia:
specifier: 'catalog:'
version: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))
+ sortablejs:
+ specifier: 'catalog:'
+ version: 1.15.7
steady-xml:
specifier: 'catalog:'
version: 0.1.0
@@ -7724,8 +7727,8 @@
orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
- own-keys@1.0.1:
- resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+ own-keys@1.0.2:
+ resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==}
engines: {node: '>= 0.4'}
oxfmt@0.56.0:
@@ -14218,7 +14221,7 @@
object-inspect: 1.13.4
object-keys: 1.1.1
object.assign: 4.1.7
- own-keys: 1.0.1
+ own-keys: 1.0.2
regexp.prototype.flags: 1.5.4
safe-array-concat: 1.1.4
safe-push-apply: 1.0.0
@@ -16099,8 +16102,9 @@
orderedmap@2.1.1: {}
- own-keys@1.0.1:
+ own-keys@1.0.2:
dependencies:
+ call-bound: 1.0.4
get-intrinsic: 1.3.0
object-keys: 1.1.1
safe-push-apply: 1.0.0
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index ad49499..5df4a75 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -210,10 +210,10 @@
allowBuilds:
'@carbon/icons': true
'@parcel/watcher': true
- '@vue-office/docx': set this to true or false
- '@vue-office/excel': set this to true or false
- '@vue-office/pdf': set this to true or false
- '@vue-office/pptx': set this to true or false
+ '@vue-office/docx': true
+ '@vue-office/excel': true
+ '@vue-office/pdf': true
+ '@vue-office/pptx': true
core-js: true
core-js-pure: true
esbuild: true
diff --git a/src/views/mes/pro/workorder/data.ts b/src/views/mes/pro/workorder/data.ts
index fcac639..f277918 100644
--- a/src/views/mes/pro/workorder/data.ts
+++ b/src/views/mes/pro/workorder/data.ts
@@ -747,7 +747,6 @@
field: 'drag',
title: '',
width: 40,
- fixed: 'left',
slots: { default: 'dragHandle' },
} as const,
]
@@ -755,6 +754,7 @@
{
type: 'expand',
width: 50,
+ slots: { content: 'expand_content' },
} as const,
{
field: 'sort',
@@ -835,7 +835,7 @@
},
{
title: '鎿嶄綔',
- width: 120,
+ width: 200,
fixed: 'right',
slots: { default: 'actions' },
},
diff --git a/src/views/mes/pro/workorder/modules/form.vue b/src/views/mes/pro/workorder/modules/form.vue
index dced20c..891a31e 100644
--- a/src/views/mes/pro/workorder/modules/form.vue
+++ b/src/views/mes/pro/workorder/modules/form.vue
@@ -332,7 +332,8 @@
<template>
<Modal :title="getTitle"
- class="w-3/5">
+ class="w-3/5"
+ content-class="!max-h-[80vh] overflow-y-auto">
<Form class="mx-4" />
<!-- BOM / 宸ュ簭 / 鐗╂枡闇�姹� Tab -->
<template v-if="showTabs">
@@ -350,7 +351,8 @@
</div>
</Tabs.TabPane>
<Tabs.TabPane key="process"
- tab="宸ュ簭">
+ tab="宸ュ簭"
+ forceRender>
<ProcessList
ref="processListRef"
:form-type="formType"
diff --git a/src/views/mes/pro/workorder/modules/process-list.vue b/src/views/mes/pro/workorder/modules/process-list.vue
index f5b0cdc..fd2a7e7 100644
--- a/src/views/mes/pro/workorder/modules/process-list.vue
+++ b/src/views/mes/pro/workorder/modules/process-list.vue
@@ -11,8 +11,10 @@
import { MesProWorkOrderStatusEnum } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
-import { useSortable } from '@vueuse/integrations/useSortable';
-import { message, Tag } from 'ant-design-vue';
+import type { MesMdItemApi } from '#/api/mes/md/item';
+
+import Sortable from 'sortablejs';
+import { Button, InputNumber, message, Modal, Tag } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
@@ -21,6 +23,7 @@
} from '#/api/mes/pro/workorder/process';
import { getRouteProcessListByProduct } from '#/api/mes/pro/route/process';
import { $t } from '#/locales';
+import { MdItemSelectDialog } from '#/views/mes/md/item/components';
import { useProcessGridColumns } from '../data';
import ProcessForm from './process-form.vue';
@@ -47,8 +50,16 @@
const isCreateMode = computed(() => props.formType === 'create');
-// 鎷栨嫿鎺掑簭
-const sortableInstance = ref<any>(null);
+// 鎷栨嫿鎺掑簭瀹炰緥
+let sortableInstance: Sortable | null = null;
+
+// 鐗╂枡鎶曞叆
+const itemSelectDialogRef = ref<InstanceType<typeof MdItemSelectDialog>>();
+const currentMaterialRow = ref<MesProWorkOrderProcessApi.WorkOrderProcess>();
+
+// 鐢ㄦ枡姣斾緥寮圭獥
+const quantityModalOpen = ref(false);
+const pendingItems = ref<(MesMdItemApi.Item & { quantity: number })[]>([]);
const [ProcessFormModal, processFormModalApi] = useVbenModal({
connectedComponent: ProcessForm,
@@ -65,51 +76,57 @@
pagerConfig: { enabled: false },
rowConfig: { isHover: true, keyField: 'processId' },
showOverflow: true,
+ expandConfig: {
+ padding: true,
+ },
toolbarConfig: { enabled: false },
} as VxeTableGridOptions<MesProWorkOrderProcessApi.WorkOrderProcess>,
});
-/** 鍒濆鍖�/閿�姣佹嫋鎷芥帓搴� */
+/** 鍒濆鍖栨嫋鎷芥帓搴� */
async function initSortable() {
destroySortable();
if (!isEditable.value || list.value.length === 0) return;
await nextTick();
- sortableInstance.value = useSortable(
- '.workorder-process-list .vxe-table--body-wrapper:not(.fixed-right--wrapper) .vxe-table--body tbody',
- list.value,
- {
- animation: 150,
- draggable: '.vxe-body--row',
- handle: '.drag-handle',
- onEnd: ({ newDraggableIndex, oldDraggableIndex }) => {
- if (
- newDraggableIndex !== undefined &&
- oldDraggableIndex !== undefined &&
- oldDraggableIndex !== newDraggableIndex
- ) {
- // 閲嶆柊鎺掑簭鏁扮粍
- list.value.splice(
- newDraggableIndex,
- 0,
- list.value.splice(oldDraggableIndex, 1)[0]!,
- );
- // 鏇存柊 sort 瀛楁
- list.value.forEach((item, index) => {
- item.sort = index + 1;
- });
- gridApi.setGridOptions({ data: list.value });
- emit('processListChange', list.value);
- }
- },
- },
+ const el = document.querySelector<HTMLElement>(
+ '.workorder-process-list .vxe-table--body-wrapper.body--wrapper .vxe-table--body tbody',
);
+ if (!el) return;
+ sortableInstance = Sortable.create(el, {
+ animation: 150,
+ draggable: '.vxe-body--row',
+ handle: '.drag-handle',
+ onEnd: (evt) => {
+ const { oldIndex, newIndex, item } = evt;
+ if (
+ oldIndex === undefined ||
+ newIndex === undefined ||
+ oldIndex === newIndex
+ ) {
+ return;
+ }
+ // 杩樺師 DOM锛歋ortable 宸茬Щ鍔ㄨ妭鐐癸紝闇�鏀惧洖鍘熶綅锛岀敱鏁版嵁椹卞姩閲嶆覆鏌�
+ if (item && oldIndex < el.children.length) {
+ const refNode = el.children[oldIndex] || null;
+ el.insertBefore(item, refNode);
+ }
+ // 鏇存柊鏁版嵁椤哄簭
+ list.value.splice(newIndex, 0, list.value.splice(oldIndex, 1)[0]!);
+ // 鏇存柊 sort 瀛楁
+ list.value.forEach((p, index) => {
+ p.sort = index + 1;
+ });
+ gridApi.setGridOptions({ data: list.value });
+ emit('processListChange', list.value);
+ },
+ });
}
/** 閿�姣佹嫋鎷芥帓搴忓疄渚� */
function destroySortable() {
- if (sortableInstance.value) {
- sortableInstance.value.stop?.();
- sortableInstance.value = null;
+ if (sortableInstance) {
+ sortableInstance.destroy();
+ sortableInstance = null;
}
}
@@ -230,6 +247,81 @@
}
}
+/** 鎵撳紑鏂板鐗╂枡寮圭獥 */
+function handleMaterialInput(row: MesProWorkOrderProcessApi.WorkOrderProcess) {
+ currentMaterialRow.value = row;
+ const selectedIds = (row.bomItems || [])
+ .map((item) => item.itemId)
+ .filter((id): id is number => id !== undefined);
+ itemSelectDialogRef.value?.open(selectedIds, { multiple: true });
+}
+
+/** 鐗╂枡閫変腑鍥炶皟锛氬幓閲嶅悗鎵撳紑鐢ㄦ枡姣斾緥濉啓寮圭獥 */
+function handleItemSelected(rows: MesMdItemApi.Item[]) {
+ const row = currentMaterialRow.value;
+ if (!row || rows.length === 0) return;
+ const existingIds = new Set(
+ (row.bomItems || [])
+ .map((item) => item.itemId)
+ .filter((id): id is number => id !== undefined),
+ );
+ const newItems = rows.filter((item) => item.id && !existingIds.has(item.id));
+ if (newItems.length === 0) {
+ message.warning('鎵�閫夌墿鏂欏潎宸插瓨鍦�');
+ return;
+ }
+ pendingItems.value = newItems.map((item) => ({ ...item, quantity: 1 }));
+ quantityModalOpen.value = true;
+}
+
+/** 纭鐢ㄦ枡姣斾緥锛屾坊鍔犲埌 bomItems */
+function handleQuantityConfirm() {
+ const row = currentMaterialRow.value;
+ if (!row) return;
+ // 鍦� list 涓壘鍒板搴旇骞舵浛鎹紝纭繚鍝嶅簲寮忔洿鏂�
+ const rowIndex = list.value.findIndex(
+ (item) => item.processId === row.processId,
+ );
+ if (rowIndex === -1) return;
+ const target = list.value[rowIndex]!;
+ const newBomItems = [
+ ...(target.bomItems || []),
+ ...pendingItems.value.map((item) => ({
+ itemId: item.id,
+ itemCode: item.code,
+ itemName: item.name,
+ quantity: item.quantity,
+ remark: undefined,
+ })),
+ ];
+ // 鍒涘缓鏂扮殑 row 瀵硅薄鍜屾柊鐨� list 鏁扮粍锛屽己鍒跺搷搴斿紡鏇存柊
+ list.value[rowIndex] = { ...target, bomItems: newBomItems };
+ quantityModalOpen.value = false;
+ pendingItems.value = [];
+ gridApi.setGridOptions({ data: list.value });
+ emit('processListChange', list.value);
+ message.success('鐗╂枡娣诲姞鎴愬姛');
+}
+
+/** 鍒犻櫎鎶曟枡鏄庣粏 */
+function handleRemoveBomItem(
+ row: MesProWorkOrderProcessApi.WorkOrderProcess,
+ index: number,
+) {
+ const rowIndex = list.value.findIndex(
+ (item) => item.processId === row.processId,
+ );
+ if (rowIndex === -1) return;
+ const target = list.value[rowIndex]!;
+ if (!target.bomItems) return;
+ list.value[rowIndex] = {
+ ...target,
+ bomItems: target.bomItems.filter((_, i) => i !== index),
+ };
+ gridApi.setGridOptions({ data: list.value });
+ emit('processListChange', list.value);
+}
+
// 缁勪欢鍗歌浇鏃堕攢姣佹帓搴忓疄渚�
onUnmounted(() => {
destroySortable();
@@ -248,6 +340,32 @@
<template>
<div class="workorder-process-list">
<ProcessFormModal @success="handleFormSuccess" />
+ <MdItemSelectDialog ref="itemSelectDialogRef" @selected="handleItemSelected" />
+ <Modal
+ v-model:open="quantityModalOpen"
+ title="濉啓鐢ㄦ枡姣斾緥"
+ width="500px"
+ @ok="handleQuantityConfirm"
+ >
+ <div class="space-y-3 py-2">
+ <div
+ v-for="(item, idx) in pendingItems"
+ :key="item.id"
+ class="flex items-center gap-3"
+ >
+ <span class="shrink-0 text-sm text-gray-700">
+ {{ item.name || item.code }}
+ </span>
+ <InputNumber
+ v-model:value="item.quantity"
+ :min="0.01"
+ :precision="2"
+ class="!w-32"
+ />
+ <span class="text-xs text-gray-400">鐢ㄩ噺姣斾緥</span>
+ </div>
+ </div>
+ </Modal>
<div v-if="isEditable" class="mb-3 flex items-center justify-start">
<TableAction
:actions="[
@@ -282,6 +400,11 @@
<TableAction
:actions="[
{
+ label: '鏂板鐗╂枡',
+ type: 'link',
+ onClick: handleMaterialInput.bind(null, row),
+ },
+ {
label: $t('common.edit'),
type: 'link',
ifShow: isEditable,
@@ -300,7 +423,7 @@
]"
/>
</template>
- <template #expand="{ row }">
+ <template #expand_content="{ row }">
<div
v-if="row.bomItems && row.bomItems.length > 0"
class="bg-gray-50 px-8 py-3"
@@ -310,7 +433,9 @@
<Tag
v-for="(bom, idx) in row.bomItems"
:key="idx"
+ closable
color="blue"
+ @close="handleRemoveBomItem(row, idx)"
>
{{ bom.itemName || bom.itemCode }}
<span class="text-gray-400 ml-1">脳{{ bom.quantity }}</span>
@@ -320,4 +445,4 @@
</template>
</Grid>
</div>
-</template>
\ No newline at end of file
+</template>
diff --git a/vite.config.ts b/vite.config.ts
index 5a07335..dcf9f5b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,14 +1,24 @@
+import { resolve } from "node:path";
import { defineConfig } from "./internal/vite-config/src";
export default defineConfig(async () => {
return {
application: {},
vite: {
+ resolve: {
+ alias: {
+ '@vue-office/pdf': resolve(__dirname, 'node_modules/@vue-office/pdf/lib/v3/vue-office-pdf.mjs'),
+ '@vue-office/docx': resolve(__dirname, 'node_modules/@vue-office/docx/lib/v3/vue-office-docx.mjs'),
+ '@vue-office/excel': resolve(__dirname, 'node_modules/@vue-office/excel/lib/v3/vue-office-excel.mjs'),
+ '@vue-office/excel-css': resolve(__dirname, 'node_modules/@vue-office/excel/lib/v3/index.css'),
+ '@vue-office/pptx': resolve(__dirname, 'node_modules/@vue-office/pptx/lib/v3/vue-office-pptx.mjs'),
+ },
+ },
oxc: {
enable: false,
},
optimizeDeps: {
- include: ["vue", "vue-router", "pinia", "ant-design-vue", "dayjs", "@vueuse/core", "axios"],
+ include: ["vue", "vue-router", "pinia", "ant-design-vue", "dayjs", "@vueuse/core", "axios", "@vue-office/pdf", "@vue-office/docx", "@vue-office/excel", "@vue-office/pptx"],
exclude: ["dhtmlx-gantt", "tinymce", "bpmn-js", "bpmn-js-properties-panel", "video.js"],
},
build: {
@@ -28,6 +38,9 @@
if (["tinymce", "@tinymce/tinymce-vue", "@form-create/ant-design-vue"].some(dep => id.includes(dep))) {
return "vendor-editor";
}
+ if (["@vue-office"].some(dep => id.includes(dep))) {
+ return "vendor-office-preview";
+ }
}
},
},
--
Gitblit v1.9.3