2
0

2 Ревизии b99fdfba45 ... bc454caf44

Автор SHA1 Съобщение Дата
  cc12458 bc454caf44 舌面象分析 异常添加分组展示逻辑 преди 6 месеца
  cc12458 7ccf192728 小程序码弹窗添加关闭按钮 преди 6 месеца

+ 26 - 4
src/components/AnalysisComponent.vue

@@ -1,9 +1,13 @@
 <script setup lang="ts">
 import type { AnalysisModel } from '@/request/model';
 
-type Props = AnalysisModel & { title?: string };
+type Props = AnalysisModel & { title?: string, exceptionType?: 'list' | 'group' };
 
-const { table, exception, result = null, cover = [], title = '分析' } = defineProps<Props>();
+const {
+  title = '分析', exceptionType = 'list',
+  table, result = null, cover = [],
+  exception, exceptionGroup,
+} = defineProps<Props>();
 </script>
 
 <template>
@@ -46,8 +50,8 @@ const { table, exception, result = null, cover = [], title = '分析' } = define
         </slot>
       </div>
       <slot name="exception">
-        <div class="grid grid-rows-1 grid-cols-2 gap-8 m-6">
-          <div class="card text-lg" v-for="item in 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">
@@ -69,6 +73,24 @@ const { table, exception, result = null, cover = [], title = '分析' } = define
             </div>
           </div>
         </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">
+            <div class="card__content">
+              <div class="flex mb-6 justify-center">
+                <img v-if="group.key" class="flex-none w-2/4 max-h-[200px] object-scale-down" :src="group.key" alt="分析异常图像" />
+              </div>
+              <div class="my-3" v-for="item in group.exception" :key="item.title">
+                <div class="card__title mb-2 text-primary text-2xl font-bold">{{ item.title }}</div>
+                <div class="card__content">
+                  <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>
+          </div>
+        </div>
       </slot>
     </slot>
   </van-skeleton>

+ 11 - 2
src/modules/report/report-analyse.page.vue

