feat: 医生端Web后台 + SignalR实时问诊
- 新增 doctor_web/ React前端 (dashboard/患者/问诊/报告/随访) - 后端新增 doctor_endpoints (14个医生API) + ConsultationHub (SignalR) - Flutter端 SignalR 替换轮询实现实时聊天
This commit is contained in:
70
doctor_web/src/components/layout/DoctorLayout.tsx
Normal file
70
doctor_web/src/components/layout/DoctorLayout.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { NavLink, Outlet, useLocation } from 'react-router-dom';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { api } from '../../services/api-client';
|
||||
import type { DashboardStats } from '../../types';
|
||||
import styles from './DoctorLayout.module.css';
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ to: '/dashboard', label: '工作台', icon: '📊' },
|
||||
{ to: '/patients', label: '患者管理', icon: '👥' },
|
||||
{ to: '/consultations', label: '在线问诊', icon: '💬' },
|
||||
{ to: '/reports', label: '报告审核', icon: '📋' },
|
||||
{ to: '/follow-ups', label: '复查随访', icon: '📅' },
|
||||
];
|
||||
|
||||
export default function DoctorLayout() {
|
||||
const location = useLocation();
|
||||
const [doctor, setDoctor] = useState<DashboardStats | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
api.get<DashboardStats>('/api/doctor/dashboard').then(setDoctor).catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.layout}>
|
||||
{/* 侧边栏 */}
|
||||
<aside className={styles.sidebar}>
|
||||
<div className={styles.brand}>
|
||||
<div className={styles.brandTitle}>
|
||||
<span>❤️</span> 健康管家
|
||||
</div>
|
||||
<div className={styles.brandSub}>医生工作台</div>
|
||||
</div>
|
||||
|
||||
<nav className={styles.nav}>
|
||||
{NAV_ITEMS.map(item => (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
className={({ isActive }) =>
|
||||
`${styles.navItem} ${isActive ? styles.active : ''}`
|
||||
}
|
||||
>
|
||||
<span className={styles.navIcon}>{item.icon}</span>
|
||||
{item.label}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className={styles.footer}>
|
||||
<div className={styles.doctorInfo}>
|
||||
<div className={styles.avatar}>
|
||||
{doctor?.doctorName?.[0] ?? '王'}
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.doctorName}>{doctor?.doctorName ?? '王建国'}</div>
|
||||
<div className={styles.doctorMeta}>
|
||||
{doctor?.doctorTitle ?? '主任医师'} · {doctor?.doctorDepartment ?? '心血管内科'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* 内容区 */}
|
||||
<main className={styles.content} key={location.pathname}>
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user