diff --git a/apps/web/src/pages/health/PatientDetail.tsx b/apps/web/src/pages/health/PatientDetail.tsx
index 29d2277..4b35286 100644
--- a/apps/web/src/pages/health/PatientDetail.tsx
+++ b/apps/web/src/pages/health/PatientDetail.tsx
@@ -27,6 +27,9 @@ import { LabReportsTab } from './components/LabReportsTab';
import { HealthRecordsTab } from './components/HealthRecordsTab';
import { FollowUpTab } from './components/FollowUpTab';
import { DeviceReadingsTab } from './components/DeviceReadingsTab';
+import { PointsAccountTab } from './components/PointsAccountTab';
+import { AiSuggestionTab } from './components/AiSuggestionTab';
+import { DailyMonitoringTab } from './components/DailyMonitoringTab';
import { GENDER_OPTIONS, BLOOD_TYPE_OPTIONS } from '../../constants/health';
import { useThemeMode } from '../../hooks/useThemeMode';
@@ -280,6 +283,7 @@ export default function PatientDetail() {
{ key: 'device', label: '设备数据', children: },
{ key: 'lab', label: '化验报告', children: },
{ key: 'records', label: '健康档案', children: },
+ { key: 'daily', label: '日常监测', children: },
]}
/>
) : null,
@@ -289,6 +293,16 @@ export default function PatientDetail() {
label: '随访记录',
children: id ? : null,
},
+ {
+ key: 'points',
+ label: '积分账户',
+ children: id ? : null,
+ },
+ {
+ key: 'ai',
+ label: 'AI 建议',
+ children: id ? : null,
+ },
]}
/>
diff --git a/apps/web/src/pages/health/components/AiSuggestionTab.tsx b/apps/web/src/pages/health/components/AiSuggestionTab.tsx
new file mode 100644
index 0000000..7d161da
--- /dev/null
+++ b/apps/web/src/pages/health/components/AiSuggestionTab.tsx
@@ -0,0 +1,163 @@
+import { useEffect, useState, useCallback } from 'react';
+import { Table, Tag, Button, Space, message, Typography } from 'antd';
+import {
+ CheckCircleOutlined,
+ CloseCircleOutlined,
+ ExclamationCircleOutlined,
+ WarningOutlined,
+} from '@ant-design/icons';
+import { suggestionApi, type SuggestionItem } from '../../../api/ai/suggestions';
+
+const { Text } = Typography;
+
+const RISK_CONFIG: Record = {
+ low: { color: 'green', text: '低风险', icon: },
+ medium: { color: 'orange', text: '中风险', icon: },
+ high: { color: 'red', text: '高风险', icon: },
+};
+
+const TYPE_MAP: Record = {
+ followup: '随访建议',
+ appointment: '预约建议',
+ alert: '预警通知',
+};
+
+const STATUS_CONFIG: Record = {
+ pending: { color: 'orange', text: '待审批' },
+ approved: { color: 'green', text: '已批准' },
+ rejected: { color: 'red', text: '已拒绝' },
+ executed: { color: 'blue', text: '已执行' },
+ expired: { color: 'default', text: '已过期' },
+};
+
+interface Props {
+ patientId: string;
+}
+
+export function AiSuggestionTab({ patientId }: Props) {
+ const [data, setData] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [actionLoading, setActionLoading] = useState(null);
+
+ const fetchData = useCallback(async () => {
+ setLoading(true);
+ try {
+ // 加载待审批的建议(后续可扩展为按患者过滤)
+ const result = await suggestionApi.list({ status: 'pending' });
+ setData(result.data || []);
+ } catch {
+ // 静默
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ fetchData();
+ }, [fetchData, patientId]);
+
+ const handleAction = async (id: string, action: 'approve' | 'reject') => {
+ setActionLoading(id);
+ try {
+ await suggestionApi.approve(id, action);
+ message.success(action === 'approve' ? '已批准' : '已拒绝');
+ fetchData();
+ } catch {
+ message.error('操作失败');
+ } finally {
+ setActionLoading(null);
+ }
+ };
+
+ const columns = [
+ {
+ title: '风险等级',
+ dataIndex: 'risk_level',
+ key: 'risk_level',
+ width: 100,
+ render: (v: string) => {
+ const cfg = RISK_CONFIG[v] || { color: 'default', text: v, icon: null };
+ return {cfg.icon} {cfg.text};
+ },
+ },
+ {
+ title: '类型',
+ dataIndex: 'suggestion_type',
+ key: 'suggestion_type',
+ width: 100,
+ render: (v: string) => {TYPE_MAP[v] || v},
+ },
+ {
+ title: '建议原因',
+ key: 'reason',
+ render: (_: unknown, record: SuggestionItem) => {
+ const params = record.params as Record | null;
+ return (
+
+ {(params?.reason as string) || (params?.message as string) || '-'}
+
+ );
+ },
+ },
+ {
+ title: '状态',
+ dataIndex: 'status',
+ key: 'status',
+ width: 90,
+ render: (v: string) => {
+ const cfg = STATUS_CONFIG[v] || { color: 'default', text: v };
+ return {cfg.text};
+ },
+ },
+ {
+ title: '时间',
+ dataIndex: 'created_at',
+ key: 'created_at',
+ width: 160,
+ render: (v: string) => v ? new Date(v).toLocaleString('zh-CN') : '-',
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 160,
+ render: (_: unknown, record: SuggestionItem) => {
+ if (record.status !== 'pending') return null;
+ return (
+
+ }
+ loading={actionLoading === record.id}
+ onClick={() => handleAction(record.id, 'approve')}
+ >
+ 批准
+
+ }
+ loading={actionLoading === record.id}
+ onClick={() => handleAction(record.id, 'reject')}
+ >
+ 拒绝
+
+
+ );
+ },
+ },
+ ];
+
+ return (
+
+
`共 ${t} 条` }}
+ />
+
+ );
+}