|
|
@@ -1,7 +1,5 @@
|
|
|
<script setup lang="ts">
|
|
|
-import type { ReportModel } from '@/model';
|
|
|
-import ReportAnalysisWidgetFace from '@/widgets/ReportAnalysisWidgetFace.vue';
|
|
|
-import ReportAnalysisWidgetTongue from '@/widgets/ReportAnalysisWidgetTongue.vue';
|
|
|
+import type { ReportModel } from '@/model';
|
|
|
|
|
|
import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons-vue';
|
|
|
import { h } from 'vue';
|
|
|
@@ -13,9 +11,11 @@ const props = withDefaults(defineProps<{
|
|
|
title?: string;
|
|
|
|
|
|
collapsible?: boolean;
|
|
|
+ hideTitle?: boolean;
|
|
|
}>(), {
|
|
|
title: '健康分析报告',
|
|
|
collapsible: true,
|
|
|
+ hideTitle: false,
|
|
|
});
|
|
|
|
|
|
const container = ref<HTMLDivElement | null>(null);
|
|
|
@@ -24,14 +24,32 @@ const collapse = (value: boolean) => {
|
|
|
collapsed.value = value;
|
|
|
container.value?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' });
|
|
|
};
|
|
|
+
|
|
|
+const tongueExceptionPanelHeight = window.innerHeight * 0.8;
|
|
|
+const openTongueExceptionAnalysis = ref(false);
|
|
|
+const tongueExceptionPanelKeys = ref<string[]>([]);
|
|
|
+const tongueExceptionData = computed(() => {
|
|
|
+ const keys = [ 'tongueColor', 'tongueCoatingColor', 'tongueShape', 'tongueCoating', 'bodyFluid', 'sublingualVein' ];
|
|
|
+ const data: Record<string, string>[] = [];
|
|
|
+ for ( const key of keys ) {
|
|
|
+ const list = props.dataset?.[ key ]?.actualList ?? [];
|
|
|
+ for ( const item of list ) if ( item?.contrast && item.contrast !== 's' ) data.push(item);
|
|
|
+ }
|
|
|
+ tongueExceptionPanelKeys.value = data.map(t => t.actualValue);
|
|
|
+ return data;
|
|
|
+});
|
|
|
+
|
|
|
+defineExpose({
|
|
|
+ elementTarget: container,
|
|
|
+});
|
|
|
</script>
|
|
|
<template>
|
|
|
<div class="card report-card" ref="container">
|
|
|
- <div class="card__header sticky flex justify-between items-center">
|
|
|
+ <div v-if="!props.hideTitle" class="card__header sticky flex justify-between items-center">
|
|
|
<div class="card__title">
|
|
|
<span>{{ props.title }}</span>
|
|
|
<a-spin v-if="props.loading" size="small" style="margin-left: 4px;" />
|
|
|
- <a-space style="margin-left: 8px">
|
|
|
+ <a-space style="margin-left: 8px" v-if="collapsible">
|
|
|
<a-tooltip :title="collapsed ? '展开' : '收起'">
|
|
|
<a-button
|
|
|
shape="circle" size="small" :icon="collapsed ? h(FullscreenOutlined) : h(FullscreenExitOutlined)"
|
|
|
@@ -45,14 +63,16 @@ const collapse = (value: boolean) => {
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="card__content" v-if="props.dataset.id">
|
|
|
- <a-card class="card no-bordered" size="small" style="background-color: #f7f7f7">
|
|
|
+ <div style="padding: 8px 0;background-color:#fff;" class="sticky top-0 z-10">
|
|
|
+ <span>报告日期:</span>
|
|
|
+ <slot name="select">
|
|
|
+ <span style="margin-right: 8px;">{{ props.dataset.time }}</span>
|
|
|
+ </slot>
|
|
|
+ <slot name="analysis"></slot>
|
|
|
+ <a-spin v-if="props.hideTitle && props.loading" size="small" style="margin-left: 4px;" />
|
|
|
+ </div>
|
|
|
+ <a-card class="card no-bordered background" size="small">
|
|
|
<a-descriptions :column="3">
|
|
|
- <a-descriptions-item v-if="props.dataset.time" label="报告日期" :span="3">
|
|
|
- <slot name="select">
|
|
|
- <span style="margin-right: 8px;">{{ props.dataset.time }}</span>
|
|
|
- </slot>
|
|
|
- <slot name="analysis"></slot>
|
|
|
- </a-descriptions-item>
|
|
|
<a-descriptions-item v-if="props.dataset.willillStateName" label="健康状态">
|
|
|
{{ props.dataset.willillStateName }}
|
|
|
</a-descriptions-item>
|
|
|
@@ -67,8 +87,8 @@ const collapse = (value: boolean) => {
|
|
|
</a-descriptions-item>
|
|
|
</a-descriptions>
|
|
|
</a-card>
|
|
|
- <template v-if="!collapsed">
|
|
|
- <a-card class="card" size="small">
|
|
|
+ <template v-if="!props.collapsible || !collapsed">
|
|
|
+ <a-card class="card background symptom-card" size="small">
|
|
|
<p v-if="props.dataset.constitutionGroupDefinition">体质: {{ props.dataset.constitutionGroupDefinition }}</p>
|
|
|
<a-descriptions :column="1" bordered size="small" :label-style="{width: '120px'}">
|
|
|
<a-descriptions-item v-if="props.dataset.constitutionGroupGeneralCharacteristics" label="总体特征">
|
|
|
@@ -91,18 +111,132 @@ const collapse = (value: boolean) => {
|
|
|
</a-descriptions-item>
|
|
|
</a-descriptions>
|
|
|
</a-card>
|
|
|
- <ReportAnalysisWidgetTongue v-bind="props" title="舌象分析" />
|
|
|
- <ReportAnalysisWidgetFace v-bind="props" title="面象分析" />
|
|
|
- <a-card class="card symptom-card" size="small" title="中医证素" v-if="props.dataset.factorItems?.length">
|
|
|
+ <a-card class="card background" size="small" title="舌象分析" :loading="props.loading">
|
|
|
+ <template #extra v-if="tongueExceptionData.length">
|
|
|
+ <a-button type="dashed" size="small" danger @click="openTongueExceptionAnalysis=true">异常舌象分析
|
|
|
+ </a-button>
|
|
|
+ <vxe-modal
|
|
|
+ title="异常舌象分析" v-model="openTongueExceptionAnalysis"
|
|
|
+ resize show-maximize :width="600" :height="tongueExceptionPanelHeight"
|
|
|
+ >
|
|
|
+ <a-collapse :bordered="false" v-model:activeKey="tongueExceptionPanelKeys">
|
|
|
+ <a-collapse-panel
|
|
|
+ v-for="(panel, index) in tongueExceptionData" :key="panel.actualValue"
|
|
|
+ :header="panel.actualValue"
|
|
|
+ >
|
|
|
+ <a-descriptions :column="1" bordered size="small" :label-style="{width: '120px',textAlign:'center' }">
|
|
|
+ <a-descriptions-item>
|
|
|
+ <div class="flex flex-row">
|
|
|
+ <a-image v-if="panel.splitImage" :src="panel.splitImage" :width="200" :preview="false" />
|
|
|
+ <a-space v-if="panel.attrs" direction="vertical">
|
|
|
+ <a-tag v-for="v in panel.attrs" color="#cd201f" style="margin-left: 8px;margin-right: 0;">
|
|
|
+ {{ v }}
|
|
|
+ </a-tag>
|
|
|
+ </a-space>
|
|
|
+ </div>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item label="特征">{{ panel.features }}</a-descriptions-item>
|
|
|
+ <a-descriptions-item label="临床意义">{{ panel.clinicalSignificance }}</a-descriptions-item>
|
|
|
+ </a-descriptions>
|
|
|
+ </a-collapse-panel>
|
|
|
+ </a-collapse>
|
|
|
+ </vxe-modal>
|
|
|
+ </template>
|
|
|
+ <a-space class="w-full analysis-wrapper">
|
|
|
+ <a-descriptions :column="3" bordered size="small">
|
|
|
+ <a-descriptions-item style="font-size: 14px;font-weight: 500;">舌象维度</a-descriptions-item>
|
|
|
+ <a-descriptions-item style="font-size: 14px;font-weight: 500;">检测结果</a-descriptions-item>
|
|
|
+ <a-descriptions-item style="font-size: 14px;font-weight: 500;">标准值</a-descriptions-item>
|
|
|
+ <template v-if="props.dataset.tongueColor?.actualList">
|
|
|
+ <a-descriptions-item>舌色</a-descriptions-item>
|
|
|
+ <a-descriptions-item>
|
|
|
+ <span class="tongue-value" v-for="item in props.dataset.tongueColor.actualList" :key="item?.actualValue">
|
|
|
+ <span>{{ item.actualValue }}</span>
|
|
|
+ <span v-if="item.contrast !== 's'">({{ item.contrast }})</span>
|
|
|
+ </span>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item>{{ props.dataset.tongueColor.standardValue }}</a-descriptions-item>
|
|
|
+ </template>
|
|
|
+ <template v-if="props.dataset.tongueCoatingColor?.actualList">
|
|
|
+ <a-descriptions-item>苔色</a-descriptions-item>
|
|
|
+ <a-descriptions-item>
|
|
|
+ <span class="tongue-value" v-for="item in props.dataset.tongueCoatingColor.actualList"
|
|
|
+ :key="item?.actualValue"
|
|
|
+ >
|
|
|
+ <span>{{ item.actualValue }}</span>
|
|
|
+ <span v-if="item.contrast !== 's'">({{ item.contrast }})</span>
|
|
|
+ </span>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item>{{ props.dataset.tongueCoatingColor.standardValue }}</a-descriptions-item>
|
|
|
+ </template>
|
|
|
+ <template v-if="props.dataset.tongueShape?.actualList">
|
|
|
+ <a-descriptions-item>舌形</a-descriptions-item>
|
|
|
+ <a-descriptions-item>
|
|
|
+ <span class="tongue-value" v-for="item in props.dataset.tongueShape.actualList" :key="item?.actualValue">
|
|
|
+ <span>{{ item.actualValue }}</span>
|
|
|
+ <span v-if="item.contrast !== 's'">({{ item.contrast }})</span>
|
|
|
+ </span>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item>{{ props.dataset.tongueShape.standardValue }}</a-descriptions-item>
|
|
|
+ </template>
|
|
|
+ <template v-if="props.dataset.tongueCoating?.actualList">
|
|
|
+ <a-descriptions-item>苔质</a-descriptions-item>
|
|
|
+ <a-descriptions-item>
|
|
|
+ <span class="tongue-value" v-for="item in props.dataset.tongueCoating.actualList" :key="item?.actualValue">
|
|
|
+ <span>{{ item.actualValue }}</span>
|
|
|
+ <span v-if="item.contrast !== 's'">({{ item.contrast }})</span>
|
|
|
+ </span>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item>{{ props.dataset.tongueCoating.standardValue }}</a-descriptions-item>
|
|
|
+ </template>
|
|
|
+ <template v-if="props.dataset.bodyFluid?.actualList">
|
|
|
+ <a-descriptions-item>津液</a-descriptions-item>
|
|
|
+ <a-descriptions-item>
|
|
|
+ <span class="tongue-value" v-for="item in props.dataset.bodyFluid.actualList" :key="item?.actualValue">
|
|
|
+ <span>{{ item.actualValue }}</span>
|
|
|
+ <span v-if="item.contrast !== 's'">({{ item.contrast }})</span>
|
|
|
+ </span>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item>{{ props.dataset.bodyFluid.standardValue }}</a-descriptions-item>
|
|
|
+ </template>
|
|
|
+ <template v-if="props.dataset.sublingualVein?.actualList">
|
|
|
+ <a-descriptions-item>舌下</a-descriptions-item>
|
|
|
+ <a-descriptions-item>
|
|
|
+ <span class="" v-for="item in props.dataset.sublingualVein.actualList" :key="item?.actualValue">
|
|
|
+ <span>{{ item.actualValue }}</span>
|
|
|
+ <span v-if="item.contrast !== 's'">({{ item.contrast }})</span>
|
|
|
+ </span>
|
|
|
+ </a-descriptions-item>
|
|
|
+ <a-descriptions-item>{{ props.dataset.sublingualVein.standardValue }}</a-descriptions-item>
|
|
|
+ </template>
|
|
|
+ </a-descriptions>
|
|
|
+ <a-image :width="200" :height="200" :src="props.dataset.upImg" :preview="false" />
|
|
|
+ <a-image :width="200" :height="200" :src="props.dataset.downImg" :preview="false" />
|
|
|
+ </a-space>
|
|
|
+ </a-card>
|
|
|
+ <template v-if="props.dataset?.faceAnalysisResult">
|
|
|
+ <a-card class="card background" size="small" title="面象分析" :loading="props.loading">
|
|
|
+ <a-space align="start" class="w-full analysis-wrapper">
|
|
|
+ <div>{{ props.dataset.faceAnalysisResult }}</div>
|
|
|
+ <a-image :width="200" :height="200" :src="props.dataset.faceImg" :preview="false" />
|
|
|
+ </a-space>
|
|
|
+ </a-card>
|
|
|
+ </template>
|
|
|
+ <a-card class="card background symptom-card" size="small" title="中医证素"
|
|
|
+ v-if="props.dataset.factorItems?.length"
|
|
|
+ >
|
|
|
<a-descriptions :column="1" bordered size="small">
|
|
|
- <a-descriptions-item v-for="item in props.dataset.factorItems" :key="item.factorItemName"
|
|
|
- :label="item.factorItemName"
|
|
|
+ <a-descriptions-item
|
|
|
+ v-for="item in props.dataset.factorItems" :key="item.factorItemName"
|
|
|
+ :label="item.factorItemName"
|
|
|
>
|
|
|
{{ item.factorItemDescription }}
|
|
|
</a-descriptions-item>
|
|
|
</a-descriptions>
|
|
|
</a-card>
|
|
|
- <a-card class="card symptom-card" size="small" title="中医证型" v-if="props.dataset.diagnoseSyndromes?.length">
|
|
|
+ <a-card class="card background symptom-card" size="small" title="中医证型"
|
|
|
+ v-if="props.dataset.diagnoseSyndromes?.length"
|
|
|
+ >
|
|
|
<a-descriptions :column="1" bordered size="small">
|
|
|
<a-descriptions-item v-for="item in props.dataset.diagnoseSyndromes" :key="item.diagnoseSyndromeName"
|
|
|
:label="item.diagnoseSyndromeName"
|
|
|
@@ -111,6 +245,7 @@ const collapse = (value: boolean) => {
|
|
|
</a-descriptions-item>
|
|
|
</a-descriptions>
|
|
|
</a-card>
|
|
|
+ <slot></slot>
|
|
|
</template>
|
|
|
</div>
|
|
|
<div v-else style="height: 40px;"></div>
|
|
|
@@ -147,5 +282,17 @@ const collapse = (value: boolean) => {
|
|
|
width: 120px;
|
|
|
text-align: center;
|
|
|
}
|
|
|
+
|
|
|
+ :deep(.ant-descriptions-item-content) {
|
|
|
+ background-color: #fff;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.analysis-wrapper {
|
|
|
+ :deep(.ant-space-item) {
|
|
|
+ &:first-of-type {
|
|
|
+ flex: auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|