refactor(dialysis+health): 透析统计从 erp-health 迁移到 erp-dialysis,消除跨 crate 残留
- erp-dialysis: 新建 dialysis_stats_dto/handler/service,注册 /health/admin/statistics/dialysis 路由 - erp-health: 删除 get_dialysis_statistics 及 helper、DialysisStatisticsResp、 DialysisRecordNotFound/DialysisPrescriptionNotFound、validate_dialysis_status* 及 9 个测试、 DoctorDashboard.pending_dialysis_review、module 路由 - Web: HealthDataStats 移除 dialysis 字段,新增 getDialysisStats() 独立 API, useStatsData 并行 fetch,HealthDataCenter 接受独立 dialysisData prop - 小程序: DoctorDashboard 移除 pending_dialysis_review,医护工作台移除"待审透析"卡片
This commit is contained in:
@@ -21,7 +21,6 @@ const CARDS: CardConfig[] = [
|
||||
];
|
||||
|
||||
const HEALTH_CARDS: CardConfig[] = [
|
||||
{ key: 'pending_dialysis_review', label: '待审透析', initial: '透', route: '/pages/doctor/patients/index' },
|
||||
{ key: 'pending_lab_review', label: '待审化验', initial: '化', route: '/pages/doctor/report/index' },
|
||||
{ key: 'today_appointments', label: '今日预约', initial: '约', route: '/pages/doctor/patients/index' },
|
||||
];
|
||||
|
||||
@@ -8,7 +8,6 @@ export interface DoctorDashboard {
|
||||
unread_messages: number;
|
||||
pending_follow_ups: number;
|
||||
today_consultations: number;
|
||||
pending_dialysis_review: number;
|
||||
pending_lab_review: number;
|
||||
today_appointments: number;
|
||||
}
|
||||
|
||||
@@ -214,7 +214,6 @@ export interface VitalSignsReportRate {
|
||||
}
|
||||
|
||||
export interface HealthDataStats {
|
||||
dialysis: DialysisStatistics;
|
||||
lab_reports: LabReportStatistics;
|
||||
appointments: AppointmentStatistics;
|
||||
vital_signs_report_rate: VitalSignsReportRate;
|
||||
@@ -376,6 +375,14 @@ export const pointsApi = {
|
||||
return data.data;
|
||||
},
|
||||
|
||||
getDialysisStats: async (): Promise<DialysisStatistics> => {
|
||||
const { data } = await client.get<{
|
||||
success: boolean;
|
||||
data: DialysisStatistics;
|
||||
}>('/health/admin/statistics/dialysis');
|
||||
return data.data;
|
||||
},
|
||||
|
||||
getPersonalStats: async (): Promise<PersonalStats> => {
|
||||
const { data } = await client.get<{
|
||||
success: boolean;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useCountUp } from '../../../hooks/useCountUp';
|
||||
import HealthDataCenter from './HealthDataCenter';
|
||||
|
||||
export function AdminDashboard() {
|
||||
const { patientStats, followUpStats, healthDataStats, loading } = useStatsData();
|
||||
const { patientStats, followUpStats, healthDataStats, dialysisStats, loading } = useStatsData();
|
||||
const patientCount = useCountUp(patientStats?.total_patients ?? 0);
|
||||
const appointmentCount = useCountUp(healthDataStats?.appointments?.this_month ?? 0);
|
||||
const doctorCount = useCountUp(0);
|
||||
@@ -72,7 +72,7 @@ export function AdminDashboard() {
|
||||
<Tabs
|
||||
defaultActiveKey="dialysis"
|
||||
items={[
|
||||
{ key: 'dialysis', label: '透析管理', children: <HealthDataCenter data={healthDataStats} tab="dialysis" /> },
|
||||
{ key: 'dialysis', label: '透析管理', children: <HealthDataCenter data={healthDataStats} dialysisData={dialysisStats} tab="dialysis" /> },
|
||||
{ key: 'lab', label: '化验报告', children: <HealthDataCenter data={healthDataStats} tab="lab" /> },
|
||||
{ key: 'appointments', label: '预约分析', children: <HealthDataCenter data={healthDataStats} tab="appointments" /> },
|
||||
{ key: 'vital-signs', label: '体征数据', children: <HealthDataCenter data={healthDataStats} tab="vital-signs" /> },
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import { Row, Col, Card, Statistic, Tag, Typography, Empty } from 'antd';
|
||||
import type { HealthDataStats } from '../../../api/health/points';
|
||||
import type { HealthDataStats, DialysisStatistics } from '../../../api/health/points';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
interface HealthDataCenterProps {
|
||||
data: HealthDataStats | null;
|
||||
dialysisData?: DialysisStatistics | null;
|
||||
tab?: string;
|
||||
}
|
||||
|
||||
function DialysisPanel({ data }: { data: HealthDataStats | null }) {
|
||||
function DialysisPanel({ data }: { data: DialysisStatistics | null | undefined }) {
|
||||
return (
|
||||
<Card type="inner" title={<span style={{ fontSize: 14, fontWeight: 600 }}>透析记录</span>} style={{ borderRadius: 8 }}>
|
||||
<Row gutter={[12, 12]}>
|
||||
<Col span={8}><Statistic title="总记录" value={data?.dialysis.total_records ?? 0} valueStyle={{ fontSize: 20 }} /></Col>
|
||||
<Col span={8}><Statistic title="本月新增" value={data?.dialysis.this_month ?? 0} valueStyle={{ fontSize: 20, color: '#2563eb' }} /></Col>
|
||||
<Col span={8}><Statistic title="待审核" value={data?.dialysis.pending_review ?? 0} valueStyle={{ fontSize: 20, color: '#d97706' }} /></Col>
|
||||
<Col span={8}><Statistic title="总记录" value={data?.total_records ?? 0} valueStyle={{ fontSize: 20 }} /></Col>
|
||||
<Col span={8}><Statistic title="本月新增" value={data?.this_month ?? 0} valueStyle={{ fontSize: 20, color: '#2563eb' }} /></Col>
|
||||
<Col span={8}><Statistic title="待审核" value={data?.pending_review ?? 0} valueStyle={{ fontSize: 20, color: '#d97706' }} /></Col>
|
||||
</Row>
|
||||
<Row gutter={[12, 12]} style={{ marginTop: 12 }}>
|
||||
<Col span={8}><Statistic title="并发症率" value={data?.dialysis.complication_rate ?? 0} suffix="%" precision={1} valueStyle={{ fontSize: 18 }} /></Col>
|
||||
<Col span={8}><Statistic title="平均超滤(ml)" value={data?.dialysis.avg_ultrafiltration ?? 0} precision={0} valueStyle={{ fontSize: 18 }} /></Col>
|
||||
<Col span={8}><Statistic title="平均时长(分)" value={data?.dialysis.avg_duration ?? 0} precision={0} valueStyle={{ fontSize: 18 }} /></Col>
|
||||
<Col span={8}><Statistic title="并发症率" value={data?.complication_rate ?? 0} suffix="%" precision={1} valueStyle={{ fontSize: 18 }} /></Col>
|
||||
<Col span={8}><Statistic title="平均超滤(ml)" value={data?.avg_ultrafiltration ?? 0} precision={0} valueStyle={{ fontSize: 18 }} /></Col>
|
||||
<Col span={8}><Statistic title="平均时长(分)" value={data?.avg_duration ?? 0} precision={0} valueStyle={{ fontSize: 18 }} /></Col>
|
||||
</Row>
|
||||
{(data?.dialysis.type_distribution ?? []).length > 0 && (
|
||||
{(data?.type_distribution ?? []).length > 0 && (
|
||||
<div style={{ marginTop: 12 }}>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>类型分布: </Text>
|
||||
{data!.dialysis.type_distribution.map((item) => (
|
||||
{data!.type_distribution.map((item) => (
|
||||
<Tag key={item.name} color="blue" style={{ marginTop: 4 }}>{item.name}: {item.value}</Tag>
|
||||
))}
|
||||
</div>
|
||||
@@ -101,15 +102,24 @@ function VitalSignsPanel({ data }: { data: HealthDataStats | null }) {
|
||||
);
|
||||
}
|
||||
|
||||
const TAB_PANELS: Record<string, React.FC<{ data: HealthDataStats | null }>> = {
|
||||
dialysis: DialysisPanel,
|
||||
lab: LabPanel,
|
||||
appointments: AppointmentsPanel,
|
||||
'vital-signs': VitalSignsPanel,
|
||||
};
|
||||
export default function HealthDataCenter({ data, dialysisData, tab = 'dialysis' }: HealthDataCenterProps) {
|
||||
if (tab === 'dialysis') {
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<DialysisPanel data={dialysisData} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HealthDataCenter({ data, tab = 'dialysis' }: HealthDataCenterProps) {
|
||||
const Panel = TAB_PANELS[tab];
|
||||
const PANELS: Record<string, React.FC<{ data: HealthDataStats | null }>> = {
|
||||
lab: LabPanel,
|
||||
appointments: AppointmentsPanel,
|
||||
'vital-signs': VitalSignsPanel,
|
||||
};
|
||||
|
||||
const Panel = PANELS[tab];
|
||||
|
||||
if (!Panel) {
|
||||
return <Empty description="未知数据面板" />;
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
type FollowUpStatistics,
|
||||
type PointsStatistics,
|
||||
type HealthDataStats,
|
||||
type DialysisStatistics,
|
||||
} from '../../../api/health/points';
|
||||
|
||||
export interface StatsData {
|
||||
@@ -14,6 +15,7 @@ export interface StatsData {
|
||||
followUpStats: FollowUpStatistics | null;
|
||||
pointsStats: PointsStatistics | null;
|
||||
healthDataStats: HealthDataStats | null;
|
||||
dialysisStats: DialysisStatistics | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
refresh: () => void;
|
||||
@@ -28,6 +30,7 @@ export function useStatsData(): StatsData {
|
||||
const [followUpStats, setFollowUpStats] = useState<FollowUpStatistics | null>(null);
|
||||
const [pointsStats, setPointsStats] = useState<PointsStatistics | null>(null);
|
||||
const [healthDataStats, setHealthDataStats] = useState<HealthDataStats | null>(null);
|
||||
const [dialysisStats, setDialysisStats] = useState<DialysisStatistics | null>(null);
|
||||
|
||||
const fetchAllStats = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -52,9 +55,10 @@ export function useStatsData(): StatsData {
|
||||
tryFetch(pointsApi.getFollowUpStats, setFollowUpStats, '随访'),
|
||||
tryFetch(pointsApi.getStatistics, setPointsStats, '积分'),
|
||||
tryFetch(pointsApi.getHealthDataStats, setHealthDataStats, '健康数据'),
|
||||
tryFetch(pointsApi.getDialysisStats, setDialysisStats, '透析'),
|
||||
]);
|
||||
|
||||
if (hasAnyError && errors.length === 5) {
|
||||
if (hasAnyError && errors.length === 6) {
|
||||
setError('加载统计数据失败');
|
||||
}
|
||||
|
||||
@@ -66,7 +70,7 @@ export function useStatsData(): StatsData {
|
||||
}, [fetchAllStats]);
|
||||
|
||||
return {
|
||||
patientStats, consultationStats, followUpStats, pointsStats, healthDataStats,
|
||||
patientStats, consultationStats, followUpStats, pointsStats, healthDataStats, dialysisStats,
|
||||
loading, error, refresh: fetchAllStats,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user