Files
hms/apps/miniprogram/src/pages/doctor/patients/detail/index.tsx
iven 7b5138a630
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
feat(miniprogram): 关怀模式全覆盖 — 58/58 页面 100% 接入
- 医生端 18 个页面全部接入(首页/待办/告警/咨询/透析/随访/
  患者/处方/报告及其详情和创建页)
- 商城子包 3 页面(商品详情/积分兑换/订单)
- 患者端剩余页面(AI报告/文章/活动/设备同步/登录/随访详情/
  报告详情/知情同意/诊断/透析处方/透析记录/家庭成员添加)
- 页面覆盖率:22/59 (37%) → 58/58 (100%)
- useElderClass hook 统一接入模式,零样板代码

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:34:44 +08:00

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>
);
}