Initial commit
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Row, Col, Statistic, Typography, Table, Tag, Space } from 'antd';
|
||||
import { UserOutlined, ExperimentOutlined, FileTextOutlined } from '@ant-design/icons';
|
||||
import { useAppStore } from '../../store';
|
||||
import { adminApi } from '../../api';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
const Dashboard: React.FC = () => {
|
||||
const { scales } = useAppStore();
|
||||
const [stats, setStats] = useState<any>({});
|
||||
const [records, setRecords] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
Promise.all([
|
||||
adminApi.getStats(),
|
||||
adminApi.getRecords(),
|
||||
]).then(([s, r]) => {
|
||||
setStats(s || {});
|
||||
setRecords(Array.isArray(r) ? r.slice(0, 10) : []);
|
||||
}).catch(() => {
|
||||
setStats({});
|
||||
setRecords([]);
|
||||
}).finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Title level={4} style={{ marginBottom: 24 }}>数据看板</Title>
|
||||
<Row gutter={16} style={{ marginBottom: 24 }}>
|
||||
<Col xs={24} sm={8}>
|
||||
<Card style={{ borderRadius: 12 }}>
|
||||
<Statistic title="用户总数" value={stats.userCount || 0}
|
||||
prefix={<UserOutlined style={{ color: '#2563eb' }} />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} sm={8}>
|
||||
<Card style={{ borderRadius: 12 }}>
|
||||
<Statistic title="量表数量" value={stats.scaleCount || 0}
|
||||
prefix={<ExperimentOutlined style={{ color: '#1d4ed8' }} />} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} sm={8}>
|
||||
<Card style={{ borderRadius: 12 }}>
|
||||
<Statistic title="测试记录" value={stats.recordCount || 0}
|
||||
prefix={<FileTextOutlined style={{ color: '#06b6d4' }} />} />
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col xs={24} lg={12}>
|
||||
<Card title="最常使用的量表" style={{ borderRadius: 12, marginBottom: 16 }}>
|
||||
{(stats.topScales || []).map((item: any, idx: number) => (
|
||||
<div key={idx} style={{
|
||||
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
||||
padding: '8px 0', borderBottom: idx < 4 ? '1px solid #f0f0f0' : 'none',
|
||||
}}>
|
||||
<Space><Tag color="blue">{idx + 1}</Tag><Text>{item.name}</Text></Space>
|
||||
<Text strong>{item.count} 次</Text>
|
||||
</div>
|
||||
))}
|
||||
{(!stats.topScales || stats.topScales.length === 0) && <Text type="secondary">暂无数据</Text>}
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} lg={12}>
|
||||
<Card title="最近测试记录" style={{ borderRadius: 12, marginBottom: 16 }}>
|
||||
<Table dataSource={records} rowKey="id" loading={loading} pagination={false} size="small"
|
||||
locale={{ emptyText: '暂无记录' }}
|
||||
columns={[
|
||||
{ title: '量表', dataIndex: 'scale_id', render: (id: string) => scales.find(s => s.id === id)?.name || id },
|
||||
{ title: '分数', dataIndex: 'total_score', width: 60, render: (v: number) => <strong>{v}</strong> },
|
||||
{ title: '等级', dataIndex: 'level', width: 100, render: (l: string) => <Tag>{l || '-'}</Tag> },
|
||||
{ title: '时间', dataIndex: 'created_at', width: 120, render: (t: string) => new Date(t).toLocaleDateString() },
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
Reference in New Issue
Block a user