Ver Fonte

添加黄酒建议

cc12458 há 2 meses atrás
pai
commit
5b93244eea

BIN
src/assets/images/alcohol-0.png


BIN
src/assets/images/alcohol-1.png


+ 142 - 0
src/modules/alcohol/alcohol.page.vue

@@ -0,0 +1,142 @@
+<script setup lang="ts">
+import { Notify } from '@/platform';
+import { tryOnBeforeMount, tryOnUnmounted, useCountdown } from '@vueuse/core';
+
+import { getAlcoholReportMethod } from '@/request/api';
+import type { Flow, FlowRoute } from '@/request/model';
+import { getRoutePath, useRouteNext } from '@/computable/useRouteNext';
+import { useRouteMeta } from '@/router/hooks/useRouteMeta';
+
+import NavHomeSelect from '@/assets/images/nav-home.select.png?url';
+import alcohol_0 from '@/assets/images/alcohol-0.png?url';
+import alcohol_1 from '@/assets/images/alcohol-1.png?url';
+
+const router = useRouter();
+const actionText = computed(() => `获取健康调理方案`);
+/* 倒计时完成动作 */
+const done = shallowRef<Flow>();
+/* 下一动作可选 */
+const next = shallowRef<Flow>();
+
+const title = useRouteMeta('title');
+const { handle, flow, loading } = useRouteNext({
+  onSuccess(flow) { return load(flow); },
+  onError(error) { Notify.warning(error.message); },
+});
+
+const { remaining, start, stop } = useCountdown(5, {
+  onComplete() { replace(done.value!); },
+  immediate: false,
+});
+const countdown = computed(() => remaining.value.toString().padStart(2, '0'));
+
+tryOnBeforeMount(() => handle());
+tryOnUnmounted(() => stop());
+
+const report = ref<Awaited<ReturnType<typeof getAlcoholReportMethod>>>();
+const tips = '建议您每日饮酒';
+const description = computed(() => report.value?.alcohol?.description?.replace?.(new RegExp(`^${tips}`), '')?.replace?.(/(\S)\s*(或)/, '$1\n$2') ?? '');
+
+async function load(flow: FlowRoute) {
+  stop();
+  report.value = await getAlcoholReportMethod();
+
+  done.value = flow.next.optional ? { title: '返回首页', route: '/screen' } : flow.next;
+  next.value = flow.next.optional ? flow.next : void 0;
+  start(report.value?.alcohol?.description ? 10 : 5);
+}
+
+const replace = (flow: Flow) => router.push({ path: getRoutePath(flow), replace: true });
+</script>
+<template>
+  <div>
+    <div class="page-header flex py-4 px-4">
+      <div class="grow shrink-0 h-full min-w-16"></div>
+      <div class="grow-[3] shrink mx-2 flex flex-col justify-center overflow-hidden">
+        <div class="font-bold text-3xl text-nowrap text-center tracking-wide overflow-ellipsis overflow-hidden">
+          {{ flow?.value.title ?? title }}
+        </div>
+      </div>
+      <div class="grow shrink-0 h-full min-w-16 flex items-center justify-end overflow-hidden">
+        <router-link :to="{ path: '/screen' }" replace>
+          <img class="size-8 object-scale-down" :src="NavHomeSelect" alt="返回首页" />
+        </router-link>
+      </div>
+    </div>
+    <div class="page-content flex flex-col">
+      <van-toast v-if="loading" show type="loading" message="加载中" />
+      <template v-else>
+        <header>
+          <div class="my-6 text-primary text-2xl text-center" v-if="report?.date">报告日期:{{ report.date }}</div>
+        </header>
+        <main class="flex flex-col justify-evenly">
+          <div class="report-wrapper" v-if="report">
+            <div class="card m-6 text-lg" v-if="report.alcohol?.condition">
+              <div v-if="title" class="card__title mb-3 text-primary text-2xl font-bold">您的情况为</div>
+              <div class="card__content">
+                <div class="text-center text-4xl font-bold pre" style="letter-spacing: 4px">
+                  {{ report.alcohol?.condition }}
+                </div>
+              </div>
+            </div>
+            <div class="card m-6 text-lg">
+              <div v-if="title" class="card__title mb-3 text-primary text-2xl">{{ tips }}</div>
+              <div class="card__content">
+                <div class="flex items-center justify-center min-h-32" v-if="report.alcohol?.description">
+                  <div class="text-center text-5xl font-semibold whitespace-pre" style="letter-spacing: 4px; line-height: 1.25em">
+                    {{ description }}
+                  </div>
+                  <img v-if="report.alcohol?.volume?.length" class="image-container object-scale-down" :src="alcohol_1" alt="可饮酒" />
+                </div>
+                <van-empty v-else :image="alcohol_0" image-size="160" description="暂无建议" />
+              </div>
+            </div>
+          </div>
+        </main>
+        <footer class="flex flex-col justify-center items-center gap-4">
+          <van-button v-if="next" class="decorate !text-xl" @click="replace(next)">
+            {{ next.title ?? actionText }}
+          </van-button>
+          <van-button v-if="done" class="decorate !text-xl !text-primary-400" @click="replace(done)">
+            {{ done.title ?? actionText }}
+            <template v-if="remaining">({{ countdown }}s)</template>
+          </van-button>
+        </footer>
+      </template>
+    </div>
+  </div>
+</template>
+<style scoped lang="scss">
+header {
+  flex: 1 1 10%;
+}
+
+footer {
+  flex: 1 1 30%;
+}
+
+main {
+  position: relative;
+  flex: 1 1 50%;
+}
+
+.image-container {
+  width: 30vw;
+  height: 30vw;
+  max-width: 216px;
+  max-height: 216px;
+}
+
+.card__content {
+  --van-empty-description-color: #fff;
+  --van-empty-description-margin-top: 36px;
+  --van-empty-description-font-size: 18px;
+}
+</style>
+<style scoped lang="scss">
+.report-wrapper .card {
+  padding: 24px;
+  border-radius: 24px;
+  box-shadow: inset 0 0 80px 0 #34a76b60;
+}
+</style>

