From c1a9bd81744474027abc73abcc778fd2798ec7ec Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 18 九月 2026 14:39:43 +0800
Subject: [PATCH] feat(mes): 增加质检指标详情查看功能并调整接口配置

---
 src/views/mes/qc/iqc/index.vue                       |   22 +++++
 src/views/mes/qc/indicatorresult/components/data.ts  |    2 
 src/views/mes/qc/ipqc/data.ts                        |    2 
 src/packages/constants/src/biz-mes-enum.ts           |    6 +
 src/views/mes/qc/iqc/data.ts                         |    2 
 src/views/mes/qc/indicatorresult/components/form.vue |   76 ++++++++++++++----
 src/views/mes/qc/oqc/data.ts                         |    2 
 src/views/mes/qc/ipqc/index.vue                      |   22 +++++
 .env.development                                     |    2 
 src/views/mes/qc/oqc/index.vue                       |   22 +++++
 vite.config.ts                                       |    2 
 src/views/mes/qc/rqc/index.vue                       |   22 +++++
 src/api/mes/qc/indicatorresult/index.ts              |   32 +++++++
 src/views/mes/qc/indicatorresult/components/list.vue |   19 ++++
 src/views/mes/qc/rqc/data.ts                         |    2 
 15 files changed, 209 insertions(+), 26 deletions(-)

diff --git a/.env.development b/.env.development
index 5ade162..d5e3ed6 100644
--- a/.env.development
+++ b/.env.development
@@ -4,7 +4,7 @@
 VITE_BASE=/
 
 # 璇锋眰璺緞
-VITE_BASE_URL=http://127.0.0.1:48080
+VITE_BASE_URL=http://127.0.0.1:48081
 # 鎺ュ彛鍦板潃
 VITE_GLOB_API_URL=/admin-api
 # 鏂囦欢涓婁紶绫诲瀷锛歴erver - 鍚庣涓婁紶锛� client - 鍓嶇鐩磋繛涓婁紶锛屼粎鏀寔S3鏈嶅姟
