From 92f48fea04ed14d188652afa6bf11f3d6f940b5c Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 16 七月 2026 10:42:33 +0800
Subject: [PATCH] Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0
---
pnpm-workspace.yaml | 4
src/components/file-preview/file-preview-modal.vue | 169 ++++++++++++++++++
src/views/bpm/processInstance/index.vue | 23 +
src/views/ai/chat/index/modules/message/files.vue | 17 +
src/views/wls/arrivalnotice/modules/line-list.vue | 24 ++
src/views/bpm/processInstance/detail/modules/operation-button.vue | 13 +
src/views/mes/qc/iqc/data.ts | 6
src/components/file-preview/typing.ts | 10 +
src/views/wls/arrivalnotice/data.ts | 1
pnpm-lock.yaml | 81 ++++++++
src/components/file-preview/index.ts | 3
src/views/mes/qc/oqc/data.ts | 6
src/views/erp/purchase/order/data.ts | 18 +
src/components/file-preview/use-file-preview.ts | 34 +++
src/views/erp/purchase/order/modules/item-form.vue | 13 +
src/router/routes/modules/bpm.ts | 39 ++--
package.json | 4
src/packages/@core/base/shared/src/utils/upload.ts | 28 +++
src/views/mes/qc/rqc/data.ts | 6
src/api/erp/purchase/order/index.ts | 1
src/types/vue-office.d.ts | 33 +++
21 files changed, 486 insertions(+), 47 deletions(-)
diff --git a/package.json b/package.json
index 1d29683..109e0d2 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,10 @@
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@videojs-player/vue": "catalog:",
+ "@vue-office/docx": "^1.6.3",
+ "@vue-office/excel": "^1.7.14",
+ "@vue-office/pdf": "^2.0.10",
+ "@vue-office/pptx": "^1.0.1",
"@vueuse/core": "catalog:",
"@vueuse/integrations": "catalog:",
"ant-design-vue": "catalog:",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 97d3ddd..bc9f97f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -590,6 +590,18 @@
'@videojs-player/vue':
specifier: 'catalog:'
version: 1.0.0(@types/video.js@7.3.58)(video.js@7.21.7)(vue@3.5.38(typescript@6.0.3))
+ '@vue-office/docx':
+ specifier: ^1.6.3
+ version: 1.6.3(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))
+ '@vue-office/excel':
+ specifier: ^1.7.14
+ version: 1.7.14(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))
+ '@vue-office/pdf':
+ specifier: ^2.0.10
+ version: 2.0.10(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))
+ '@vue-office/pptx':
+ specifier: ^1.0.1
+ version: 1.0.1(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))
'@vueuse/core':
specifier: 'catalog:'
version: 14.3.0(vue@3.5.38(typescript@6.0.3))
@@ -4624,6 +4636,46 @@
vue:
optional: true
+ '@vue-office/docx@1.6.3':
+ resolution: {integrity: sha512-Cs+3CAaRBOWOiW4XAhTwwxJ0dy8cPIf6DqfNvYcD3YACiLwO4kuawLF2IAXxyijhbuOeoFsfvoVbOc16A/4bZA==}
+ peerDependencies:
+ '@vue/composition-api': ^1.7.1
+ vue: ^2.0.0 || >=3.0.0
+ vue-demi: ^0.14.6
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ '@vue-office/excel@1.7.14':
+ resolution: {integrity: sha512-pVUgt+emDQUnW7q22CfnQ+jl43mM/7IFwYzOg7lwOwPEbiVB4K4qEQf+y/bc4xGXz75w1/e3Kz3G6wAafmFBFg==}
+ peerDependencies:
+ '@vue/composition-api': ^1.7.1
+ vue: ^2.0.0 || >=3.0.0
+ vue-demi: ^0.14.6
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ '@vue-office/pdf@2.0.10':
+ resolution: {integrity: sha512-yHVLrMAKpMPBkhBwofFyGEtEeJF0Zd7oGmf56Pe5aj/xObdRq3E1CIZqTqhWJNgHV8oLQqaX0vs4p5T1zq+GIA==}
+ peerDependencies:
+ '@vue/composition-api': ^1.7.1
+ vue: ^2.0.0 || >=3.0.0
+ vue-demi: ^0.14.6
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ '@vue-office/pptx@1.0.1':
+ resolution: {integrity: sha512-+V7Kctzl6f6+Yk4NaD/wQGRIkqLWcowe0jEhPexWQb8Oilbzt1OyhWRWcMsxNDTdrgm6aMLP+0/tmw27cxddMg==}
+ peerDependencies:
+ '@vue/composition-api': ^1.7.1
+ vue: ^2.0.0 || >=3.0.0
+ vue-demi: ^0.14.6
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
'@vue/babel-helper-vue-transform-on@1.1.5':
resolution: {integrity: sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==}
@@ -6674,6 +6726,10 @@
ignore@7.0.5:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.6:
+ resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==}
engines: {node: '>= 4'}
immutable@5.1.7:
@@ -11532,7 +11588,7 @@
destr: 2.0.5
errx: 0.1.0
exsolve: 1.1.0
- ignore: 7.0.5
+ ignore: 7.0.6
jiti: 2.7.0
klona: 2.0.6
knitwork: 1.3.0
@@ -12689,6 +12745,26 @@
unplugin-utils: 0.3.1
optionalDependencies:
vue: 3.5.38(typescript@6.0.3)
+
+ '@vue-office/docx@1.6.3(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))':
+ dependencies:
+ vue: 3.5.38(typescript@6.0.3)
+ vue-demi: 0.14.10(vue@3.5.38(typescript@6.0.3))
+
+ '@vue-office/excel@1.7.14(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))':
+ dependencies:
+ vue: 3.5.38(typescript@6.0.3)
+ vue-demi: 0.14.10(vue@3.5.38(typescript@6.0.3))
+
+ '@vue-office/pdf@2.0.10(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))':
+ dependencies:
+ vue: 3.5.38(typescript@6.0.3)
+ vue-demi: 0.14.10(vue@3.5.38(typescript@6.0.3))
+
+ '@vue-office/pptx@1.0.1(vue-demi@0.14.10(vue@3.5.38(typescript@6.0.3)))(vue@3.5.38(typescript@6.0.3))':
+ dependencies:
+ vue: 3.5.38(typescript@6.0.3)
+ vue-demi: 0.14.10(vue@3.5.38(typescript@6.0.3))
'@vue/babel-helper-vue-transform-on@1.1.5': {}
@@ -15005,6 +15081,9 @@
ignore@7.0.5: {}
+ ignore@7.0.6:
+ optional: true
+
immutable@5.1.7: {}
import-fresh@3.3.0:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index f01f48f..ad49499 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -210,6 +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
core-js: true
core-js-pure: true
esbuild: true
diff --git a/src/api/erp/purchase/order/index.ts b/src/api/erp/purchase/order/index.ts
index 02becec..1a75e5a 100644
--- a/src/api/erp/purchase/order/index.ts
+++ b/src/api/erp/purchase/order/index.ts
@@ -50,6 +50,7 @@
totalTaxPrice?: number; // 鍚◣鎬讳环锛屽崟浣嶏細鍏�
remark?: string; // 澶囨敞
stockCount?: number; // 搴撳瓨鏁伴噺锛堟樉绀哄瓧娈碉級
+ qcCheckFlag?: boolean; // 鏄惁闇�瑕佹潵鏂欐楠�
}
}
diff --git a/src/components/file-preview/file-preview-modal.vue b/src/components/file-preview/file-preview-modal.vue
new file mode 100644
index 0000000..e6732e0
--- /dev/null
+++ b/src/components/file-preview/file-preview-modal.vue
@@ -0,0 +1,169 @@
+<script lang="ts" setup>
+import type { PreviewFileType } from '@vben/utils';
+
+import { computed, defineAsyncComponent, ref, watch } from 'vue';
+
+import { getFileIcon } from '@vben/utils';
+
+import { Button, Modal, Spin } from 'ant-design-vue';
+
+import { IconifyIcon } from '@vben/icons';
+
+const VueOfficePdf = defineAsyncComponent(() => import('@vue-office/pdf'));
+const VueOfficeDocx = defineAsyncComponent(() => import('@vue-office/docx'));
+const VueOfficeExcel = defineAsyncComponent(() => import('@vue-office/excel'));
+const VueOfficePptx = defineAsyncComponent(() => import('@vue-office/pptx'));
+
+// @vue-office/excel 闇�瑕侀澶栧鍏� CSS
+// @vue-office/excel 闇�瑕侀澶栧鍏� CSS
+// @vue-office/excel 闇�瑕侀澶栧鍏� CSS
+import '@vue-office/excel-css';
+
+const props = withDefaults(
+ defineProps<{
+ fileUrl?: string;
+ fileName?: string;
+ visible?: boolean;
+ width?: number | string;
+ }>(),
+ {
+ fileUrl: '',
+ fileName: '',
+ visible: false,
+ width: '90vw',
+ },
+);
+
+const emit = defineEmits<{
+ 'update:visible': [value: boolean];
+}>();
+
+const loading = ref(false);
+const hasError = ref(false);
+
+const open = computed({
+ get: () => props.visible,
+ set: (val) => emit('update:visible', val),
+});
+
+const displayName = computed(() => props.fileName || '鏂囦欢棰勮');
+
+// 鏍规嵁 fileName 鎴� fileUrl 鍒ゆ柇鏂囦欢绫诲瀷
+const fileType = computed<PreviewFileType>(() => {
+ const name = props.fileName || props.fileUrl;
+ if (!name) return 'unknown';
+ const ext = name.split('.').pop()?.toLowerCase() || '';
+ if (ext === 'pdf') return 'pdf';
+ if (['doc', 'docx'].includes(ext)) return 'docx';
+ if (['xls', 'xlsx'].includes(ext)) return 'excel';
+ if (['ppt', 'pptx'].includes(ext)) return 'pptx';
+ return 'unknown';
+});
+
+const canPreview = computed(
+ () => ['pdf', 'docx', 'excel', 'pptx'].includes(fileType.value),
+);
+
+watch(open, (val) => {
+ if (val) {
+ hasError.value = false;
+ loading.value = canPreview.value;
+ }
+});
+
+function onRendered() {
+ loading.value = false;
+}
+
+function onError() {
+ loading.value = false;
+ hasError.value = true;
+}
+
+function openInNewTab() {
+ if (props.fileUrl) {
+ window.open(props.fileUrl, '_blank');
+ }
+}
+</script>
+
+<template>
+ <Modal
+ v-model:open="open"
+ :title="displayName"
+ :width="width"
+ :footer="null"
+ destroy-on-close
+ centered
+ :style="{ top: '20px' }"
+ :body-style="{ height: '80vh', overflow: 'auto' }"
+ >
+ <!-- Loading -->
+ <div
+ v-if="loading"
+ class="flex h-full items-center justify-center"
+ >
+ <Spin size="large" tip="鍔犺浇涓�..." />
+ </div>
+
+ <!-- Error -->
+ <div
+ v-else-if="hasError"
+ class="flex h-full flex-col items-center justify-center"
+ >
+ <IconifyIcon icon="lucide:file-x" :size="64" class="text-red-400" />
+ <p class="mt-4 text-gray-500">鏂囦欢棰勮澶辫触</p>
+ <Button type="primary" class="mt-4" @click="openInNewTab">
+ 鍦ㄦ柊鏍囩椤垫墦寮�
+ </Button>
+ </div>
+
+ <!-- Unsupported -->
+ <div
+ v-else-if="!canPreview"
+ class="flex h-full flex-col items-center justify-center"
+ >
+ <IconifyIcon
+ :icon="getFileIcon(fileName || fileUrl)"
+ :size="64"
+ class="text-gray-400"
+ />
+ <p class="mt-4 text-gray-500">涓嶆敮鎸侀瑙堟鏂囦欢绫诲瀷</p>
+ <Button type="primary" class="mt-4" @click="openInNewTab">
+ 涓嬭浇鏂囦欢
+ </Button>
+ </div>
+
+ <!-- PDF -->
+ <VueOfficePdf
+ v-else-if="fileType === 'pdf'"
+ :src="fileUrl"
+ @rendered="onRendered"
+ @error="onError"
+ />
+
+ <!-- Word -->
+ <VueOfficeDocx
+ v-else-if="fileType === 'docx'"
+ :src="fileUrl"
+ @rendered="onRendered"
+ @error="onError"
+ />
+
+ <!-- Excel -->
+ <VueOfficeExcel
+ v-else-if="fileType === 'excel'"
+ :src="fileUrl"
+ @rendered="onRendered"
+ @error="onError"
+ />
+
+ <!-- PowerPoint -->
+ <VueOfficePptx
+ v-else-if="fileType === 'pptx'"
+ :src="fileUrl"
+ @rendered="onRendered"
+ @error="onError"
+ />
+ </Modal>
+</template>
diff --git a/src/components/file-preview/index.ts b/src/components/file-preview/index.ts
new file mode 100644
index 0000000..2b6ee86
--- /dev/null
+++ b/src/components/file-preview/index.ts
@@ -0,0 +1,3 @@
+export { default as FilePreviewModal } from './file-preview-modal.vue';
+export { useFilePreview } from './use-file-preview';
+export type { FilePreviewModalProps, PreviewFileType } from './typing';
diff --git a/src/components/file-preview/typing.ts b/src/components/file-preview/typing.ts
new file mode 100644
index 0000000..c9653cd
--- /dev/null
+++ b/src/components/file-preview/typing.ts
@@ -0,0 +1,10 @@
+import type { PreviewFileType } from '@vben/utils';
+
+export type { PreviewFileType };
+
+export interface FilePreviewModalProps {
+ fileUrl?: string;
+ fileName?: string;
+ visible?: boolean;
+ width?: number | string;
+}
diff --git a/src/components/file-preview/use-file-preview.ts b/src/components/file-preview/use-file-preview.ts
new file mode 100644
index 0000000..ca4fec0
--- /dev/null
+++ b/src/components/file-preview/use-file-preview.ts
@@ -0,0 +1,34 @@
+import type { Ref } from 'vue';
+
+import { computed, ref } from 'vue';
+
+import { getFileNameFromUrl, getFileType } from '@vben/utils';
+
+export function useFilePreview() {
+ const visible = ref(false);
+ const fileUrl = ref('');
+ const fileName = ref('');
+
+ const fileType = computed(() => getFileType(fileName.value || fileUrl.value));
+
+ function openPreview(url: string, name?: string) {
+ fileUrl.value = url;
+ fileName.value = name || getFileNameFromUrl(url);
+ visible.value = true;
+ }
+
+ function closePreview() {
+ visible.value = false;
+ }
+
+ return {
+ closePreview,
+ openPreview,
+ previewState: {
+ fileUrl: fileUrl as Ref<string>,
+ fileName: fileName as Ref<string>,
+ fileType,
+ visible: visible as Ref<boolean>,
+ },
+ };
+}
diff --git a/src/packages/@core/base/shared/src/utils/upload.ts b/src/packages/@core/base/shared/src/utils/upload.ts
index 57718b4..e25e83a 100644
--- a/src/packages/@core/base/shared/src/utils/upload.ts
+++ b/src/packages/@core/base/shared/src/utils/upload.ts
@@ -196,6 +196,34 @@
}
/**
+ * 棰勮鏂囦欢绫诲瀷
+ */
+export type PreviewFileType =
+ | 'docx'
+ | 'excel'
+ | 'image'
+ | 'pdf'
+ | 'pptx'
+ | 'unknown';
+
+/**
+ * 鏍规嵁鏂囦欢鍚嶅垽鏂瑙堟枃浠剁被鍨�
+ *
+ * @param filename 鏂囦欢鍚嶆垨 URL
+ * @returns 棰勮鏂囦欢绫诲瀷
+ */
+export function getFileType(filename: null | string | undefined): PreviewFileType {
+ if (!filename) return 'unknown';
+ const ext = filename.split('.').pop()?.toLowerCase() || '';
+ if (isImage(ext)) return 'image';
+ if (ext === 'pdf') return 'pdf';
+ if (['doc', 'docx'].includes(ext)) return 'docx';
+ if (['xls', 'xlsx'].includes(ext)) return 'excel';
+ if (['ppt', 'pptx'].includes(ext)) return 'pptx';
+ return 'unknown';
+}
+
+/**
* 鑾峰彇鏂囦欢鍥炬爣锛圠ucide Icons锛�
*
* @param filename 鏂囦欢鍚�
diff --git a/src/router/routes/modules/bpm.ts b/src/router/routes/modules/bpm.ts
index bd4a7a2..c97f4d7 100644
--- a/src/router/routes/modules/bpm.ts
+++ b/src/router/routes/modules/bpm.ts
@@ -2,6 +2,26 @@
const routes: RouteRecordRaw[] = [
{
+ path: '/bpm/process-instance/detail',
+ component: () => import('#/views/bpm/processInstance/detail/index.vue'),
+ name: 'BpmProcessInstanceDetail',
+ meta: {
+ title: '娴佺▼璇︽儏',
+ activePath: '/bpm/task/my',
+ icon: 'ant-design:history-outlined',
+ keepAlive: false,
+ hideInMenu: true,
+ noBasicLayout: false,
+ },
+ props: (route) => {
+ return {
+ id: route.query.id,
+ taskId: route.query.taskId,
+ activityId: route.query.activityId,
+ };
+ },
+ },
+ {
path: '/bpm',
name: 'bpm',
meta: {
@@ -9,25 +29,6 @@
hideInMenu: true,
},
children: [
- {
- path: 'process-instance/detail',
- component: () => import('#/views/bpm/processInstance/detail/index.vue'),
- name: 'BpmProcessInstanceDetail',
- meta: {
- title: '娴佺▼璇︽儏',
- activePath: '/bpm/task/my',
- icon: 'ant-design:history-outlined',
- keepAlive: false,
- hideInMenu: true,
- },
- props: (route) => {
- return {
- id: route.query.id,
- taskId: route.query.taskId,
- activityId: route.query.activityId,
- };
- },
- },
{
path: '/bpm/manager/form/edit',
name: 'BpmFormEditor',
diff --git a/src/types/vue-office.d.ts b/src/types/vue-office.d.ts
new file mode 100644
index 0000000..a7a6426
--- /dev/null
+++ b/src/types/vue-office.d.ts
@@ -0,0 +1,33 @@
+declare module '@vue-office/pdf' {
+ import type { DefineComponent } from 'vue';
+ const VueOfficePdf: DefineComponent<{
+ src: string;
+ }>;
+ export default VueOfficePdf;
+}
+
+declare module '@vue-office/docx' {
+ import type { DefineComponent } from 'vue';
+ const VueOfficeDocx: DefineComponent<{
+ src: string;
+ }>;
+ export default VueOfficeDocx;
+}
+
+declare module '@vue-office/excel' {
+ import type { DefineComponent } from 'vue';
+ const VueOfficeExcel: DefineComponent<{
+ src: string;
+ }>;
+ export default VueOfficeExcel;
+}
+
+declare module '@vue-office/pptx' {
+ import type { DefineComponent } from 'vue';
+ const VueOfficePptx: DefineComponent<{
+ src: string;
+ }>;
+ export default VueOfficePptx;
+}
+
+declare module '@vue-office/excel/lib/index.css';
diff --git a/src/views/ai/chat/index/modules/message/files.vue b/src/views/ai/chat/index/modules/message/files.vue
index ed163a0..5cbc411 100644
--- a/src/views/ai/chat/index/modules/message/files.vue
+++ b/src/views/ai/chat/index/modules/message/files.vue
@@ -2,11 +2,15 @@
import { computed } from 'vue';
import { IconifyIcon } from '@vben/icons';
-import { getFileIcon, getFileNameFromUrl, getFileTypeClass } from '@vben/utils';
+import { getFileIcon, getFileNameFromUrl, getFileTypeClass, isImage } from '@vben/utils';
+
+import { FilePreviewModal, useFilePreview } from '#/components/file-preview';
const props = defineProps<{
attachmentUrls?: string[];
}>();
+
+const { openPreview, previewState } = useFilePreview();
/** 杩囨护鎺夌┖鍊肩殑闄勪欢鍒楄〃 */
const validAttachmentUrls = computed(() => {
@@ -15,7 +19,11 @@
/** 鐐瑰嚮鏂囦欢 */
function handleFileClick(url: string) {
- window.open(url, '_blank');
+ if (isImage(url)) {
+ window.open(url, '_blank');
+ } else {
+ openPreview(url);
+ }
}
</script>
@@ -50,4 +58,9 @@
</div>
</div>
</div>
+ <FilePreviewModal
+ v-model:visible="previewState.visible.value"
+ :file-url="previewState.fileUrl.value"
+ :file-name="previewState.fileName.value"
+ />
</template>
diff --git a/src/views/bpm/processInstance/detail/modules/operation-button.vue b/src/views/bpm/processInstance/detail/modules/operation-button.vue
index b955d7a..248bc1d 100644
--- a/src/views/bpm/processInstance/detail/modules/operation-button.vue
+++ b/src/views/bpm/processInstance/detail/modules/operation-button.vue
@@ -23,6 +23,8 @@
import { useUserStore } from '@vben/stores';
import { isEmpty } from '@vben/utils';
+import { FilePreviewModal, useFilePreview } from '#/components/file-preview';
+
import FormCreate from '@form-create/ant-design-vue';
import { until, useDebounceFn } from '@vueuse/core';
import {
@@ -789,6 +791,8 @@
const imagePreviewVisible = ref(false);
const imagePreviewUrl = ref('');
+const { openPreview: openFilePreview, previewState: filePreviewState } = useFilePreview();
+
/** 鍒ゆ柇鏂囦欢鏄惁涓哄浘鐗囩被鍨� */
function isImageUrl(url: string) {
return /\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i.test(
@@ -811,7 +815,7 @@
imagePreviewUrl.value = url;
imagePreviewVisible.value = true;
} else {
- window.open(url, '_blank');
+ openFilePreview(url, file?.name);
}
}
@@ -1552,4 +1556,11 @@
:src="imagePreviewUrl"
/>
</div>
+
+ <!-- 鏂囦欢棰勮锛圥DF/Word/Excel/PPT锛� -->
+ <FilePreviewModal
+ v-model:visible="filePreviewState.visible.value"
+ :file-url="filePreviewState.fileUrl.value"
+ :file-name="filePreviewState.fileName.value"
+ />
</template>
diff --git a/src/views/bpm/processInstance/index.vue b/src/views/bpm/processInstance/index.vue
index 1f5612b..9f3ccff 100644
--- a/src/views/bpm/processInstance/index.vue
+++ b/src/views/bpm/processInstance/index.vue
@@ -11,7 +11,7 @@
DICT_TYPE,
} from '@vben/constants';
-import { Button, message, Textarea } from 'ant-design-vue';
+import { Button, message, Textarea, Tooltip } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getProcessDefinition } from '#/api/bpm/definition';
@@ -128,16 +128,27 @@
<template>
<Page auto-content-height><Grid table-title="娴佺▼鐘舵��">
<template #slot-summary="{ row }">
- <div
- class="flex flex-col py-2"
+ <Tooltip
v-if="row.summary && row.summary.length > 0"
+ placement="topLeft"
>
- <div v-for="(item, index) in row.summary" :key="index">
- <span class="text-gray-500">
+ <template #title>
+ <div v-for="(item, index) in row.summary" :key="index">
{{ item.key }} : {{ item.value }}
+ </div>
+ </template>
+ <div class="truncate max-w-[200px] cursor-pointer">
+ <span
+ v-for="(item, index) in row.summary"
+ :key="index"
+ >
+ <span class="text-gray-500">
+ {{ item.key }} : {{ item.value }}
+ </span>
+ <span v-if="index < row.summary.length - 1" class="mx-1 text-gray-300">|</span>
</span>
</div>
- </div>
+ </Tooltip>
<div v-else>-</div>
</template>
<template #slot-status="{ row }">
diff --git a/src/views/erp/purchase/order/data.ts b/src/views/erp/purchase/order/data.ts
index 361ba18..761cbad 100644
--- a/src/views/erp/purchase/order/data.ts
+++ b/src/views/erp/purchase/order/data.ts
@@ -1,15 +1,17 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import { markRaw } from 'vue';
+
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { erpPriceInputFormatter } from '@vben/utils';
import { z } from '#/adapter/form';
import { getAccountSimpleList } from '#/api/erp/finance/account';
-import { getProductSimpleList } from '#/api/erp/product/product';
import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
import { getSimpleUserList } from '#/api/system/user';
+import { MdmItemSelect } from '#/views/basicData/mdm/components';
import { getRangePickerDefaultProps } from '#/utils';
/** 琛ㄥ崟绫诲瀷 */
@@ -200,6 +202,13 @@
slots: { default: 'remark' },
},
{
+ field: 'qcCheckFlag',
+ title: '鏄惁璐ㄦ',
+ minWidth: 80,
+ fixed: 'right',
+ slots: { default: 'qcCheckFlag' },
+ },
+ {
field: 'count',
title: '鏁伴噺',
minWidth: 120,
@@ -266,14 +275,9 @@
{
fieldName: 'productId',
label: '浜у搧',
- component: 'ApiSelect',
+ component: markRaw(MdmItemSelect),
componentProps: {
placeholder: '璇烽�夋嫨浜у搧',
- allowClear: true,
- showSearch: true,
- api: getProductSimpleList,
- labelField: 'name',
- valueField: 'id',
},
},
{
diff --git a/src/views/erp/purchase/order/modules/item-form.vue b/src/views/erp/purchase/order/modules/item-form.vue
index 37b3d5f..f4565b5 100644
--- a/src/views/erp/purchase/order/modules/item-form.vue
+++ b/src/views/erp/purchase/order/modules/item-form.vue
@@ -10,7 +10,7 @@
erpPriceMultiply,
} from '@vben/utils';
-import { Input, InputNumber, Select } from 'ant-design-vue';
+import { Input, InputNumber, Select, Switch } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getItemPage } from '#/api/mdm/item';
@@ -134,6 +134,7 @@
taxPrice: undefined,
totalPrice: undefined,
remark: undefined,
+ qcCheckFlag: false,
};
tableData.value.push(newRow);
// 閫氱煡鐖剁粍浠舵洿鏂�
@@ -261,6 +262,16 @@
<Input v-if="!disabled" v-model:value="row.remark" class="w-full" />
<span v-else>{{ row.remark || '-' }}</span>
</template>
+ <template #qcCheckFlag="{ row }">
+ <Switch
+ v-if="!disabled"
+ v-model:checked="row.qcCheckFlag"
+ checked-children="鏄�"
+ un-checked-children="鍚�"
+ @change="handleRowChange(row)"
+ />
+ <span v-else>{{ row.qcCheckFlag ? '鏄�' : '鍚�' }}</span>
+ </template>
<template #taxPercent="{ row }">
<InputNumber
v-if="!disabled"
diff --git a/src/views/mes/qc/iqc/data.ts b/src/views/mes/qc/iqc/data.ts
index 7b5b3ee..0a77b97 100644
--- a/src/views/mes/qc/iqc/data.ts
+++ b/src/views/mes/qc/iqc/data.ts
@@ -13,7 +13,7 @@
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerDefaultProps } from '#/utils';
-import { MdItemSelect } from '#/views/mes/md/item/components';
+import { MdmItemSelect } from '#/views/basicData/mdm/components';
import { MdVendorSelect } from '#/views/mes/md/vendor/components';
/** 琛ㄥ崟绫诲瀷 */
@@ -120,7 +120,7 @@
{
fieldName: 'itemId',
label: '浜у搧鐗╂枡',
- component: markRaw(MdItemSelect),
+ component: markRaw(MdmItemSelect),
componentProps: {
placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
},
@@ -291,7 +291,7 @@
{
fieldName: 'itemId',
label: '浜у搧鐗╂枡',
- component: markRaw(MdItemSelect),
+ component: markRaw(MdmItemSelect),
componentProps: {
placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
},
diff --git a/src/views/mes/qc/oqc/data.ts b/src/views/mes/qc/oqc/data.ts
index f0a8f81..08ddd90 100644
--- a/src/views/mes/qc/oqc/data.ts
+++ b/src/views/mes/qc/oqc/data.ts
@@ -13,7 +13,7 @@
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { getSimpleUserList } from '#/api/system/user';
import { MdClientSelect } from '#/views/mes/md/client/components';
-import { MdItemSelect } from '#/views/mes/md/item/components';
+import { MdmItemSelect } from '#/views/basicData/mdm/components';
/** 琛ㄥ崟绫诲瀷 */
export type FormType = 'create' | 'detail' | 'update';
@@ -119,7 +119,7 @@
{
fieldName: 'itemId',
label: '浜у搧鐗╂枡',
- component: markRaw(MdItemSelect),
+ component: markRaw(MdmItemSelect),
componentProps: {
placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
},
@@ -324,7 +324,7 @@
{
fieldName: 'itemId',
label: '浜у搧鐗╂枡',
- component: markRaw(MdItemSelect),
+ component: markRaw(MdmItemSelect),
componentProps: {
placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
},
diff --git a/src/views/mes/qc/rqc/data.ts b/src/views/mes/qc/rqc/data.ts
index dde09ff..4d08cbf 100644
--- a/src/views/mes/qc/rqc/data.ts
+++ b/src/views/mes/qc/rqc/data.ts
@@ -16,7 +16,7 @@
import { generateAutoCode } from '#/api/mes/md/autocode/record';
import { getSimpleUserList } from '#/api/system/user';
-import { MdItemSelect } from '#/views/mes/md/item/components';
+import { MdmItemSelect } from '#/views/basicData/mdm/components';
/** 琛ㄥ崟绫诲瀷 */
export type FormType = 'create' | 'detail' | 'update';
@@ -144,7 +144,7 @@
{
fieldName: 'itemId',
label: '浜у搧鐗╂枡',
- component: markRaw(MdItemSelect),
+ component: markRaw(MdmItemSelect),
componentProps: {
placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
},
@@ -293,7 +293,7 @@
{
fieldName: 'itemId',
label: '浜у搧鐗╂枡',
- component: markRaw(MdItemSelect),
+ component: markRaw(MdmItemSelect),
componentProps: {
placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
},
diff --git a/src/views/wls/arrivalnotice/data.ts b/src/views/wls/arrivalnotice/data.ts
index 238646d..136ed26 100644
--- a/src/views/wls/arrivalnotice/data.ts
+++ b/src/views/wls/arrivalnotice/data.ts
@@ -315,6 +315,7 @@
field: 'iqcCode',
title: '妫�楠屽崟鍙�',
minWidth: 140,
+ slots: { default: 'iqcCode' },
},
{
field: 'remark',
diff --git a/src/views/wls/arrivalnotice/modules/line-list.vue b/src/views/wls/arrivalnotice/modules/line-list.vue
index a48316f..29a152a 100644
--- a/src/views/wls/arrivalnotice/modules/line-list.vue
+++ b/src/views/wls/arrivalnotice/modules/line-list.vue
@@ -6,9 +6,11 @@
import { computed } from 'vue';
+import { useRouter } from 'vue-router';
+
import { useVbenModal } from '@vben/common-ui';
-import { message } from 'ant-design-vue';
+import { Button, message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
@@ -24,6 +26,8 @@
formType: FormType;
noticeId: number;
}>();
+
+const router = useRouter();
const isEditable = computed(() =>
// 鏄惁鍙紪杈戞槑缁嗚
@@ -62,6 +66,13 @@
handleRefresh();
} finally {
hideLoading();
+ }
+}
+
+/** 璺宠浆 IQC 妫�楠屽崟璇︽儏 */
+function handleOpenIqc(row: MesWmArrivalNoticeLineApi.ArrivalNoticeLine) {
+ if (row.iqcId) {
+ router.push({ path: '/mes/qc/iqc', query: { id: row.iqcId } });
}
}
@@ -111,6 +122,17 @@
]"
/>
</template>
+ <template #iqcCode="{ row }">
+ <Button
+ v-if="row.iqcCode"
+ size="small"
+ type="link"
+ @click="handleOpenIqc(row)"
+ >
+ {{ row.iqcCode }}
+ </Button>
+ <span v-else>-</span>
+ </template>
<template #actions="{ row }">
<TableAction
:actions="[
--
Gitblit v1.9.3