From 8dd269d15015493b2b2e9699e25b5dc98c88e1ce Mon Sep 17 00:00:00 2001 From: iven Date: Fri, 1 May 2026 18:17:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20=E6=82=A3=E8=80=85=E5=BF=AB?= =?UTF-8?q?=E6=8D=B7=E5=AF=BC=E8=88=AA=20+=20=E5=88=97=E8=A1=A8=E9=A1=B5?= =?UTF-8?q?=20URL=20patient=5Fid=20=E7=AD=9B=E9=80=89=20+=20AI=20=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=82=A3=E8=80=85=20Link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 患者详情页增加快捷导航卡片(预约/咨询/透析/随访/AI) - 5 个列表页支持 URL ?patient_id=xxx 自动筛选 - AI 分析列表患者 ID 改为可点击 Link 跳转详情 --- apps/web/src/pages/health/AiAnalysisList.tsx | 14 ++++++++++---- apps/web/src/pages/health/AppointmentList.tsx | 5 ++++- apps/web/src/pages/health/ConsultationList.tsx | 7 +++++-- apps/web/src/pages/health/DialysisManageList.tsx | 16 ++++++++++++++-- apps/web/src/pages/health/FollowUpTaskList.tsx | 7 ++++++- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/apps/web/src/pages/health/AiAnalysisList.tsx b/apps/web/src/pages/health/AiAnalysisList.tsx index b30e316..12be95e 100644 --- a/apps/web/src/pages/health/AiAnalysisList.tsx +++ b/apps/web/src/pages/health/AiAnalysisList.tsx @@ -1,4 +1,5 @@ import { useEffect, useState, useCallback, useMemo } from 'react'; +import { useSearchParams, Link } from 'react-router-dom'; import { Table, Select, Tag, Space, Button, message, Typography } from 'antd'; import { RobotOutlined, @@ -247,12 +248,15 @@ function SuggestionPanel({ analysisId, isDark }: { analysisId: string; isDark: b // --------------------------------------------------------------------------- export default function AiAnalysisList() { + const [searchParams] = useSearchParams(); + const urlPatientId = searchParams.get('patient_id'); const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); - const [query, setQuery] = useState<{ page: number; page_size: number; analysis_type?: string }>({ + const [query, setQuery] = useState<{ page: number; page_size: number; analysis_type?: string; patient_id?: string }>({ page: 1, page_size: 20, + patient_id: urlPatientId || undefined, }); const [expandedId, setExpandedId] = useState(null); const [detail, setDetail] = useState(null); @@ -313,12 +317,14 @@ export default function AiAnalysisList() { ), }, { - title: '患者 ID', + title: '患者', dataIndex: 'patient_id', key: 'patient_id', - width: 120, + width: 140, render: (v: string) => ( - {v.slice(0, 8)} + + {v.slice(0, 8)} + ), }, { diff --git a/apps/web/src/pages/health/AppointmentList.tsx b/apps/web/src/pages/health/AppointmentList.tsx index 36fcb14..fcee3c8 100644 --- a/apps/web/src/pages/health/AppointmentList.tsx +++ b/apps/web/src/pages/health/AppointmentList.tsx @@ -1,4 +1,5 @@ import { useState, useCallback, useEffect, useMemo } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { Table, Button, @@ -83,6 +84,8 @@ interface AppointmentFilters { } export default function AppointmentList() { + const [searchParams] = useSearchParams(); + const urlPatientId = searchParams.get('patient_id'); const [drawerOpen, setDrawerOpen] = useState(false); const [submitting, setSubmitting] = useState(false); @@ -104,7 +107,7 @@ export default function AppointmentList() { page_size: pageSize, status: filters.status || undefined, date: dateStart === dateEnd ? dateStart : undefined, - patient_id: undefined, // 后端暂不支持 patientSearch 文本搜索 + patient_id: urlPatientId || undefined, }); }, [], diff --git a/apps/web/src/pages/health/ConsultationList.tsx b/apps/web/src/pages/health/ConsultationList.tsx index 31fd5b7..542f5c6 100644 --- a/apps/web/src/pages/health/ConsultationList.tsx +++ b/apps/web/src/pages/health/ConsultationList.tsx @@ -12,7 +12,7 @@ import { } from 'antd'; import { PlusOutlined, CloseCircleOutlined } from '@ant-design/icons'; import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { consultationApi, type Session, type CreateSessionReq } from '../../api/health/consultations'; import { StatusTag } from './components/StatusTag'; import { PatientSelect } from './components/PatientSelect'; @@ -49,6 +49,8 @@ interface ConsultationFilters { export default function ConsultationList() { const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const urlPatientId = searchParams.get('patient_id'); // Close session const [closingId, setClosingId] = useState(null); @@ -63,13 +65,14 @@ export default function ConsultationList() { async (page: number, pageSize: number, filters: ConsultationFilters) => { const params: Record = { page, page_size: pageSize }; if (filters.status) params.status = filters.status; + if (urlPatientId) params.patient_id = urlPatientId; if (filters.dateRange) { params.created_start = filters.dateRange[0]; params.created_end = filters.dateRange[1]; } return consultationApi.listSessions(params as Parameters[0]); }, - [], + [urlPatientId], ); const { diff --git a/apps/web/src/pages/health/DialysisManageList.tsx b/apps/web/src/pages/health/DialysisManageList.tsx index 589c5bd..070c37a 100644 --- a/apps/web/src/pages/health/DialysisManageList.tsx +++ b/apps/web/src/pages/health/DialysisManageList.tsx @@ -1,5 +1,6 @@ -import { useState, useCallback, useMemo } from 'react'; +import { useState, useCallback, useMemo, useEffect } from 'react'; import { Table, Tag, Button, Modal, Form, InputNumber, DatePicker, Select, message, Popconfirm, Space, Input } from 'antd'; +import { useSearchParams } from 'react-router-dom'; import { PlusOutlined, EditOutlined, DeleteOutlined, AuditOutlined } from '@ant-design/icons'; import { dayjs } from '../../utils/dayjs'; import type { Dayjs } from 'dayjs'; @@ -27,7 +28,9 @@ interface PatientOption { } export default function DialysisManageList() { - const [selectedPatientId, setSelectedPatientId] = useState(null); + const [searchParams] = useSearchParams(); + const urlPatientId = searchParams.get('patient_id'); + const [selectedPatientId, setSelectedPatientId] = useState(urlPatientId); const [patientOptions, setPatientOptions] = useState([]); const [patientSearch, setPatientSearch] = useState(''); const [modalOpen, setModalOpen] = useState(false); @@ -38,6 +41,15 @@ export default function DialysisManageList() { const [reviewRecord, setReviewRecord] = useState(null); const [reviewSubmitting, setReviewSubmitting] = useState(false); + // URL 带了 patient_id 时预加载患者选项 + useEffect(() => { + if (urlPatientId) { + patientApi.get(urlPatientId).then((p) => { + if (p) setPatientOptions([{ id: p.id, name: p.name }]); + }).catch(() => {}); + } + }, [urlPatientId]); + const searchPatients = async (keyword: string) => { if (!keyword.trim()) { setPatientOptions([]); diff --git a/apps/web/src/pages/health/FollowUpTaskList.tsx b/apps/web/src/pages/health/FollowUpTaskList.tsx index bc568a0..f4da370 100644 --- a/apps/web/src/pages/health/FollowUpTaskList.tsx +++ b/apps/web/src/pages/health/FollowUpTaskList.tsx @@ -1,4 +1,5 @@ import { useState, useCallback } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { Table, Select, @@ -61,6 +62,9 @@ interface AssignFormValues { } export default function FollowUpTaskList() { + const [searchParams] = useSearchParams(); + const urlPatientId = searchParams.get('patient_id'); + // --- Paginated data with usePaginatedData --- const fetchFn = useCallback( async (page: number, pageSize: number, filters: FollowUpFilters) => { @@ -68,13 +72,14 @@ export default function FollowUpTaskList() { if (filters.status) params.status = filters.status; if (filters.followUpType) params.follow_up_type = filters.followUpType; if (filters.assigneeId) params.assigned_to = filters.assigneeId; + if (urlPatientId) params.patient_id = urlPatientId; if (filters.dateRange) { params.planned_date_start = filters.dateRange[0]; params.planned_date_end = filters.dateRange[1]; } return followUpApi.listTasks(params as Parameters[0]); }, - [], + [urlPatientId], ); const {