ReportPreview.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <script setup lang="ts">
  2. import type { PatientModel, ReportModel, ReportSchemeModel } from '@/model';
  3. import { indicatorByReportIdMethod, reportMethod, reportSchemeMethod, reportsMethod } from '@/request/api/report.api';
  4. import ReportCardWidget
  5. from '@/widgets/ReportCardWidget.vue';
  6. import ReportSchemeCardWidget
  7. from '@/widgets/ReportSchemeCardWidget.vue';
  8. import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons-vue';
  9. import { useWatcher } from 'alova/client';
  10. const props = defineProps<{
  11. patient?: Partial<PatientModel>;
  12. report?: Partial<ReportModel>;
  13. scheme?: Partial<ReportSchemeModel>;
  14. }>();
  15. const emits = defineEmits<{
  16. destroy: [];
  17. }>();
  18. const patientId = defineModel<string>('patientId', { default: void 0 });;
  19. const reportId = defineModel<string>('reportId', { default: void 0 });
  20. const showType = defineModel<'analysis' | 'scheme'>('type', { default: 'analysis' });
  21. const { data: report, loading: reportLoading } = useWatcher(
  22. () => reportMethod(reportId.value!),
  23. [ reportId, showType ],
  24. {
  25. initialData: { ...props.report }, immediate: true,
  26. middleware: (_, next) => { if ( reportId.value && showType.value === 'analysis' ) next(); },
  27. },
  28. ).onSuccess(({data}) => {
  29. scrollIntoView();
  30. patientId.value ??= data.patientId;
  31. });
  32. const { data: indicator, loading: indicatorLoading } = useWatcher(
  33. () => indicatorByReportIdMethod(reportId.value!),
  34. [ reportId, showType ],
  35. {
  36. initialData: [], immediate: true,
  37. middleware: (_, next) => { if ( reportId.value && showType.value === 'analysis' ) next(); },
  38. },
  39. );
  40. const { data: reports, loading: reportsLoading } = useWatcher(
  41. () => reportsMethod(patientId.value!),
  42. [ patientId, showType ],
  43. {
  44. initialData: [], immediate: true,
  45. middleware: (_, next) => { if ( patientId.value && showType.value === 'analysis' ) next(); },
  46. },
  47. );
  48. const { data: scheme, loading: schemeLoading } = useWatcher(
  49. () => reportSchemeMethod(reportId.value!),
  50. [ reportId, showType ],
  51. {
  52. initialData: { ...props.scheme }, immediate: true,
  53. middleware: (_, next) => { if ( reportId.value && showType.value === 'scheme' ) next(); },
  54. },
  55. ).onSuccess(() => scrollIntoView());
  56. onBeforeMount(() => {
  57. reportId.value ??= props.report?.id!;
  58. patientId.value ??= props.patient?.id!;
  59. });
  60. const schemeKeys = ref([]);
  61. const collapsed = ref(false);
  62. const expandAll = (value: boolean) => {
  63. collapsed.value = value;
  64. schemeKeys.value = value ? scheme.value.children.map(child => child.id) : [] as string[];
  65. };
  66. const containerRef = ref<HTMLElement & { elementTarget: HTMLElement }>();
  67. function scrollIntoView() {
  68. const el = unref(containerRef.value?.elementTarget) ?? containerRef.value;
  69. el?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' });
  70. }
  71. </script>
  72. <template>
  73. <div id="page-container-scroller" class="page-container flex flex-col">
  74. <template v-if="showType === 'analysis'">
  75. <ReportCardWidget ref="containerRef" :dataset="report" :loading="reportLoading" :collapsible="false" hide-title>
  76. <template #select>
  77. <a-select
  78. v-model:value="reportId" :loading="reportsLoading"
  79. :options="reports" :field-names="{label: 'time', value: 'id'}"
  80. style="min-width: 120px;"
  81. ></a-select>
  82. </template>
  83. <a-card class="card background no-bordered-8" size="small" title="指标信息" :loading="indicatorLoading">
  84. <a-descriptions v-if="indicator?.length" :column="3" size="small">
  85. <a-descriptions-item v-for="item in indicator" :key="item.id" :label="item.name">
  86. <div v-if="item.value">
  87. <span>{{ item.value }}</span>
  88. <span v-if="item.unit">{{ item.unit }}</span>
  89. <ArrowUpOutlined class="icon" v-if="item.records?.[0]?.abnormal === 1" />
  90. <ArrowDownOutlined class="icon" v-if="item.records?.[0]?.abnormal === -1" />
  91. </div>
  92. <span v-else>-</span>
  93. </a-descriptions-item>
  94. </a-descriptions>
  95. <div v-else style="padding-bottom: 8px;">暂无数据</div>
  96. </a-card>
  97. </ReportCardWidget>
  98. </template>
  99. <template v-else-if="showType === 'scheme'">
  100. <div class="card report-card" ref="containerRef">
  101. <div class="card__content" style="padding-bottom: 0;">
  102. <a-card class="card no-bordered background" size="small" :loading="schemeLoading">
  103. <a-descriptions :column="3">
  104. <a-descriptions-item v-if="scheme.time" label="方案日期" :span="3">
  105. <span>{{ scheme.time }}</span>
  106. </a-descriptions-item>
  107. <a-descriptions-item v-if="scheme.process" label="方案进程" :span="3">
  108. <span style="margin-right: 8px;">{{ scheme.process }}</span>
  109. </a-descriptions-item>
  110. </a-descriptions>
  111. </a-card>
  112. </div>
  113. </div>
  114. <ReportSchemeCardWidget :dataset="scheme" :loading="schemeLoading" hide-title />
  115. </template>
  116. </div>
  117. </template>
  118. <style scoped lang="scss">
  119. @import "@/themes/report-card";
  120. .scheme-dataset-collapse-2 {
  121. .header + .header,
  122. .description + .description {
  123. &::before {
  124. content: ":";
  125. margin: 0 4px 0 2px;
  126. color: #333;
  127. }
  128. }
  129. :deep(.preview-wrapper) {
  130. padding: 0;
  131. .ant-descriptions-item-label {
  132. width: 120px;
  133. text-align: center;
  134. }
  135. .ant-carousel {
  136. transform: translateY(-12px)
  137. }
  138. }
  139. }
  140. </style>