import React, { useEffect, useMemo, useState } from 'react' import { Card, Col, Progress, Row, Statistic, Table, Tag, Typography } from 'antd' import api from '../utils/axios' import { formatDateTime } from '../utils/datetime' import PageHeader from '../components/PageHeader' interface UserItem { id: number; username: string; nickname?: string; status?: number; created_at?: string } export default function Dashboard() { const [loading, setLoading] = useState(false) const [userTotal, setUserTotal] = useState(0) const [roleTotal, setRoleTotal] = useState(0) const [deptTotal, setDeptTotal] = useState(0) const [menuTotal, setMenuTotal] = useState(0) const [userSample, setUserSample] = useState([] as UserItem[]) useEffect(() => { const fetchAll = async () => { setLoading(true) try { const [usersRes, rolesRes, deptsRes, menusRes, usersSampleRes] = await Promise.all([ api.get('/users', { params: { page: 1, page_size: 1 } }), api.get('/roles', { params: { page: 1, page_size: 1 } }), api.get('/departments', { params: { keyword: '' } }), api.get('/menus'), api.get('/users', { params: { page: 1, page_size: 200 } }), ]) if (usersRes.data?.code === 0) setUserTotal(Number(usersRes.data?.data?.total || 0)) if (rolesRes.data?.code === 0) setRoleTotal(Number(rolesRes.data?.data?.total || 0)) if (deptsRes.data?.code === 0) setDeptTotal(Array.isArray(deptsRes.data?.data) ? deptsRes.data.data.length : 0) if (menusRes.data?.code === 0) setMenuTotal(Array.isArray(menusRes.data?.data) ? menusRes.data.data.length : 0) if (usersSampleRes.data?.code === 0) setUserSample(Array.isArray(usersSampleRes.data?.data?.items) ? usersSampleRes.data.data.items : []) } catch (e) { // ignore on dashboard } finally { setLoading(false) } } fetchAll() }, []) // 用户状态分布(基于 sample 数据近似统计) const statusDist = useMemo(() => { const enabled = userSample.reduce((acc, u) => acc + (u.status === 1 ? 1 : 0), 0) const total = userSample.length || 1 const percentEnabled = Math.round((enabled / total) * 100) return { enabled, disabled: total - enabled, percentEnabled } }, [userSample]) // 近7天新增用户(基于 sample 的 created_at 统计) const last7Days = useMemo(() => { const today = new Date() const days: string[] = [] for (let i = 6; i >= 0; i--) { const d = new Date(today) d.setDate(today.getDate() - i) const y = d.getFullYear() const m = String(d.getMonth() + 1).padStart(2, '0') const da = String(d.getDate()).padStart(2, '0') days.push(`${y}-${m}-${da}`) } const counter: Record = Object.fromEntries(days.map(d => [d, 0])) userSample.forEach(u => { if (!u.created_at) return const dt = new Date(u.created_at) if (Number.isNaN(dt.getTime())) return const y = dt.getFullYear() const m = String(dt.getMonth() + 1).padStart(2, '0') const da = String(dt.getDate()).padStart(2, '0') const key = `${y}-${m}-${da}` if (key in counter) counter[key] += 1 }) return days.map(d => ({ date: d, value: counter[d] || 0 })) }, [userSample]) const userColumns = useMemo(() => [ { title: 'ID', dataIndex: 'id', width: 80 }, { title: '用户名', dataIndex: 'username' }, { title: '昵称', dataIndex: 'nickname' }, { title: '状态', dataIndex: 'status', render: (v: number) => v === 1 ? 启用 : 禁用 }, { title: '创建时间', dataIndex: 'created_at', render: (v: any) => formatDateTime(v) }, ], []) const recentUsers = useMemo(() => { const withTime = userSample.filter(u => !!u.created_at) withTime.sort((a, b) => (new Date(b.created_at!).getTime() - new Date(a.created_at!).getTime())) return withTime.slice(0, 8) }, [userSample]) const maxDaily = Math.max(...last7Days.map(d => d.value), 1) return (
启用占比
启用:{statusDist.enabled}
禁用:{statusDist.disabled}
样本:{userSample.length}
{last7Days.map(d => (
{d.value}
{d.date.slice(5)}
))}
rowKey="id" size="small" pagination={false} dataSource={recentUsers} columns={userColumns as any} /> 欢迎使用 Udmin。当前首页展示了若干概览与示例报表,真实数据以接口返回为准。
) }