141 lines
4.2 KiB
React
141 lines
4.2 KiB
React
import React, { useState, useEffect } from 'react'
|
||
import { Card, Row, Col, Statistic, Spin } from 'antd'
|
||
import {
|
||
DesktopOutlined,
|
||
TeamOutlined,
|
||
BellOutlined,
|
||
CheckCircleOutlined,
|
||
ToolOutlined,
|
||
CloseCircleOutlined,
|
||
ExclamationCircleOutlined
|
||
} from '@ant-design/icons'
|
||
import axios from 'axios'
|
||
|
||
function Dashboard({ onNavigate }) {
|
||
const [stats, setStats] = useState(null)
|
||
const [loading, setLoading] = useState(true)
|
||
|
||
useEffect(() => {
|
||
fetchStats()
|
||
}, [])
|
||
|
||
const fetchStats = async () => {
|
||
try {
|
||
const res = await axios.get('/api/dashboard/stats')
|
||
if (res.data.success) {
|
||
setStats(res.data.data)
|
||
}
|
||
} catch (err) {
|
||
console.error('获取统计数据失败:', err)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
if (loading) {
|
||
return <div style={{ textAlign: 'center', padding: 60 }}><Spin size="large" tip="加载中..." /></div>
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<h2 style={{ marginBottom: 24 }}>数据概览</h2>
|
||
<Row gutter={[16, 16]}>
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card className="dashboard-card" onClick={() => onNavigate('devices')}>
|
||
<Statistic
|
||
title="设备总数"
|
||
value={stats?.deviceTotal || 0}
|
||
prefix={<DesktopOutlined />}
|
||
valueStyle={{ color: '#1890ff' }}
|
||
/>
|
||
</Card>
|
||
</Col>
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card className="dashboard-card">
|
||
<Statistic
|
||
title="正常运行"
|
||
value={stats?.deviceNormal || 0}
|
||
prefix={<CheckCircleOutlined />}
|
||
valueStyle={{ color: '#52c41a' }}
|
||
/>
|
||
</Card>
|
||
</Col>
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card className="dashboard-card">
|
||
<Statistic
|
||
title="维修中"
|
||
value={stats?.deviceRepairing || 0}
|
||
prefix={<ToolOutlined />}
|
||
valueStyle={{ color: '#faad14' }}
|
||
/>
|
||
</Card>
|
||
</Col>
|
||
<Col xs={24} sm={12} lg={6}>
|
||
<Card className="dashboard-card">
|
||
<Statistic
|
||
title="已报废"
|
||
value={stats?.deviceScrapped || 0}
|
||
prefix={<CloseCircleOutlined />}
|
||
valueStyle={{ color: '#f5222d' }}
|
||
/>
|
||
</Card>
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row gutter={[16, 16]} style={{ marginTop: 16 }}>
|
||
<Col xs={24} sm={12} lg={8}>
|
||
<Card className="dashboard-card" onClick={() => onNavigate('personnel')}>
|
||
<Statistic
|
||
title="人员总数"
|
||
value={stats?.personnelTotal || 0}
|
||
prefix={<TeamOutlined />}
|
||
valueStyle={{ color: '#722ed1' }}
|
||
/>
|
||
</Card>
|
||
</Col>
|
||
<Col xs={24} sm={12} lg={8}>
|
||
<Card className="dashboard-card">
|
||
<Statistic
|
||
title="在职人员"
|
||
value={stats?.personnelActive || 0}
|
||
prefix={<CheckCircleOutlined />}
|
||
valueStyle={{ color: '#13c2c2' }}
|
||
/>
|
||
</Card>
|
||
</Col>
|
||
<Col xs={24} sm={12} lg={8}>
|
||
<Card className="dashboard-card" onClick={() => onNavigate('notifications')}>
|
||
<Statistic
|
||
title="未读通知"
|
||
value={stats?.notifUnread || 0}
|
||
prefix={<ExclamationCircleOutlined />}
|
||
valueStyle={{ color: '#fa541c' }}
|
||
suffix={<span style={{ fontSize: 14, color: '#999' }}>/ {stats?.notifTotal || 0}</span>}
|
||
/>
|
||
</Card>
|
||
</Col>
|
||
</Row>
|
||
|
||
<Card style={{ marginTop: 24 }}>
|
||
<h3>系统信息</h3>
|
||
<Row gutter={16} style={{ marginTop: 12 }}>
|
||
<Col span={8}>
|
||
<p><strong>系统版本:</strong>v2.2</p>
|
||
<p><strong>系统名称:</strong>设备管理系统</p>
|
||
</Col>
|
||
<Col span={8}>
|
||
<p><strong>数据库:</strong>SQLite</p>
|
||
<p><strong>前端框架:</strong>React + Ant Design</p>
|
||
</Col>
|
||
<Col span={8}>
|
||
<p><strong>后端框架:</strong>Express.js</p>
|
||
<p><strong>部署方式:</strong>服务器 / 桌面应用</p>
|
||
</Col>
|
||
</Row>
|
||
</Card>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default Dashboard
|