diff --git a/src/api/mes/qc/indicatorresult/index.ts b/src/api/mes/qc/indicatorresult/index.ts
index dfa8a7f..32600e0 100644
--- a/src/api/mes/qc/indicatorresult/index.ts
+++ b/src/api/mes/qc/indicatorresult/index.ts
@@ -3,18 +3,34 @@
 import { requestClient } from '#/api/request';
 
 export namespace MesQcIndicatorResultApi {
-  /** MES 妫�楠岀粨鏋滄槑缁� */
+  /** 骞宠娴嬮噺鍗曟壒娆″�� */
+  export interface IndicatorResultMeasure {
+    detailId?: number; // 鏄庣粏缂栧彿
+    resultId?: number; // 鍏宠仈妫�楠岀粨鏋� ID
+    measureNo?: number; // 娴嬮噺鎵规鍙�
+    value?: string; // 妫�娴嬪��
+    remark?: string; // 澶囨敞
+  }
+
+  /** MES 妫�楠岀粨鏋滄槑缁嗭紙鎺ュ彛杩斿洖涓哄垎缁勬爲锛宑hildren 鎸傚瓙椤癸級 */
   export interface IndicatorResultDetail {
     id?: number; // 缂栧彿
     resultId?: number; // 鍏宠仈妫�楠岀粨鏋� ID
     indicatorId?: number; // 妫�娴嬫寚鏍� ID
-    value?: string; // 妫�娴嬪�硷紙缁熶竴瀛樹负瀛楃涓诧級
+    value?: string; // 妫�娴嬪�硷紙缁熶竴瀛樹负瀛楃涓诧紝鍏煎绗竴鎵规祴閲忓�硷級
     valueNumber?: number; // UI 鏁板�肩粦瀹氾紙鎻愪氦鍓嶈浆瀛楃涓诧級
     remark?: string; // 澶囨敞
+    measures?: IndicatorResultMeasure[]; // 骞宠娴嬮噺澶氭壒娆″��
     // 鍏宠仈鏌ヨ瀛楁锛堟潵鑷� indicator锛�
     indicatorName?: string; // 妫�娴嬫寚鏍囧悕绉�
+    parentId?: number; // 鐖跺垎缁勬寚鏍� ID
+    itemType?: number; // 鎸囨爣椤圭被鍨嬶紙1 褰曞叆椤� / 2 鍒嗙粍锛�
+    formulaText?: string; // 鍏紡鏂囨湰
+    unitText?: string; // 鍗曚綅鏂囨湰
+    sortOrder?: number; // 鎺掑簭鍙�
     valueType?: number; // 璐ㄦ鍊肩被鍨�
     valueSpecification?: string; // 鍊煎睘鎬�
+    children?: IndicatorResultDetail[]; // 鍒嗙粍瀛愰」
   }
 
   /** MES 妫�楠岀粨鏋� */
@@ -71,3 +87,15 @@
 export function deleteIndicatorResult(id: number) {
   return requestClient.delete(`/mes/qc/indicator-result/delete?id=${id}`);
 }
+
+/** 瀵煎嚭浜у搧妫�楠屽崟 Word锛坬cType锛�1=IQC銆�2=IPQC銆�3=OQC銆�4=RQC锛況esultId 鍙�夛紝榛樿鍙栨渶鏃╂牱鍝侊級 */
+export function exportProductWord(params: {
+  qcId: number;
+  qcType: number;
+  resultId?: number;
+}) {
+  return requestClient.download(
+    '/mes/qc/indicator-result/export-product-word',
+    { params },
+  );
+}
diff --git a/src/packages/constants/src/biz-mes-enum.ts b/src/packages/constants/src/biz-mes-enum.ts
index 1ac585c..848c41d 100644
--- a/src/packages/constants/src/biz-mes-enum.ts
+++ b/src/packages/constants/src/biz-mes-enum.ts
@@ -440,6 +440,12 @@
   WIP_VIRTUAL: 'WIP_VIRTUAL',
 } as const;
 
