feat(web): 患者快捷导航 + 列表页 URL patient_id 筛选 + AI 列表患者 Link
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

- 患者详情页增加快捷导航卡片(预约/咨询/透析/随访/AI)
- 5 个列表页支持 URL ?patient_id=xxx 自动筛选
- AI 分析列表患者 ID 改为可点击 Link 跳转详情
This commit is contained in:
iven
2026-05-01 18:17:07 +08:00
parent 0f32d28ddb
commit 8dd269d150
5 changed files with 39 additions and 10 deletions

View File

@@ -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<AnalysisItem[]>([]);
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<string | null>(null);
const [detail, setDetail] = useState<AnalysisItem | null>(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) => (
<span style={{ fontFamily: 'monospace', fontSize: 12 }}>{v.slice(0, 8)}</span>
<Link to={`/health/patients/${v}`} style={{ fontFamily: 'monospace', fontSize: 12 }}>
{v.slice(0, 8)}
</Link>
),
},
{

View File

@@ -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,
});
},
[],

View File

@@ -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<string | null>(null);
@@ -63,13 +65,14 @@ export default function ConsultationList() {
async (page: number, pageSize: number, filters: ConsultationFilters) => {
const params: Record<string, unknown> = { 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<typeof consultationApi.listSessions>[0]);
},
[],
[urlPatientId],
);
const {

View File

@@ -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<string | null>(null);
const [searchParams] = useSearchParams();
const urlPatientId = searchParams.get('patient_id');
const [selectedPatientId, setSelectedPatientId] = useState<string | null>(urlPatientId);
const [patientOptions, setPatientOptions] = useState<PatientOption[]>([]);
const [patientSearch, setPatientSearch] = useState('');
const [modalOpen, setModalOpen] = useState(false);
@@ -38,6 +41,15 @@ export default function DialysisManageList() {
const [reviewRecord, setReviewRecord] = useState<DialysisRecord | null>(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([]);

View File

@@ -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<typeof followUpApi.listTasks>[0]);
},
[],
[urlPatientId],
);
const {