fix(web): 修复仪表盘 hooks 顺序 + 患者 DatePicker 初始值
- AdminDashboard/OperatorDashboard/DoctorDashboard/NurseDashboard: 将 useCountUp 调用从 JSX 中提取到组件顶层,避免条件提前返回 导致 hooks 数量不一致引发 React crash - PatientList: 编辑时 birth_date 字符串转 dayjs 对象,修复 Ant Design 6 DatePicker getUDayjs().isValid() 报错
This commit is contained in:
@@ -30,6 +30,7 @@ import { PageContainer } from '../../components/PageContainer';
|
||||
import { DrawerForm } from '../../components/DrawerForm';
|
||||
import { usePaginatedData } from '../../hooks/usePaginatedData';
|
||||
import { calcAge, formatDateTime } from '../../utils/format';
|
||||
import { dayjs } from '../../utils/dayjs';
|
||||
|
||||
/** 筛选器结构 */
|
||||
interface PatientFilters {
|
||||
@@ -392,7 +393,7 @@ export default function PatientList() {
|
||||
? {
|
||||
name: editingPatient.name,
|
||||
gender: editingPatient.gender,
|
||||
birth_date: editingPatient.birth_date,
|
||||
birth_date: editingPatient.birth_date ? dayjs(editingPatient.birth_date) : undefined,
|
||||
blood_type: editingPatient.blood_type,
|
||||
id_number: editingPatient.id_number,
|
||||
allergy_history: editingPatient.allergy_history,
|
||||
|
||||
@@ -12,6 +12,9 @@ import HealthDataCenter from './HealthDataCenter';
|
||||
|
||||
export function AdminDashboard() {
|
||||
const { patientStats, followUpStats, healthDataStats, loading } = useStatsData();
|
||||
const patientCount = useCountUp(patientStats?.total_patients ?? 0);
|
||||
const appointmentCount = useCountUp(healthDataStats?.appointments?.this_month ?? 0);
|
||||
const doctorCount = useCountUp(0);
|
||||
|
||||
if (loading && !patientStats) return <Spin size="large" style={{ display: 'block', margin: '80px auto' }} />;
|
||||
|
||||
@@ -27,12 +30,12 @@ export function AdminDashboard() {
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col xs={12} sm={8} lg={4}>
|
||||
<Card size="small">
|
||||
<Statistic title="患者总数" value={useCountUp(patientStats?.total_patients ?? 0)} prefix={<TeamOutlined />} />
|
||||
<Statistic title="患者总数" value={patientCount} prefix={<TeamOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} lg={4}>
|
||||
<Card size="small">
|
||||
<Statistic title="本月预约" value={useCountUp(healthDataStats?.appointments?.this_month ?? 0)} prefix={<CalendarOutlined />} />
|
||||
<Statistic title="本月预约" value={appointmentCount} prefix={<CalendarOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={12} sm={8} lg={4}>
|
||||
@@ -59,7 +62,7 @@ export function AdminDashboard() {
|
||||
</Col>
|
||||
<Col xs={12} sm={8} lg={4}>
|
||||
<Card size="small">
|
||||
<Statistic title="医护人数" value={useCountUp(0)} prefix={<UserOutlined />} />
|
||||
<Statistic title="医护人数" value={doctorCount} prefix={<UserOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -15,6 +15,8 @@ export function DoctorDashboard() {
|
||||
const [personal, setPersonal] = useState<PersonalStats | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { consultationStats } = useStatsData();
|
||||
const myPatientsCount = useCountUp(personal?.my_patients ?? 0);
|
||||
const consultationsCount = useCountUp(personal?.consultations_this_month ?? 0);
|
||||
|
||||
const fetchPersonal = useCallback(async () => {
|
||||
try {
|
||||
@@ -66,7 +68,7 @@ export function DoctorDashboard() {
|
||||
<Card size="small">
|
||||
<Statistic
|
||||
title="我的患者"
|
||||
value={useCountUp(p?.my_patients ?? 0)}
|
||||
value={myPatientsCount}
|
||||
prefix={<TeamOutlined />}
|
||||
suffix={p && p.new_patients_this_month > 0 ? (
|
||||
<Typography.Text type="success" style={{ fontSize: 12 }}>
|
||||
@@ -92,7 +94,7 @@ export function DoctorDashboard() {
|
||||
<Card size="small">
|
||||
<Statistic
|
||||
title="本月咨询"
|
||||
value={useCountUp(p?.consultations_this_month ?? 0)}
|
||||
value={consultationsCount}
|
||||
prefix={<MessageOutlined />}
|
||||
suffix={p && p.pending_consultations > 0 ? (
|
||||
<Typography.Text type="warning" style={{ fontSize: 12 }}>
|
||||
|
||||
@@ -12,6 +12,8 @@ import { useCountUp } from '../../../hooks/useCountUp';
|
||||
export function NurseDashboard() {
|
||||
const [personal, setPersonal] = useState<PersonalStats | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const appointmentCount = useCountUp(personal?.today_appointments ?? 0);
|
||||
const overdueCount = useCountUp(personal?.overdue_follow_ups ?? 0);
|
||||
|
||||
const fetchPersonal = useCallback(async () => {
|
||||
try {
|
||||
@@ -97,7 +99,7 @@ export function NurseDashboard() {
|
||||
{/* 统计卡 */}
|
||||
<Col xs={8}>
|
||||
<Card size="small">
|
||||
<Statistic title="今日预约" value={useCountUp(p?.today_appointments ?? 0)} prefix={<CalendarOutlined />} />
|
||||
<Statistic title="今日预约" value={appointmentCount} prefix={<CalendarOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={8}>
|
||||
@@ -113,7 +115,7 @@ export function NurseDashboard() {
|
||||
</Col>
|
||||
<Col xs={8}>
|
||||
<Card size="small">
|
||||
<Statistic title="逾期随访" value={useCountUp(p?.overdue_follow_ups ?? 0)} prefix={<TeamOutlined />}
|
||||
<Statistic title="逾期随访" value={overdueCount} prefix={<TeamOutlined />}
|
||||
valueStyle={{ color: (p?.overdue_follow_ups ?? 0) > 0 ? '#cf1322' : undefined }}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@@ -10,6 +10,9 @@ import { useCountUp } from '../../../hooks/useCountUp';
|
||||
|
||||
export function OperatorDashboard() {
|
||||
const { pointsStats, loading } = useStatsData();
|
||||
const issuedCount = useCountUp(pointsStats?.total_issued ?? 0);
|
||||
const spentCount = useCountUp(pointsStats?.total_spent ?? 0);
|
||||
const activeCount = useCountUp(pointsStats?.active_accounts ?? 0);
|
||||
|
||||
if (loading && !pointsStats) return <Spin size="large" style={{ display: 'block', margin: '80px auto' }} />;
|
||||
|
||||
@@ -25,12 +28,12 @@ export function OperatorDashboard() {
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col xs={12} sm={6}>
|
||||
<Card size="small">
|
||||
<Statistic title="积分发放" value={useCountUp(pointsStats?.total_issued ?? 0)} prefix={<TrophyOutlined />} />
|
||||
<Statistic title="积分发放" value={issuedCount} prefix={<TrophyOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={12} sm={6}>
|
||||
<Card size="small">
|
||||
<Statistic title="积分消费" value={useCountUp(pointsStats?.total_spent ?? 0)} prefix={<ShoppingOutlined />}
|
||||
<Statistic title="积分消费" value={spentCount} prefix={<ShoppingOutlined />}
|
||||
suffix={pointsStats ? (
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
消费率{pointsStats.total_issued > 0 ? Math.round(pointsStats.total_spent / pointsStats.total_issued * 100) : 0}%
|
||||
@@ -41,7 +44,7 @@ export function OperatorDashboard() {
|
||||
</Col>
|
||||
<Col xs={12} sm={6}>
|
||||
<Card size="small">
|
||||
<Statistic title="活跃账户" value={useCountUp(pointsStats?.active_accounts ?? 0)} prefix={<FileTextOutlined />} />
|
||||
<Statistic title="活跃账户" value={activeCount} prefix={<FileTextOutlined />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={12} sm={6}>
|
||||
|
||||
Reference in New Issue
Block a user