+/** MES 璐ㄦ鎸囨爣椤圭被鍨嬫灇涓撅紙瀵瑰簲鍚庣 MesQcIndicatorItemTypeEnum锛� */
+export const MesQcIndicatorItemType = {
+  RECORD: 1,
+  GROUP: 2,
+} as const;
+
 /** MES 璐ㄦ缁撴灉鍊肩被鍨嬫灇涓� */
 export const MesQcResultValueType = {
   FLOAT: 1,
diff --git a/src/views/mes/qc/indicatorresult/components/data.ts b/src/views/mes/qc/indicatorresult/components/data.ts
index 694a01c..586d2e8 100644
--- a/src/views/mes/qc/indicatorresult/components/data.ts
+++ b/src/views/mes/qc/indicatorresult/components/data.ts
@@ -11,7 +11,7 @@
 import { generateAutoCode } from '#/api/mes/md/autocode/record';
 
 /** 琛ㄥ崟绫诲瀷 */
-export type FormType = 'create' | 'update';
+export type FormType = 'create' | 'detail' | 'update';
 
 /** 鏂板/淇敼妫�娴嬬粨鏋滅殑琛ㄥ崟 */
 export function useQcIndicatorResultFormSchema(
diff --git a/src/views/mes/qc/indicatorresult/components/form.vue b/src/views/mes/qc/indicatorresult/components/form.vue
index c6481b2..fc345f8 100644
--- a/src/views/mes/qc/indicatorresult/components/form.vue
+++ b/src/views/mes/qc/indicatorresult/components/form.vue
@@ -6,7 +6,7 @@
 import { computed, ref } from 'vue';
 
 import { useVbenModal } from '@vben/common-ui';
-import { MesQcResultValueType } from '@vben/constants';
+import { MesQcIndicatorItemType, MesQcResultValueType } from '@vben/constants';
 import { getDictOptions } from '@vben/hooks';
 
 import {
@@ -43,11 +43,49 @@
 const items = ref<MesQcIndicatorResultApi.IndicatorResultDetail[]>([]);
 const ctxData = ref<CtxData>();
 
-const getTitle = computed(() =>
-  formType.value === 'update'
+const isDetail = computed(() => formType.value === 'detail');
+
+const getTitle = computed(() => {
+  if (formType.value === 'detail') {
+    return $t('ui.actionTitle.view', ['妫�楠岀粨鏋�']);
+  }
+  return formType.value === 'update'
     ? $t('ui.actionTitle.edit', ['妫�楠岀粨鏋�'])
-    : $t('ui.actionTitle.create', ['妫�楠岀粨鏋�']),
-);
+    : $t('ui.actionTitle.create', ['妫�楠岀粨鏋�']);
+});
+
+/** 鍒嗙粍鏍戝钩閾轰负褰曞叆椤瑰垪琛細鍒嗙粍鑺傜偣涓嶅睍绀猴紝浠呭睍寮�鍏跺瓙椤� */
+function flattenRecordItems(
+  nodes: MesQcIndicatorResultApi.IndicatorResultDetail[] | undefined,
+  out: MesQcIndicatorResultApi.IndicatorResultDetail[],
+) {
+  (nodes ?? []).forEach((node) => {
+    if (node.itemType === MesQcIndicatorItemType.GROUP) {
+      flattenRecordItems(node.children, out);
+      return;
+    }
+    out.push({
+      ...node,
+      valueNumber:
+        (node.valueType === MesQcResultValueType.FLOAT ||
+          node.valueType === MesQcResultValueType.INTEGER) &&
+        node.value != null
+          ? Number(node.value)
+          : undefined,
+    });
+  });
+}
+
+/** 璇︽儏灞曠ず鍊硷細骞宠娴嬮噺澶氭壒娆¢】鍙峰悎骞讹紝鍏煎鏃ф暟鎹崟鍊� */
+function measureText(item: MesQcIndicatorResultApi.IndicatorResultDetail) {
+  const vals = (item.measures ?? [])
+    .map((m) => m.value)
+    .filter((v) => v !== undefined && v !== null && v !== '');
+  if (vals.length > 0) {
+    return vals.join('銆�');
+  }
+  return item.value ?? '';
+}
 
 const [Form, formApi] = useVbenForm({
   commonConfig: {
@@ -135,6 +173,9 @@
     const data = modalApi.getData<CtxData>();
     ctxData.value = data;
     formType.value = data.formType;
+    const detailMode = data.formType === 'detail';
+    formApi.setDisabled(detailMode);
+    modalApi.setState({ showConfirmButton: !detailMode });
     modalApi.lock();
     try {
       const detail = await getIndicatorResultDetail(
@@ -142,16 +183,10 @@
         data.qcType,
         data.id,
       );
-      // 鍥炲~鏁板�煎瓧娈碉紙鐢ㄤ簬 InputNumber 缁戝畾锛�
-      items.value = (detail.items ?? []).map((item) => ({
-        ...item,
-        valueNumber:
-          (item.valueType === MesQcResultValueType.FLOAT ||
-            item.valueType === MesQcResultValueType.INTEGER) &&
-          item.value !== null
-            ? Number(item.value)
-            : undefined,
-      }));
+      // 鍒嗙粍鏍戝钩閾� + 鍥炲~鏁板�煎瓧娈碉紙鐢ㄤ簬 InputNumber 缁戝畾锛�
+      const flat: MesQcIndicatorResultApi.IndicatorResultDetail[] = [];
+      flattenRecordItems(detail.items, flat);
+      items.value = flat;
       // 璁剧疆鍒� values
       await formApi.setValues({
         id: detail.id,
@@ -179,10 +214,17 @@
           class="mb-2"
         >
           <AForm.Item
-            :label="`妫�娴嬮」${index + 1}锛�${item.indicatorName ?? ''}`"
+            :label="`妫�娴嬮」${index + 1}锛�${item.indicatorName ?? ''}${
+              isDetail && item.unitText ? `锛�${item.unitText}锛塦 : ''
+            }`"
           >
+            <Input
+              v-if="isDetail"
+              :value="measureText(item) || '/'"
+              disabled
+            />
             <InputNumber
-              v-if="
+              v-else-if="
                 item.valueType === MesQcResultValueType.FLOAT ||
                 item.valueType === MesQcResultValueType.INTEGER
               "
diff --git a/src/views/mes/qc/indicatorresult/components/list.vue b/src/views/mes/qc/indicatorresult/components/list.vue
index 48e11bb..d4611a8 100644
--- a/src/views/mes/qc/indicatorresult/components/list.vue
+++ b/src/views/mes/qc/indicatorresult/components/list.vue
@@ -43,6 +43,18 @@
     .open();
 }
 
+/** 鏌ョ湅妫�娴嬬粨鏋滆鎯咃紙鍙锛� */
+function handleDetail(row: MesQcIndicatorResultApi.IndicatorResult) {
+  formModalApi
+    .setData({
+      formType: 'detail',
+      id: row.id,
+      qcId: props.qcId,
+      qcType: props.qcType,
+    })
+    .open();
+}
+
 /** 缂栬緫妫�娴嬬粨鏋� */
 function handleEdit(row: MesQcIndicatorResultApi.IndicatorResult) {
   formModalApi
@@ -127,6 +139,13 @@
               onClick: handleEdit.bind(null, row),
             },
             {
+              label: $t('common.detail'),
+              type: 'link',
+              icon: ACTION_ICON.VIEW,
+              ifShow: readonly,
+              onClick: handleDetail.bind(null, row),
+            },
+            {
               label: $t('common.delete'),
               type: 'link',
               danger: true,
diff --git a/src/views/mes/qc/ipqc/data.ts b/src/views/mes/qc/ipqc/data.ts
index 58f1e88..b4979f3 100644
--- a/src/views/mes/qc/ipqc/data.ts
+++ b/src/views/mes/qc/ipqc/data.ts
@@ -491,7 +491,7 @@
     },
     {
       title: '鎿嶄綔',
-      width: 180,
+      width: 260,
       fixed: 'right',
       slots: { default: 'actions' },
     },
diff --git a/src/views/mes/qc/ipqc/index.vue b/src/views/mes/qc/ipqc/index.vue
index 32d46c4..82bf057 100644
--- a/src/views/mes/qc/ipqc/index.vue
+++ b/src/views/mes/qc/ipqc/index.vue
@@ -9,6 +9,7 @@
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import { exportProductWord } from '#/api/mes/qc/indicatorresult';
 import { deleteIpqc, exportIpqc, getIpqcPage } from '#/api/mes/qc/ipqc';
 import { $t } from '#/locales';
 
@@ -59,6 +60,20 @@
 async function handleExport() {
   const data = await exportIpqc(await gridApi.formApi.getValues());
   downloadFileFromBlobPart({ fileName: '杩囩▼妫�楠屽崟.xls', source: data });
+}
+
+/** 涓嬭浇璐ㄦ鎶ュ憡锛堜骇鍝佹楠屽崟 Word锛屾墦鍗扮敤 Word 鎵撳紑锛� */
+async function handleExportWord(row: MesQcIpqcApi.Ipqc) {
+  const hideLoading = message.loading({ content: '鎶ュ憡瀵煎嚭涓�...', duration: 0 });
+  try {
+    const data = await exportProductWord({ qcId: row.id!, qcType: 2 });
+    downloadFileFromBlobPart({
+      fileName: `${row.code || ''}-浜у搧璐ㄩ噺妫�楠屽崟.docx`,
+      source: data,
+    });
+  } finally {
+    hideLoading();
+  }
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -151,6 +166,13 @@
               auth: ['mes:qc-ipqc:query'],
               onClick: handleDetail.bind(null, row),
             },
+            {
+              label: '璐ㄦ鎶ュ憡',
+              type: 'link',
+              icon: ACTION_ICON.DOWNLOAD,
+              auth: ['mes:qc-ipqc:query'],
+              onClick: handleExportWord.bind(null, row),
+            },
           ]"
         />
       </template>
diff --git a/src/views/mes/qc/iqc/data.ts b/src/views/mes/qc/iqc/data.ts
index 40cb523..fa7bfa6 100644
--- a/src/views/mes/qc/iqc/data.ts
+++ b/src/views/mes/qc/iqc/data.ts
@@ -436,7 +436,7 @@
     },
     {
       title: '鎿嶄綔',
-      width: 180,
+      width: 260,
       fixed: 'right',
       slots: { default: 'actions' },
     },
diff --git a/src/views/mes/qc/iqc/index.vue b/src/views/mes/qc/iqc/index.vue
index 44e0901..75a2d25 100644
--- a/src/views/mes/qc/iqc/index.vue
+++ b/src/views/mes/qc/iqc/index.vue
@@ -12,6 +12,7 @@
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import { exportProductWord } from '#/api/mes/qc/indicatorresult';
 import { deleteIqc, exportIqc, getIqcPage } from '#/api/mes/qc/iqc';
 import { $t } from '#/locales';
 
@@ -66,6 +67,20 @@
 async function handleExport() {
   const data = await exportIqc(await gridApi.formApi.getValues());
   downloadFileFromBlobPart({ fileName: '鏉ユ枡妫�楠屽崟.xls', source: data });
+}
+
+/** 涓嬭浇璐ㄦ鎶ュ憡锛堜骇鍝佹楠屽崟 Word锛屾墦鍗扮敤 Word 鎵撳紑锛� */
+async function handleExportWord(row: MesQcIqcApi.Iqc) {
+  const hideLoading = message.loading({ content: '鎶ュ憡瀵煎嚭涓�...', duration: 0 });
+  try {
+    const data = await exportProductWord({ qcId: row.id!, qcType: 1 });
+    downloadFileFromBlobPart({
+      fileName: `${row.code || ''}-浜у搧璐ㄩ噺妫�楠屽崟.docx`,
+      source: data,
+    });
+  } finally {
+    hideLoading();
+  }
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -165,6 +180,13 @@
               auth: ['mes:qc-iqc:query'],
               onClick: handleDetail.bind(null, row),
             },
+            {
+              label: '璐ㄦ鎶ュ憡',
+              type: 'link',
+              icon: ACTION_ICON.DOWNLOAD,
+              auth: ['mes:qc-iqc:query'],
+              onClick: handleExportWord.bind(null, row),
+            },
           ]"
         />
       </template>
diff --git a/src/views/mes/qc/oqc/data.ts b/src/views/mes/qc/oqc/data.ts
index 0183a35..1143391 100644
--- a/src/views/mes/qc/oqc/data.ts
+++ b/src/views/mes/qc/oqc/data.ts
@@ -439,7 +439,7 @@
     },
     {
       title: '鎿嶄綔',
-      width: 220,
+      width: 260,
       fixed: 'right',
       slots: { default: 'actions' },
     },
diff --git a/src/views/mes/qc/oqc/index.vue b/src/views/mes/qc/oqc/index.vue
index 965cfda..8647b27 100644
--- a/src/views/mes/qc/oqc/index.vue
+++ b/src/views/mes/qc/oqc/index.vue
@@ -9,6 +9,7 @@
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import { exportProductWord } from '#/api/mes/qc/indicatorresult';
 import { deleteOqc, exportOqc, getOqcPage } from '#/api/mes/qc/oqc';
 import { $t } from '#/locales';
 
@@ -59,6 +60,20 @@
 async function handleExport() {
   const data = await exportOqc(await gridApi.formApi.getValues());
   downloadFileFromBlobPart({ fileName: '鍑鸿揣妫�楠屽崟.xls', source: data });
+}
+
+/** 涓嬭浇璐ㄦ鎶ュ憡锛堜骇鍝佹楠屽崟 Word锛屾墦鍗扮敤 Word 鎵撳紑锛� */
+async function handleExportWord(row: MesQcOqcApi.Oqc) {
+  const hideLoading = message.loading({ content: '鎶ュ憡瀵煎嚭涓�...', duration: 0 });
+  try {
+    const data = await exportProductWord({ qcId: row.id!, qcType: 3 });
+    downloadFileFromBlobPart({
+      fileName: `${row.code || ''}-浜у搧璐ㄩ噺妫�楠屽崟.docx`,
+      source: data,
+    });
+  } finally {
+    hideLoading();
+  }
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -151,6 +166,13 @@
               auth: ['mes:qc-oqc:query'],
               onClick: handleDetail.bind(null, row),
             },
+            {
+              label: '璐ㄦ鎶ュ憡',
+              type: 'link',
+              icon: ACTION_ICON.DOWNLOAD,
+              auth: ['mes:qc-oqc:query'],
+              onClick: handleExportWord.bind(null, row),
+            },
           ]"
         />
       </template>
diff --git a/src/views/mes/qc/rqc/data.ts b/src/views/mes/qc/rqc/data.ts
index 92d5e1e..0026430 100644
--- a/src/views/mes/qc/rqc/data.ts
+++ b/src/views/mes/qc/rqc/data.ts
@@ -417,7 +417,7 @@
     },
     {
       title: '鎿嶄綔',
-      width: 180,
+      width: 260,
       fixed: 'right',
       slots: { default: 'actions' },
     },
diff --git a/src/views/mes/qc/rqc/index.vue b/src/views/mes/qc/rqc/index.vue
index 0cfc6f2..08efd64 100644
--- a/src/views/mes/qc/rqc/index.vue
+++ b/src/views/mes/qc/rqc/index.vue
@@ -9,6 +9,7 @@
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import { exportProductWord } from '#/api/mes/qc/indicatorresult';
 import { deleteRqc, exportRqc, getRqcPage } from '#/api/mes/qc/rqc';
 import { $t } from '#/locales';
 
@@ -59,6 +60,20 @@
 async function handleExport() {
   const data = await exportRqc(await gridApi.formApi.getValues());
   downloadFileFromBlobPart({ fileName: '閫�璐ф楠屽崟.xls', source: data });
+}
+
+/** 涓嬭浇璐ㄦ鎶ュ憡锛堜骇鍝佹楠屽崟 Word锛屾墦鍗扮敤 Word 鎵撳紑锛� */
+async function handleExportWord(row: MesQcRqcApi.Rqc) {
+  const hideLoading = message.loading({ content: '鎶ュ憡瀵煎嚭涓�...', duration: 0 });
+  try {
+    const data = await exportProductWord({ qcId: row.id!, qcType: 4 });
+    downloadFileFromBlobPart({
+      fileName: `${row.code || ''}-浜у搧璐ㄩ噺妫�楠屽崟.docx`,
+      source: data,
+    });
+  } finally {
+    hideLoading();
+  }
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -151,6 +166,13 @@
               auth: ['mes:qc-rqc:query'],
               onClick: handleDetail.bind(null, row),
             },
+            {
+              label: '璐ㄦ鎶ュ憡',
+              type: 'link',
+              icon: ACTION_ICON.DOWNLOAD,
+              auth: ['mes:qc-rqc:query'],
+              onClick: handleExportWord.bind(null, row),
+            },
           ]"
         />
       </template>
diff --git a/vite.config.ts b/vite.config.ts
index 74a95a8..3bb15c1 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -72,7 +72,7 @@
           "/admin-api": {
             changeOrigin: true,
             rewrite: path => path.replace(/^\/admin-api/, ""),
-            target: "http://42.63.70.90:9003/admin-api",
+            target: "http://localhost:48081/admin-api",
             ws: true,
           },
         },

--
Gitblit v1.9.3