+ 2 - 1
src/request/api/questionnaire.api.ts

@@ -1,6 +1,7 @@
 import { useVisitor } from '@/stores';
 import HTTP                                           from '../alova';
 import type { QuestionnaireStorage }                  from '../model';
+import { getAnalysisExtendFlowValue }                 from '../model';
 import { fromQuestionnaireData, toQuestionnaireData } from '../model';
 
 
@@ -11,7 +12,7 @@ export function questionnaireMethod(data = []) {
   if ( !data?.length ) { storage = { questions: [] }; }
   return HTTP.Post(
     `/fdhb-tablet/dialogueManage/dialog/${ Visitor.patientId }/${ Visitor.resultId }`,
-    { ...toQuestionnaireData (data, storage), asyncTongueResult: true },
+    { ...toQuestionnaireData (data, storage), asyncTongueResult: true, extendFlow: getAnalysisExtendFlowValue() },
     {
       meta: { ignoreException: true },
       transform(data: Record<string, any>, headers) {

+ 14 - 0
src/request/api/report.api.ts

@@ -55,6 +55,20 @@ export function getReportMethod(id: string) {
   });
 }
 
+export function getAlcoholReportMethod() {
+  const Visitor = useVisitor();
+  const params = { healthAnalysisReportId: Visitor.reportId, patientId: Visitor.patientId };
+  return HTTP.Get(`/fdhb-tablet/analysisManage/getHealRepDetailById`, {
+    params,
+    transform(data: Record<string, any>) {
+      return {
+        date: data?.alcoholAnalysisReportDate /*?? data?.reportTime*/,
+        alcohol: fromAnalysisModel('alcohol', data),
+      }
+    }
+  });
+}
+
 export function updateReportMethod(id: string, data: Record<string, any>) {
   const Visitor = useVisitor();
   const params = {

+ 20 - 2
src/request/model/analysis.model.ts

@@ -2,10 +2,11 @@ import { groupBy } from '@/tools';
 
 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;
 export function fromAnalysisModel(
-  mode: 'tongue' | 'face' | 'pulse',
+  mode: 'tongue' | 'face' | 'pulse' | 'alcohol',
   data: Record<string, any>
-): AnalysisModel | PulseAnalysisModel {
+): AnalysisModel | AlcoholAnalysisModel | PulseAnalysisModel {
   let model: AnalysisModel;
   switch (mode) {
     case 'tongue':
@@ -16,6 +17,8 @@ export function fromAnalysisModel(
       break;
     case 'pulse':
       return fromPulseAnalysisModel(data);
+    case 'alcohol':
+      return fromAlcoholAnalysisModel(data?.extendFlowData);
   }
 
   const group = groupBy<AnalysisException>(model.exception, (item) => item.cover ?? '');
@@ -29,6 +32,12 @@ export interface PulseAnalysisModel extends Pick<Awaited<ReturnType<typeof Bridg
   url?: string;
 }
 
+export interface AlcoholAnalysisModel {
+  condition: string;
+  description: string;
+  volume: [min: number, max?: number];
+}
+
 export interface AnalysisModel {
   table: {
     columns: string[];
@@ -77,6 +86,15 @@ 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) ?? [];
+  return {
+    condition: data?.alcoholCondition,
+    description: data?.alcoholCapacity,
+    volume: volume.sort((a, b) => a - b),
+  };
+}
+
 function fromTongueAnalysisModel(data: Record<string, any>): AnalysisModel {
   const exception: AnalysisException[] = [];
   const fromTongueException = fromAnalysisException(exception);

+ 12 - 0
src/request/model/flow.model.ts

@@ -9,9 +9,14 @@ const Routes = {
   'health_analysis': /* 健康报告页 */ '/report',
   'pulse_upload': /* 脉诊页 */ '/pulse',
   'pulse_upload_result': /* 脉诊结果页 */ '/pulse/result',
+  'alcohol_upload_result': /* 酒精结果页 */ '/alcohol/result',
 
   'screen': ROUTE_START,
 } as const;
+const analysisFlowMap = new Map<FlowKey, string>([
+  ['/alcohol/result', 'alcohol'],
+]);
+const ANALYSIS_EXTEND_FLOW = 'ANALYSIS_EXTEND_FLOW';
 
 export type FlowKey = (typeof Routes)[keyof typeof Routes];
 
@@ -41,13 +46,16 @@ export class FlowMap extends Map<FlowKey, Flow> {
     options.unshift(ROUTE_START);
     options.push('screen?返回首页');
 
+    const analysisFlowSet = new Set<string>();
     for ( let i = 1; i < options.length; i++ ) {
       const path = options[i];
       const optional = path.endsWith('?');
       const [name, title] = path.split('?');
       const route = (options[i] = getPath(name));
       this.set(<FlowKey>options[i - 1], { route, optional, title: title || void 0});
+      if (analysisFlowMap.has(route)) analysisFlowSet.add(analysisFlowMap.get(route)!);
     }
+    sessionStorage.setItem(ANALYSIS_EXTEND_FLOW, Array.from(analysisFlowSet).filter(Boolean).join(','));
   }
 
   parse(key: FlowKey): FlowRoute {
@@ -73,3 +81,7 @@ export class FlowMap extends Map<FlowKey, Flow> {
 export function getPath(value?: string): FlowKey {
   return Routes[value as keyof typeof Routes];
 }
+
+export function getAnalysisExtendFlowValue(key = ANALYSIS_EXTEND_FLOW) {
+  return sessionStorage.getItem(key) || ''
+}

+ 1 - 1
src/router/hooks/useRouteMeta.ts

@@ -9,7 +9,7 @@ export interface ReactiveRouteOptionsWithTransform<V, R> {
 }
 
 export function useRouteMeta<T extends RouteMeta = RouteMeta, K = T>(
-  name: string,
+  name: keyof T,
   defaultValue?: MaybeRefOrGetter,
   options: ReactiveRouteOptionsWithTransform<T, K> = {},
 ): Ref<K> {

+ 1 - 0
src/router/index.ts

@@ -11,6 +11,7 @@ const router = createRouter({
     { path: '/camera', component: () => import('@/modules/camera/camera.page.vue'), meta: { title: '拍摄' } },
     { path: '/camera/result', component: () => import('@/modules/camera/camera-result.page.vue'), meta: { title: '拍摄完成' } },
     { path: '/questionnaire', component: () => import('@/modules/questionnaire/page.vue'), meta: { title: '问卷' } },
+    { path: '/alcohol/result', component: () => import('@/modules/alcohol/alcohol.page.vue'), meta: { title: '黄酒建议' } },
     { path: '/report/analysis', component: () => import('@/modules/report/report-analyse.page.vue'), meta: { title: '舌面象分析报告' } },
     { path: '/report/:id/scheme', component: () => import('@/modules/report/scheme.page.vue'), meta: { title: '调理方案' } },
     { path: '/report/:id', component: () => import('@/modules/report/report.page.vue'), meta: { title: '健康分析报告' } },