@@ -72,8 +72,8 @@ function next() {
       <van-skeleton class="flex-auto" v-if="!error" title :row="3" :loading>
         <div class="flex-auto" :class="{ 'overflow-y-auto': scrollable }">
           <div class="my-6 text-primary text-2xl text-center" v-if="data.date">报告日期:{{ data.date }}</div>
-          <AnalysisComponent title="舌象分析" v-bind="data.tongue"></AnalysisComponent>
-          <AnalysisComponent title="面象分析" v-bind="data.face"></AnalysisComponent>
+          <AnalysisComponent title="舌象分析" exception-type="list" v-bind="data.tongue"></AnalysisComponent>
+          <AnalysisComponent title="面象分析" exception-type="group" v-bind="data.face"></AnalysisComponent>
           <div class="m-4" v-if="!nextLoading && showNext">
             <div class="m-auto size-16 cursor-pointer">
               <img class="size-full" src="@/assets/images/next-step.svg" alt="提交" @click="next()">
@@ -84,6 +84,15 @@ function next() {
       </van-skeleton>
     </div>
     <van-floating-panel v-model:height="panelHeight" v-bind="panelProps">
+      <template #header>
+        <div class="van-floating-panel__header !justify-between">
+          <div></div>
+          <div class="van-floating-panel__header-bar"></div>
+          <div>
+            <van-icon v-if="!data.payLock" name="cross" @click.stop="panelHeight = panelProps.anchors[0];" />
+          </div>
+        </div>
+      </template>
       <Transition>
         <div class="panel-content">
           <img

+ 9 - 0
src/modules/report/report.page.vue

@@ -223,6 +223,15 @@ const scrollable = computed(() => !data.value.payLock &&
       </div>
       <Component :is="ReportPreview" v-bind="reportPreviewProps" v-model:show="reportPreviewProps.show"></Component>
       <van-floating-panel v-model:height="panelHeight" v-bind="panelProps">
+        <template #header>
+          <div class="van-floating-panel__header !justify-between">
+            <div></div>
+            <div class="van-floating-panel__header-bar"></div>
+            <div>
+              <van-icon v-if="!data.payLock" name="cross" @click.stop="panelHeight = panelProps.anchors[0];" />
+            </div>
+          </div>
+        </template>
         <Transition>
           <div class="panel-content">
             <img

+ 27 - 0
src/request/model/analysis.model.ts

@@ -1,3 +1,6 @@
+import { groupBy } from '@/tools';
+
+
 export function fromTongueAnalysisModel(data: Record<string, any>): AnalysisModel {
   const exception: AnalysisException[] = [];
   const fromTongueException = fromAnalysisException(exception);
@@ -16,6 +19,7 @@ export function fromTongueAnalysisModel(data: Record<string, any>): AnalysisMode
       ],
     },
     exception,
+    exceptionGroup: [],
     result: data?.tongueAnalysisResult ?? data?.tongue ?? '',
     cover: Object.assign([c1, c2].filter(Boolean), {
       ['舌上']: c1,
@@ -50,6 +54,7 @@ export function fromFaceAnalysisModel(data: Record<string, any>): AnalysisModel
       ],
     },
     exception,
+    exceptionGroup: [],
     result: data?.faceAnalysisResult ?? data?.face ?? '',
     cover: Object.assign([c1, c2, c3].filter(Boolean), {
       ['正面']: c1,
@@ -59,12 +64,34 @@ export function fromFaceAnalysisModel(data: Record<string, any>): AnalysisModel
   };
 }
 
+export function fromAnalysisModel(mode: 'tongue' | 'face', data: Record<string, any>): AnalysisModel {
+  let model: AnalysisModel;
+  switch ( mode ) {
+    case 'tongue':
+      model = fromTongueAnalysisModel(data);
+      break;
+    case 'face':
+      model = fromFaceAnalysisModel(data);
+      break;
+  }
+
+  const group = groupBy<AnalysisException>(model.exception, (item) => item.cover ?? '');
+  model.exceptionGroup = Object.entries(group).map(([ key, exception ]) => (
+    { key, exception }
+  ));
+  return model;
+}
+
 export interface AnalysisModel {
   table: {
     columns: string[];
     data: (string[] & { exception: boolean })[];
   };
   exception: AnalysisException[];
+  exceptionGroup: {
+    key: string;
+    exception: AnalysisException[]
+  }[];
   cover: string[];
   result: string;
 }

+ 4 - 5
src/request/model/report.model.ts

@@ -1,8 +1,7 @@
-import { fromFaceAnalysisModel, fromTongueAnalysisModel } from '@/request/model/analysis.model';
+import { fromAnalysisModel } from '@/request/model/analysis.model';
+
 
 export function fromReportData(data: Record<string, any>) {
-  const tongueException: ReportTongueException[] = [];
-  const fromTongueException = fromReportTongueExceptionData.bind(null, tongueException);
   return {
     id: data?.healthAnalysisReportId,
     date: data?.reportTime,
@@ -28,8 +27,8 @@ export function fromReportData(data: Record<string, any>) {
       ],
     },
 
-    tongue: fromTongueAnalysisModel(data),
-    face: fromFaceAnalysisModel(data),
+    tongue: fromAnalysisModel('tongue', data),
+    face: fromAnalysisModel('face', data),
 
     [ '中医证素' ]: data?.factorItems?.map?.((item: Record<string, any>) => {
       return { label: item?.factorItemName, value: item?.factorItemDescription, score: +item?.score };

+ 4 - 0
src/themes/vant.scss

@@ -55,3 +55,7 @@
   --van-dialog-message-line-height: 24px;
   --van-font-size-md: 16px;
 }
+
+.van-floating-panel__header {
+  padding: 0 calc((var(--van-floating-panel-header-height) - 16px) / 2);
+}

+ 18 - 0
src/tools/polyfills.ts

@@ -20,3 +20,21 @@ export function withResolvers<T>(): {
     return fn();
   }
 }
+
+export function groupBy<T>(items: Iterable<T>, callbackFn: (element: T, index: number) => any): Record<string, T[]> {
+  if ( typeof Object.groupBy === 'function' ) {
+    groupBy = Object.groupBy.bind(Object);
+    return Object.groupBy(items, callbackFn);
+  } else {
+    const fn = function(items, callbackFn) {
+      const obj = Object.create(null);
+      let k = 0;
+      for ( const value of items ) {
+        const key = callbackFn(value, k++);
+        if ( key in obj ) { obj[ key ].push(value); } else { obj[ key ] = [ value ]; }
+      }
+    };
+    groupBy = fn.bind(Object);
+    return fn(items, callbackFn);
+  }
+}