- 医生端 18 个页面全部接入(首页/待办/告警/咨询/透析/随访/ 患者/处方/报告及其详情和创建页) - 商城子包 3 页面(商品详情/积分兑换/订单) - 患者端剩余页面(AI报告/文章/活动/设备同步/登录/随访详情/ 报告详情/知情同意/诊断/透析处方/透析记录/家庭成员添加) - 页面覆盖率:22/59 (37%) → 58/58 (100%) - useElderClass hook 统一接入模式,零样板代码 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
156 lines
6.5 KiB
TypeScript
156 lines
6.5 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { View, Text, ScrollView } from '@tarojs/components';
|
|
import Taro, { useRouter } from '@tarojs/taro';
|
|
import * as doctorApi from '@/services/doctor';
|
|
import Loading from '@/components/Loading';
|
|
import { useElderClass } from '../../../../hooks/useElderClass';
|
|
import './index.scss';
|
|
|
|
export default function PatientDetail() {
|
|
const router = useRouter();
|
|
const patientId = router.params.id || '';
|
|
const modeClass = useElderClass();
|
|
const [patient, setPatient] = useState<doctorApi.PatientDetail | null>(null);
|
|
const [summary, setSummary] = useState<doctorApi.HealthSummary | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (patientId) loadData();
|
|
}, [patientId]);
|
|
|
|
const loadData = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const [p, s] = await Promise.all([
|
|
doctorApi.getPatient(patientId),
|
|
doctorApi.getHealthSummary(patientId),
|
|
]);
|
|
setPatient(p);
|
|
setSummary(s);
|
|
} catch {
|
|
Taro.showToast({ title: '加载失败', icon: 'none' });
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const getGenderLabel = (g?: string) => (g === 'male' ? '男' : g === 'female' ? '女' : g || '-');
|
|
const calcAge = (bd?: string) => {
|
|
if (!bd) return '-';
|
|
const diff = Date.now() - new Date(bd).getTime();
|
|
return Math.floor(diff / (365.25 * 24 * 3600 * 1000));
|
|
};
|
|
|
|
if (loading) return <Loading />;
|
|
if (!patient) return <View className={`error-text ${modeClass}`}><Text>患者信息加载失败</Text></View>;
|
|
|
|
return (
|
|
<ScrollView scrollY className={`patient-detail ${modeClass}`}>
|
|
{/* 基本信息 */}
|
|
<View className='section'>
|
|
<View className='section-header'>
|
|
<Text className='section-title'>基本信息</Text>
|
|
</View>
|
|
<View className='info-grid'>
|
|
<View className='info-item'><Text className='info-label'>姓名</Text><Text className='info-value'>{patient.name}</Text></View>
|
|
<View className='info-item'><Text className='info-label'>性别</Text><Text className='info-value'>{getGenderLabel(patient.gender)}</Text></View>
|
|
<View className='info-item'><Text className='info-label'>年龄</Text><Text className='info-value'>{calcAge(patient.birth_date)}岁</Text></View>
|
|
{patient.blood_type && <View className='info-item'><Text className='info-label'>血型</Text><Text className='info-value'>{patient.blood_type}</Text></View>}
|
|
</View>
|
|
</View>
|
|
|
|
{/* 医疗信息 */}
|
|
{(patient.allergy_history || patient.medical_history_summary) && (
|
|
<View className='section'>
|
|
<Text className='section-title'>医疗信息</Text>
|
|
{patient.allergy_history && (
|
|
<View className='warning-card'>
|
|
<Text className='warning-label'>过敏史</Text>
|
|
<Text className='warning-text'>{patient.allergy_history}</Text>
|
|
</View>
|
|
)}
|
|
{patient.medical_history_summary && (
|
|
<View className='info-block'>
|
|
<Text className='info-block-label'>病史摘要</Text>
|
|
<Text className='info-block-text'>{patient.medical_history_summary}</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
)}
|
|
|
|
{/* 健康摘要 */}
|
|
{summary && (
|
|
<View className='section'>
|
|
<Text className='section-title'>健康概览</Text>
|
|
{summary.latest_vital_signs && (
|
|
<View className='vitals-grid'>
|
|
{summary.latest_vital_signs.systolic_bp != null && (
|
|
<View className='vital-item'>
|
|
<Text className='vital-value'>{summary.latest_vital_signs.systolic_bp}/{summary.latest_vital_signs.diastolic_bp}</Text>
|
|
<Text className='vital-label'>血压 mmHg</Text>
|
|
</View>
|
|
)}
|
|
{summary.latest_vital_signs.heart_rate != null && (
|
|
<View className='vital-item'>
|
|
<Text className='vital-value'>{summary.latest_vital_signs.heart_rate}</Text>
|
|
<Text className='vital-label'>心率 bpm</Text>
|
|
</View>
|
|
)}
|
|
{summary.latest_vital_signs.weight != null && (
|
|
<View className='vital-item'>
|
|
<Text className='vital-value'>{summary.latest_vital_signs.weight}</Text>
|
|
<Text className='vital-label'>体重 kg</Text>
|
|
</View>
|
|
)}
|
|
{summary.latest_vital_signs.blood_sugar != null && (
|
|
<View className='vital-item'>
|
|
<Text className='vital-value'>{summary.latest_vital_signs.blood_sugar}</Text>
|
|
<Text className='vital-label'>血糖 mmol/L</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
)}
|
|
{summary.pending_follow_ups != null && summary.pending_follow_ups > 0 && (
|
|
<View className='stat-row'>
|
|
<Text className='stat-label'>待处理随访</Text>
|
|
<Text className='stat-value stat-value--warn'>{summary.pending_follow_ups} 项</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
)}
|
|
|
|
{/* 近期化验 */}
|
|
{summary?.latest_lab_report && (
|
|
<View className='section'>
|
|
<Text className='section-title'>近期化验</Text>
|
|
<View
|
|
className='lab-item'
|
|
onClick={() => Taro.navigateTo({ url: `/pages/doctor/report/detail/index?patientId=${patientId}&id=${summary.latest_lab_report!.id}` })}
|
|
>
|
|
<View className='lab-item__header'>
|
|
<Text className='lab-item__type'>{summary.latest_lab_report.report_type}</Text>
|
|
<Text className='lab-item__date'>{summary.latest_lab_report.report_date}</Text>
|
|
</View>
|
|
{(summary.latest_lab_report.abnormal_count ?? 0) > 0 && (
|
|
<Text className='lab-item__abnormal'>{summary.latest_lab_report.abnormal_count} 项异常</Text>
|
|
)}
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
{/* 快捷操作 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>操作</Text>
|
|
<View className='action-buttons'>
|
|
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/doctor/report/index?patientId=${patientId}` })}>
|
|
<Text>查看化验报告</Text>
|
|
</View>
|
|
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/doctor/followup/index?patientId=${patientId}` })}>
|
|
<Text>随访记录</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
);
|
|
}
|