cc12458 1 месяц назад
Родитель
Сommit
32d95f7f04
2 измененных файлов с 103 добавлено и 62 удалено
  1. 21 18
      src/components/AnalysisComponent.vue
  2. 82 44
      src/request/model/analysis.model.ts

+ 21 - 18
src/components/AnalysisComponent.vue

@@ -7,7 +7,8 @@ const {
   title = '分析', exceptionType = 'list',
   table, result = null, cover = [],
   exception, exceptionGroup,
-} = defineProps<Props>();
+  filterEmptyException = false,
+} = defineProps<Props & { filterEmptyException?: boolean }>();
 </script>
 
 <template>
@@ -56,27 +57,29 @@ const {
       </div>
       <slot name="exception">
         <div class="grid grid-rows-1 grid-cols-2 gap-8 m-6" v-if="exceptionType === 'list'">
-          <div class="card text-lg" v-for="item in exception" :key="item.title">
-            <div class="card__title mb-3 text-primary text-2xl font-bold">{{ item.title }}</div>
-            <div class="card__content">
-              <div class="flex my-6 justify-center">
-                <img v-if="item.cover" class="flex-none w-2/4 object-scale-down" :src="item.cover" alt="分析异常图像" />
-                <div class="flex-none ml-8">
-                  <div
-                    class="my-2 px-4 py-2 rounded-lg border border-primary-400 text-primary"
-                    v-for="value in item.tags"
-                    :key="value"
-                  >
-                    {{ value }}
+          <template  v-for="item in exception" :key="item.title">
+            <div class="card text-lg" v-if="item.cover">
+              <div class="card__title mb-3 text-primary text-2xl font-bold">{{ item.title }}</div>
+              <div class="card__content">
+                <div class="flex my-2 justify-center">
+                  <img v-if="item.cover" class="flex-none w-2/4 object-scale-down" :src="item.cover" alt="分析异常图像" />
+                  <div class="flex-none ml-8" v-if="item.tags.length">
+                    <div
+                      class="my-2 px-4 py-2 rounded-lg border border-primary-400 text-primary"
+                      v-for="value in item.tags"
+                      :key="value"
+                    >
+                      {{ value }}
+                    </div>
                   </div>
                 </div>
-              </div>
-              <div class="my-2 text-grey" v-for="description in item.descriptions" :key="description.value">
-                <label>{{ description.label }}</label>
-                <span v-html="description.value"></span>
+                <div class="my-2 text-grey" v-for="description in item.descriptions" :key="description.value">
+                  <label>{{ description.label }}</label>
+                  <span v-html="description.value"></span>
+                </div>
               </div>
             </div>
-          </div>
+          </template>
         </div>
         <div class="grid grid-rows-1 grid-cols-1 gap-8 m-6" v-else-if="exceptionType === 'group'" >
           <div class="card text-lg" v-for="group in exceptionGroup" :key="group.key">

+ 82 - 44
src/request/model/analysis.model.ts

@@ -1,5 +1,42 @@
 import { groupBy } from '@/tools';
 
+/**
+ * 单个检测维度:业务侧用 `key` 关联字段,展示与字典匹配用 `dimension`(与 JSON 中 `dimension` 一致)。
+ */
+export interface AnnotatorScope {
+  /** 字段标识,如 `tongueColor` */
+  key: string;
+  /** 与 `annotator.json` 行内 `dimension` 一致,如「舌色」 */
+  dimension: string;
+}
+
+export const tongueScopes: readonly AnnotatorScope[] = [
+  { key: 'tongueColor', dimension: '舌色' },
+  { key: 'tongueCoatingColor', dimension: '苔色' },
+  { key: 'tongueShape', dimension: '舌形' },
+  { key: 'tongueCoating', dimension: '苔质' },
+  { key: 'bodyFluid', dimension: '津液' },
+  { key: 'sublingualVein', dimension: '舌下' },
+];
+
+export const faceScopes: readonly AnnotatorScope[] = [
+  { key: 'faceColor', dimension: '面色' },
+  { key: 'mainColor', dimension: '主色' },
+  { key: 'shine', dimension: '光泽' },
+  { key: 'blackEye', dimension: '黑眼圈' },
+  // { key: 'leftBlackEye', dimension: '左黑眼圈' },
+  // { key: 'rightBlackEye', dimension: '右黑眼圈' },
+  { key: 'lipColor', dimension: '唇色' },
+  { key: 'eyeContact', dimension: '眼神' },
+  { key: 'eyeColor', dimension: '目色' },
+  // { key: 'leftEyeColor', dimension: '左目色' },
+  // { key: 'rightEyeColor', dimension: '右目色' },
+  { key: 'hecticCheek', dimension: '两颧红' },
+  { key: 'noseFold', dimension: '鼻褶' },
+  { key: 'cyanGlabella', dimension: '眉间/鼻柱青色' },
+  { key: 'faceSkinDefects', dimension: '面部皮损' },
+];
+
 export function fromAnalysisModel(mode: 'tongue' | 'face', data: Record<string, any>): AnalysisModel;
 export function fromAnalysisModel(mode: 'pulse', data: Record<string, any>): PulseAnalysisModel;
 export function fromAnalysisModel(mode: 'alcohol', data: Record<string, any>): AlcoholAnalysisModel;
@@ -41,17 +78,27 @@ export interface AlcoholAnalysisModel {
 export interface AnalysisModel {
   table: {
     columns: string[];
-    data: { exception: boolean; invalid?: boolean; columns: string[] }[];
+    data: { key: string; exception: boolean; invalid?: boolean; columns: string[] }[];
   };
   exception: AnalysisException[];
   exceptionGroup: {
     key: string;
-    exception: AnalysisException[]
+    exception: AnalysisException[];
   }[];
   cover: string[];
   result: string;
 }
 
+export interface AnalysisData {
+  standardValue: string;
+  actualList: {
+    actualValue: string;
+    contrast: 's' | string;
+    splitImage?: string | null;
+    attrs: string[] | null;
+  }[];
+}
+
 export interface AnalysisException {
   title: string;
   cover?: string;
@@ -87,7 +134,12 @@ function fromPulseAnalysisModel(data: Record<string, any>): PulseAnalysisModel {
 }
 
 function fromAlcoholAnalysisModel(data: Record<string, any>): AlcoholAnalysisModel {
-  const volume: [number, number] = data?.alcoholCapacity?.match(/(\d+)/g)?.slice(0, 2)?.filter(Boolean)?.map((v: string) => +v) ?? [];
+  const volume: [number, number] =
+    data?.alcoholCapacity
+      ?.match(/(\d+)/g)
+      ?.slice(0, 2)
+      ?.filter(Boolean)
+      ?.map((v: string) => +v) ?? [];
   return {
     condition: data?.alcoholCondition,
     description: data?.alcoholCapacity,
@@ -97,20 +149,13 @@ function fromAlcoholAnalysisModel(data: Record<string, any>): AlcoholAnalysisMod
 
 function fromTongueAnalysisModel(data: Record<string, any>): AnalysisModel {
   const exception: AnalysisException[] = [];
-  const fromTongueException = fromAnalysisException(exception);
-  const c1 = data?.upImg ?? data?.tongueImgUrl;
-  const c2 = data?.downImg ?? data?.tongueBackImgUrl;
+  const fromTongueException = fromAnalysisException(exception).bind(null, data);
+  const c1 = fromPictureAnnotator(data?.upImg) ?? data?.tongueImgUrl;
+  const c2 = fromPictureAnnotator(data?.downImg) ?? data?.tongueBackImgUrl;
   return {
     table: {
       columns: ['舌象维度', '检测结果', '标准值'],
-      data: [
-        fromTongueException(data?.tongueColor, '舌色'),
-        fromTongueException(data?.tongueCoatingColor, '苔色'),
-        fromTongueException(data?.tongueShape, '舌形'),
-        fromTongueException(data?.tongueCoating, '苔质'),
-        fromTongueException(data?.bodyFluid, '津液'),
-        fromTongueException(data?.sublingualVein, '舌下'),
-      ],
+      data: tongueScopes.map(({ key, dimension }) => fromTongueException(key, dimension)),
     },
     exception,
     exceptionGroup: [],
@@ -124,30 +169,14 @@ function fromTongueAnalysisModel(data: Record<string, any>): AnalysisModel {
 
 function fromFaceAnalysisModel(data: Record<string, any>): AnalysisModel {
   const exception: AnalysisException[] = [];
-  const fromFaceException = fromAnalysisException(exception, (label, value) => `${label}${value}`);
-  const c1 = data?.faceImg ?? data?.faceImgUrl;
-  const c2 = data?.faceLeft ?? data?.faceLeftImgUrl;
-  const c3 = data?.faceRight ?? data?.faceRightImgUrl;
+  const fromFaceException = fromAnalysisException(exception, (label, value) => `${label}${value}`).bind(null, data);
+  const c1 = fromPictureAnnotator(data?.faceImg) ?? data?.faceImgUrl;
+  const c2 = fromPictureAnnotator(data?.faceLeft) ?? data?.faceLeftImgUrl;
+  const c3 = fromPictureAnnotator(data?.faceRight) ?? data?.faceRightImgUrl;
   return {
     table: {
       columns: ['面象维度', '检测结果', '标准值'],
-      data: [
-        fromFaceException(data?.faceColor, '面色'),
-        fromFaceException(data?.mainColor, '主色'),
-        fromFaceException(data?.shine, '光泽'),
-        fromFaceException(data?.blackEye, '黑眼圈'),
-        // fromFaceException(data?.leftBlackEye, '左黑眼圈'),
-        // fromFaceException(data?.rightBlackEye, '右黑眼圈'),
-        fromFaceException(data?.lipColor, '唇色'),
-        fromFaceException(data?.eyeContact, '眼神'),
-        fromFaceException(data?.eyeColor, '目色'),
-        // fromFaceException(data?.leftEyeColor, '左目色'),
-        // fromFaceException(data?.rightEyeColor, '右目色'),
-        fromFaceException(data?.hecticCheek, '两颧红'),
-        fromFaceException(data?.noseFold, '鼻褶'),
-        fromFaceException(data?.cyanGlabella, '眉间/鼻柱青色'),
-        fromFaceException(data?.faceSkinDefects, '面部皮损'),
-      ],
+      data: faceScopes.map(({ key, dimension }) => fromFaceException(key, dimension)),
     },
     exception,
     exceptionGroup: [],
@@ -161,19 +190,24 @@ function fromFaceAnalysisModel(data: Record<string, any>): AnalysisModel {
 }
 
 function fromAnalysisException(exception: AnalysisException[], $title = (label: string, value: string) => value) {
-  return function (data: { actualList?: Record<string, any>[]; standardValue?: string }, label: string) {
+  const getTitle = (label: string, value: string) => {
+    if (value.startsWith(label)) label = '';
+    return $title(label, value);
+  }
+  return function (data: Record<string, any>, key: string, label: string) {
+    const value = data?.[key] as { actualList?: Record<string, any>[]; standardValue?: string };
     let is = false;
     let invalid = false;
-    const values =
-      data?.actualList?.map((item) => {
+    const values = value?.actualList?.map((item) => {
         let title: string = item?.actualValue ?? '';
         const suffix = item?.contrast ?? 's';
-        if (title.endsWith('不符合检测要求')) { invalid = true; }
-        else if (suffix !== 's') {
+        if (title.endsWith('不符合检测要求')) {
+          invalid = true;
+        } else if (suffix !== 's') {
           if (suffix !== 'r') title += ` (${suffix || ''}) `;
           is = true;
           exception.push({
-            title: $title(label, title),
+            title: getTitle(label, title),
             cover: item.splitImage,
             descriptions: [
               item.features ? { label: '【特征】', value: item.features } : null,
@@ -185,8 +219,12 @@ function fromAnalysisException(exception: AnalysisException[], $title = (label:
         return title;
       }) ?? [];
     return {
-      exception: is, invalid,
-      columns: [label, values.join('<br>'), data?.standardValue ?? '']
-    }
+      key, exception: is, invalid,
+      columns: [label, values.join('<br>'), value?.standardValue ?? ''],
+    };
   };
 }
+
+function fromPictureAnnotator(value?: string | { object?: { src?: string } }) {
+  return typeof value === 'string' ? value : value?.object?.src;
+}