Files
novalon-manage-system/novalon-manage-web/src/pages/dashboard/index.tsx
T
张翔 0b246b3e24 chore: 配置与基础设施更新
- 开发服务器端口从 3002 改为 5174
- vitest 配置从 vue plugin 改为 react plugin
- playwright 新增 uat 项目配置,修正 baseURL
- 添加 less 依赖支持
- 修复各页面 catch 块空语句为注释标记
2026-05-06 14:17:31 +08:00

101 lines
3.2 KiB
TypeScript

import { useEffect, useRef, useState } from 'react'
import { Row, Col, Card, Statistic, Timeline, Spin } from 'antd'
import { UserOutlined, TeamOutlined, FileOutlined, WarningOutlined } from '@ant-design/icons'
import { userApi } from '@/api/user.api'
import { roleApi } from '@/api/role.api'
import { exceptionLogApi } from '@/api/exceptionLog'
import { operationLogApi } from '@/api/operationLog'
interface DashboardData {
userCount: number
roleCount: number
opLogCount: number
exLogCount: number
recentOps: { operation: string; username: string; time: string }[]
}
export default function Dashboard() {
const [data, setData] = useState<DashboardData>({
userCount: 0,
roleCount: 0,
opLogCount: 0,
exLogCount: 0,
recentOps: [],
})
const [loading, setLoading] = useState(true)
const chartRef = useRef<HTMLDivElement>(null)
async function loadDashboard() {
try {
const [users, roles, opLogs, exLogs] = await Promise.all([
userApi.getAll().catch(() => []),
roleApi.getAll().catch(() => []),
operationLogApi.getCount().catch(() => 0),
exceptionLogApi.getCount().catch(() => 0),
])
setData({
userCount: Array.isArray(users) ? users.length : 0,
roleCount: Array.isArray(roles) ? roles.length : 0,
opLogCount: typeof opLogs === 'number' ? opLogs : 0,
exLogCount: typeof exLogs === 'number' ? exLogs : 0,
recentOps: [],
})
} finally {
setLoading(false)
}
}
useEffect(() => {
loadDashboard()
}, [])
if (loading) {
return <Spin size="large" style={{ display: 'block', margin: '100px auto' }} />
}
return (
<div style={{ padding: 24 }}>
<Row gutter={[16, 16]}>
<Col xs={24} sm={12} lg={6}>
<Card>
<Statistic title="用户总数" value={data.userCount} prefix={<UserOutlined />} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card>
<Statistic title="角色总数" value={data.roleCount} prefix={<TeamOutlined />} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card>
<Statistic title="操作日志" value={data.opLogCount} prefix={<FileOutlined />} />
</Card>
</Col>
<Col xs={24} sm={12} lg={6}>
<Card>
<Statistic title="异常日志" value={data.exLogCount} prefix={<WarningOutlined />} valueStyle={{ color: data.exLogCount > 0 ? '#cf1322' : undefined }} />
</Card>
</Col>
</Row>
<Row gutter={[16, 16]} style={{ marginTop: 24 }}>
<Col xs={24} lg={16}>
<Card title="最近活动" ref={chartRef}>
<div ref={chartRef} style={{ height: 300 }}>
<p style={{ color: '#999', textAlign: 'center', paddingTop: 120 }}>G2 ( @antv/g2)</p>
</div>
</Card>
</Col>
<Col xs={24} lg={8}>
<Card title="最近操作">
<Timeline
items={data.recentOps.length > 0 ? data.recentOps.map((op) => ({ children: `${op.username}: ${op.operation}`, color: 'blue' })) : [{ children: '暂无最近操作记录', color: 'gray' }]}
/>
</Card>
</Col>
</Row>
</div>
)
}