Phase 0 基础设施:
- statusTag.ts: getStatusInlineStyle() 移除内联 borderRadius/padding/fontSize,仅返回 {background, color}
- 新增 SEVERITY_COLORS + getSeverityStyle() + getSeverityLabel() 统一告警严重程度样式
- variables.scss: 新增 9 个语义颜色别名 ($success/$danger/$warning/$info 等)
- mixins.scss: 新增 status-inline mixin 统一状态标签样式
- 7 个消费者页面添加 @include status-inline CSS 补偿
Phase 1 HIGH 修复 (4 页面):
- P46 随访管理: 移除 getTypeStyle() 硬编码 fontSize,替换文字 Loading 为组件
- P45 咨询详情医护: 添加 Loading/ErrorState 三态模板 + error ref
- P02 健康数据: 添加 loading ref + Loading 组件 + 错误 toast 提示
- P48 告警中心: 替换本地 SEVERITY_COLORS/SEVERITY_LABELS 为 statusTag.ts 导出
Phase 2 全局一致性:
- 2.1 触控补全: 17 页面为可点击元素添加 min-height: $touch-min
- 2.2 字号替换: 19 文件 31 处硬编码 px → Design Token CSS 变量
- 2.3 颜色替换: 18 文件 ~50 处硬编码十六进制 → SCSS 语义变量
- 2.4 elder-mode.scss: 新增 9 个选择器到触控放大清单
Phase 3 LOW 修复:
- 3.1 统一 Loading: 21 页面旧式文字加载 → <Loading> 组件
- 3.2 useElderClass: 8 页面补全长者模式 class 绑定
- 3.3 零散修复: 按钮 44px→48px,诊断记录添加 scroll-view 无限加载
同时新增 UniApp (Vue 3 + Vite) 小程序完整代码库 (146 文件)
94 lines
4.6 KiB
Vue
94 lines
4.6 KiB
Vue
<template>
|
|
<view :class="['detail-page', elderClass]">
|
|
<Loading v-if="loading" text="加载中..." />
|
|
<view v-else-if="!report" class="empty-wrap"><text class="empty-text">报告不存在</text></view>
|
|
<template v-else>
|
|
<view class="detail-card">
|
|
<text class="detail-title">{{ report.report_type }}</text>
|
|
<view class="detail-row">
|
|
<text class="detail-label">报告日期</text>
|
|
<text class="detail-value">{{ report.report_date }}</text>
|
|
</view>
|
|
<view v-if="report.doctor_interpretation" class="detail-row">
|
|
<text class="detail-label">医生解读</text>
|
|
<text class="detail-value">{{ report.doctor_interpretation }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="indicators-card">
|
|
<text class="section-title">检查指标</text>
|
|
<view v-for="item in indicators" :key="item.name" class="indicator-item">
|
|
<view class="indicator-left">
|
|
<text class="indicator-name">{{ item.name }}</text>
|
|
<text class="indicator-value">{{ item.value }}{{ item.unit ? ` ${item.unit}` : '' }}</text>
|
|
</view>
|
|
<view class="indicator-right">
|
|
<text v-if="item.reference_min != null && item.reference_max != null" class="indicator-ref">
|
|
{{ item.reference_min }}~{{ item.reference_max }}
|
|
</text>
|
|
<text :class="['indicator-status', getStatusInfo(item.status).className]">
|
|
{{ getStatusInfo(item.status).text }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
import { getReportDetail, type LabReport } from '@/services/report'
|
|
import { useElderClass } from '@/composables/useElderClass'
|
|
import Loading from '@/components/Loading.vue'
|
|
|
|
interface IndicatorItem { name: string; value: number; unit?: string; reference_min?: number; reference_max?: number; status?: string }
|
|
|
|
const { elderClass } = useElderClass()
|
|
const report = ref<LabReport | null>(null)
|
|
const loading = ref(true)
|
|
|
|
const indicators = computed<IndicatorItem[]>(() => {
|
|
if (!report.value?.indicators || typeof report.value.indicators !== 'object') return []
|
|
return Object.entries(report.value.indicators).map(([name, val]) => ({ name, value: val.value, unit: val.unit, reference_min: val.reference_min, reference_max: val.reference_max, status: val.status }))
|
|
})
|
|
|
|
const getStatusInfo = (status?: string) => {
|
|
if (status === 'high') return { text: '偏高', className: 'high' }
|
|
if (status === 'low') return { text: '偏低', className: 'low' }
|
|
return { text: '正常', className: 'normal' }
|
|
}
|
|
|
|
onLoad((query) => {
|
|
const id = query?.id || ''
|
|
const patientId = uni.getStorageSync('current_patient_id') || ''
|
|
if (!id || !patientId) { loading.value = false; return }
|
|
getReportDetail(patientId, id).then(data => { report.value = data }).catch(() => uni.showToast({ title: '加载失败', icon: 'none' })).finally(() => { loading.value = false })
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.detail-page { min-height: 100vh; background: $bg; padding: 24px; }
|
|
.empty-wrap { @include flex-center; padding: 120px 0; }
|
|
.empty-text { font-size: var(--tk-font-body); color: $tx3; }
|
|
.detail-card { @include card; margin-bottom: 16px; }
|
|
.detail-title { font-size: var(--tk-font-title); font-weight: 600; color: $tx; display: block; margin-bottom: 12px; }
|
|
.detail-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid rgba(0,0,0,0.04); }
|
|
.detail-row:last-child { border-bottom: none; }
|
|
.detail-label { font-size: var(--tk-font-cap); color: $tx3; }
|
|
.detail-value { font-size: var(--tk-font-body); color: $tx; flex: 1; text-align: right; }
|
|
.indicators-card { @include card; }
|
|
.section-title { font-size: var(--tk-font-body); font-weight: 500; color: $tx; margin-bottom: 12px; display: block; }
|
|
.indicator-item { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid rgba(0,0,0,0.04); }
|
|
.indicator-item:last-child { border-bottom: none; }
|
|
.indicator-left { flex: 1; }
|
|
.indicator-name { font-size: var(--tk-font-cap); color: $tx2; display: block; }
|
|
.indicator-value { font-size: var(--tk-font-body); color: $tx; display: block; margin-top: 2px; }
|
|
.indicator-right { text-align: right; flex-shrink: 0; }
|
|
.indicator-ref { font-size: var(--tk-font-micro); color: $tx3; display: block; }
|
|
.indicator-status { font-size: var(--tk-font-cap); display: block; margin-top: 2px; }
|
|
.indicator-status.high { color: $wrn; }
|
|
.indicator-status.low { color: $info; }
|
|
.indicator-status.normal { color: $acc; }
|
|
</style>
|