feat: 医生端Web后台 + SignalR实时问诊
- 新增 doctor_web/ React前端 (dashboard/患者/问诊/报告/随访) - 后端新增 doctor_endpoints (14个医生API) + ConsultationHub (SignalR) - Flutter端 SignalR 替换轮询实现实时聊天
This commit is contained in:
72
doctor_web/src/pages/dashboard/DashboardPage.tsx
Normal file
72
doctor_web/src/pages/dashboard/DashboardPage.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { api } from '../../services/api-client';
|
||||
import type { DashboardStats } from '../../types';
|
||||
|
||||
const CARD_CONFIG = [
|
||||
{ key: 'totalPatients', label: '患者总数', color: '#4F6EF7', bg: '#EDF0FD' },
|
||||
{ key: 'activeConsultations', label: '进行中问诊', color: '#20C997', bg: '#E6F9F2' },
|
||||
{ key: 'pendingReports', label: '待审核报告', color: '#F59E0B', bg: '#FFF8E6' },
|
||||
{ key: 'todayFollowUps', label: '今日随访', color: '#845EF7', bg: '#F3E8FF' },
|
||||
];
|
||||
|
||||
const QUICK_LINKS = [
|
||||
{ to: '/patients', label: '患者列表', color: '#4F6EF7' },
|
||||
{ to: '/consultations', label: '在线问诊', color: '#20C997' },
|
||||
{ to: '/reports', label: '报告审核', color: '#F59E0B' },
|
||||
{ to: '/follow-ups', label: '随访管理', color: '#845EF7' },
|
||||
];
|
||||
|
||||
export default function DashboardPage() {
|
||||
const [stats, setStats] = useState<DashboardStats | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
api.get<DashboardStats>('/api/doctor/dashboard').then(setStats).catch(() => {});
|
||||
}, []);
|
||||
|
||||
if (!stats) return <div style={{ padding: 40, color: '#9BA0B4' }}>加载中...</div>;
|
||||
|
||||
return (
|
||||
<div style={{ padding: 32, maxWidth: 1200 }}>
|
||||
{/* 欢迎 */}
|
||||
<div style={{ marginBottom: 32 }}>
|
||||
<h1 style={{ fontSize: 24, fontWeight: 700 }}>欢迎回来,{stats.doctorName}</h1>
|
||||
<p style={{ color: '#9BA0B4', marginTop: 4 }}>{stats.doctorTitle} · {stats.doctorDepartment}</p>
|
||||
</div>
|
||||
|
||||
{/* 统计卡片 */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 20, marginBottom: 32 }}>
|
||||
{CARD_CONFIG.map(c => (
|
||||
<div key={c.key} style={{ background: '#FFF', borderRadius: 16, padding: '24px 20px', position: 'relative', overflow: 'hidden', boxShadow: '0 2px 12px rgba(0,0,0,0.04)' }}>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, width: 4, height: '100%', background: c.color }} />
|
||||
<div style={{ fontSize: 30, fontWeight: 800, color: c.color }}>{(stats as any)[c.key]}</div>
|
||||
<div style={{ fontSize: 13, color: '#5A6072', marginTop: 4 }}>{c.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 快捷操作 */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 20 }}>
|
||||
<div style={{ background: '#FFF', borderRadius: 16, padding: 24, boxShadow: '0 2px 12px rgba(0,0,0,0.04)' }}>
|
||||
<h2 style={{ fontSize: 16, fontWeight: 600, marginBottom: 16 }}>快捷操作</h2>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
||||
{QUICK_LINKS.map(l => (
|
||||
<Link key={l.to} to={l.to} style={{ padding: '14px 16px', borderRadius: 12, border: `1px solid ${l.color}20`, color: l.color, fontWeight: 500, textAlign: 'center', transition: 'all 0.15s', fontSize: 14 }}>
|
||||
{l.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ background: '#FFF', borderRadius: 16, padding: 24, boxShadow: '0 2px 12px rgba(0,0,0,0.04)' }}>
|
||||
<h2 style={{ fontSize: 16, fontWeight: 600, marginBottom: 16 }}>今日待办</h2>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<div style={{ fontSize: 14, color: '#5A6072' }}>📋 待审核报告 <b style={{ color: '#F59E0B' }}>{stats.pendingReports}</b> 份</div>
|
||||
<div style={{ fontSize: 14, color: '#5A6072' }}>💬 进行中问诊 <b style={{ color: '#20C997' }}>{stats.activeConsultations}</b> 个</div>
|
||||
<div style={{ fontSize: 14, color: '#5A6072' }}>📅 今日随访 <b style={{ color: '#845EF7' }}>{stats.todayFollowUps}</b> 个</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user