Files
inchstep/admin/src/pages/Dashboard.tsx
T
2026-06-25 08:39:47 +08:00

114 lines
4.7 KiB
TypeScript

import { useEffect, useState } from 'react'
import { api } from '../services/api'
import { useNavigate } from 'react-router-dom'
export default function Dashboard() {
const [stats, setStats] = useState<any>(null)
const [recentUsers, setRecentUsers] = useState<any[]>([])
const [recentPosts, setRecentPosts] = useState<any[]>([])
const [loading, setLoading] = useState(true)
const navigate = useNavigate()
useEffect(() => {
Promise.all([
api.get<any>('/api/admin/dashboard'),
api.get<any>('/api/admin/users?page=1&pageSize=5'),
api.get<any>('/api/admin/posts?page=1&pageSize=5'),
]).then(([dashData, usersData, postsData]) => {
setStats({
totalUsers: dashData.totalUsers || 0,
totalPosts: dashData.totalPosts || 0,
totalCheckins: dashData.totalRecords || 0,
totalItems: dashData.totalItems || 0,
newUsersToday: dashData.newUsersToday || 0,
})
setRecentUsers(usersData.list || [])
setRecentPosts(postsData.list || [])
}).catch(() => {
setStats({ totalUsers: 0, totalPosts: 0, totalCheckins: 0, totalItems: 0, newUsersToday: 0 })
}).finally(() => setLoading(false))
}, [])
if (loading) return <div style={{ textAlign: 'center', padding: 40, color: '#86868B' }}>...</div>
if (!stats) return <div style={{ textAlign: 'center', padding: 40, color: '#86868B' }}></div>
return (
<div>
<h1 className='page-title'></h1>
{/* 统计卡片 */}
<div className='stat-grid'>
<div className='stat-card' onClick={() => navigate('/users')} style={{ cursor: 'pointer' }}>
<div className='stat-value'>{stats.totalUsers}</div>
<div className='stat-label'></div>
</div>
<div className='stat-card' onClick={() => navigate('/users')} style={{ cursor: 'pointer' }}>
<div className='stat-value'>{stats.newUsersToday}</div>
<div className='stat-label'></div>
</div>
<div className='stat-card'>
<div className='stat-value'>{stats.totalCheckins}</div>
<div className='stat-label'></div>
</div>
<div className='stat-card'>
<div className='stat-value'>{stats.totalItems}</div>
<div className='stat-label'></div>
</div>
<div className='stat-card' onClick={() => navigate('/posts')} style={{ cursor: 'pointer' }}>
<div className='stat-value'>{stats.totalPosts}</div>
<div className='stat-label'></div>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
{/* 最近用户 */}
<div className='card'>
<h3 style={{ marginBottom: 12, display: 'flex', justifyContent: 'space-between' }}>
<span></span>
<a href='/users' style={{ fontSize: 13, color: '#FF6B35', textDecoration: 'none' }}> </a>
</h3>
<table>
<thead>
<tr><th></th><th></th><th></th><th></th></tr>
</thead>
<tbody>
{recentUsers.map((u: any) => (
<tr key={u.id}>
<td>{u.nickname || '--'}</td>
<td>{u.phone || '--'}</td>
<td>{u.points}</td>
<td style={{ fontSize: 12 }}>{u.createdAt?.slice(0, 10) || '--'}</td>
</tr>
))}
{recentUsers.length === 0 && <tr><td colSpan={4} style={{ textAlign: 'center', color: '#86868B' }}></td></tr>}
</tbody>
</table>
</div>
{/* 最近动态 */}
<div className='card'>
<h3 style={{ marginBottom: 12, display: 'flex', justifyContent: 'space-between' }}>
<span></span>
<a href='/posts' style={{ fontSize: 13, color: '#FF6B35', textDecoration: 'none' }}> </a>
</h3>
<table>
<thead>
<tr><th></th><th></th><th></th></tr>
</thead>
<tbody>
{recentPosts.map((p: any) => (
<tr key={p.id}>
<td style={{ maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.content || '--'}</td>
<td>{p.user?.nickname || '--'}</td>
<td style={{ fontSize: 12 }}>{p.createdAt?.slice(0, 10) || '--'}</td>
</tr>
))}
{recentPosts.length === 0 && <tr><td colSpan={3} style={{ textAlign: 'center', color: '#86868B' }}></td></tr>}
</tbody>
</table>
</div>
</div>
</div>
)
}