import React, { useState, useEffect } from 'react' import { APP_VERSION } from '../config/version' export default function Home() { const [user, setUser] = useState(null) const [stats, setStats] = useState({ totalCount: 0, totalCost: 0, totalRevenue: 0, expectedProfit: 0, totalProfit: 0, gradedCount: 0 }) const [recentCollections, setRecentCollections] = useState([]) const [yichensStats, setYichensStats] = useState({ total: 0, deals: 0, wants: 0, dragons: 0, horses: 0, tianma: 0 }) const [seekStats, setSeekStats] = useState({ seekCount: 0, matchedCount: 0 }) const [recentPosts, setRecentPosts] = useState([]) const [dragonStats, setDragonStats] = useState({}) const currentPath = window.location.hash.slice(1) || '/' useEffect(() => { const token = localStorage.getItem('token') const userData = localStorage.getItem('user') if (userData) { try { setUser(JSON.parse(userData)) } catch (e) { console.error('Parse user error:', e) } } if (token) { fetch('/api/collections/stats', { headers: { 'Authorization': 'Bearer ' + token } }).then(res => { if (!res.ok) throw new Error('Stats API failed') return res.json() }).then(data => { // stats API直接返回对象,不需要data.data包装 const info = data.totalCount !== undefined ? data : (data.data || data) if (info && info.totalCount !== undefined) { // 从byGrading计算评级数 const gradedCount = info.byGrading ? (info.byGrading.find(x => x.isGraded === true)?.count || 0) : 0 setStats({ totalCount: info.totalCount || 0, totalCost: info.totalCost || 0, totalRevenue: info.totalRevenue || 0, expectedProfit: info.expectedProfit || 0, totalProfit: info.totalProfit || 0, gradedCount: gradedCount }) } }) fetch('/api/collections?limit=5&sort=createdAt&order=desc', { headers: { 'Authorization': 'Bearer ' + token } }).then(res => res.json()).then(data => { // 新格式 {data:[], pagination:{}} 或老格式 {items:[]} const list = data.data || data.items || data if (Array.isArray(list)) { setRecentCollections(list) } }) } }, []) // 获取一尘看板数据 useEffect(() => { fetch('/api/yichens/stats/dragons-today').then(res => res.json()).then(data => { setDragonStats(data || {}) }).catch(() => {}) fetch('/api/yichens/stats/today').then(res => res.json()).then(data => { setYichensStats(data || {}) }).catch(() => {}) // 获取最新一尘帖子 fetch('/api/yichens/stats/dragons-today').then(res => res.json()).then(data => { setDragonStats(data || {}) }).catch(() => {}) fetch('/api/yichens/posts?limit=20&offset=0').then(res => res.json()).then(data => { setRecentPosts(data.posts || data || []) }).catch(() => {}) // 获取寻配号统计数据 fetch('/api/information/seek/stats').then(res => res.json()).then(data => { setSeekStats(data || {}) }).catch(() => {}) }, []) // 检查是否为管理员 const isAdmin = user && user.role === 'admin' const handleLogout = () => { localStorage.removeItem('token') localStorage.removeItem('user') window.location.hash = '#/login' window.location.reload() } const formatMoney = (val) => { if (!val || val === 0) return '0' const v = val / 10000 return v.toFixed(1) } const statCards = [ { label: '藏品数', value: stats.totalCount, color: '#3b82f6' }, { label: '总成本', value: formatMoney(stats.totalCost) + '万', color: '#22c55e' }, { label: '总收入', value: formatMoney(stats.totalRevenue) + '万', color: '#06b6d4' }, { label: '评级数', value: stats.gradedCount, color: '#8b5cf6' }, { label: '预期利润', value: formatMoney(stats.expectedProfit) + '万', color: stats.expectedProfit >= 0 ? '#f59e0b' : '#ef4444' }, { label: '总利润', value: formatMoney(stats.totalProfit) + '万', color: stats.totalProfit >= 0 ? '#10b981' : '#f43f5e' } ] return (
{/* 顶部用户信息 */}
甲辰收藏
{user?.username || '用户'} ID:{user?.user_code || '-'}
欢迎回来 👋
v{APP_VERSION}
window.location.hash = '#/settings'} style={{ color: 'rgba(255,255,255,0.5)', cursor: 'pointer', fontSize: '12px', padding: '4px 8px', border: '1px solid rgba(255,255,255,0.2)', borderRadius: '4px' }}>设置
退出
{/* 统计卡片网格 */}
{statCards.map((card, idx) => (
{ e.currentTarget.style.transform = 'translateY(-2px)' }} onMouseOut={e => { e.currentTarget.style.transform = 'translateY(0)' }} onClick={() => window.location.hash = '#/stats'} >
{card.value}
{card.label}
))}
{/* 快捷操作 */}
快捷操作
window.location.hash = '#/add?mode=ocr'} style={{ background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)', borderRadius: '12px', padding: '16px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '12px' }}>
📷
AI识别
拍照识别藏品
window.location.hash = '#/add?mode=manual'} style={{ background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)', borderRadius: '12px', padding: '16px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '12px' }}>
✏️
手动录入
添加新藏品
{/* 寻配号数据 */}
🔍 寻配号数据
window.location.hash = '#/news?type=seek'} style={{ background: 'linear-gradient(135deg, rgba(245,158,11,0.15) 0%, rgba(245,158,11,0.05) 100%)', borderRadius: '12px', padding: '16px', border: '1px solid rgba(245,158,11,0.2)', cursor: 'pointer' }}>
{seekStats.seekCount || 0}
寻号需求
{seekStats.userMatchedCount || 0}
我的匹配
{seekStats.totalMatchedCount || 0}
总共匹配
{/* 一尘今日数据 */}
📊 一尘今日连体纪念钞数据
{yichensStats.total || 0}
总帖子
{yichensStats.deals || 0}
出售
{yichensStats.wants || 0}
求购
{yichensStats.dragons || 0}
龙钞
{yichensStats.horses || 0}
马钞
{yichensStats.tianma || 0}
天马
{/* 今日龙钞帖子数据统计 */}
🐉 今日龙钞帖子数据统计
{/* 表头 */}
出售
求购
合计
{/* 数据行 */}
带4
{dragonStats.dai4?.deals || 0}
{dragonStats.dai4?.wants || 0}
{(dragonStats.dai4?.deals || 0) + (dragonStats.dai4?.wants || 0)}
无4
{dragonStats.wu4?.deals || 0}
{dragonStats.wu4?.wants || 0}
{(dragonStats.wu4?.deals || 0) + (dragonStats.wu4?.wants || 0)}
无47
{dragonStats.wu47?.deals || 0}
{dragonStats.wu47?.wants || 0}
{(dragonStats.wu47?.deals || 0) + (dragonStats.wu47?.wants || 0)}
无247
{dragonStats.wu247?.deals || 0}
{dragonStats.wu247?.wants || 0}
{(dragonStats.wu247?.deals || 0) + (dragonStats.wu247?.wants || 0)}
无347
{dragonStats.wu347?.deals || 0}
{dragonStats.wu347?.wants || 0}
{(dragonStats.wu347?.deals || 0) + (dragonStats.wu347?.wants || 0)}
{/* 最新一尘发帖 */}
📝 最新一尘发帖
window.location.hash = '#/news'} style={{ color: '#3b82f6', fontSize: '12px', cursor: 'pointer' }}>查看全部 →
{recentPosts.length === 0 ? (
暂无帖子
) : ( recentPosts.map((item, idx) => (
window.open(item.url, '_blank')} style={{ padding: '12px 16px', borderBottom: idx < recentPosts.length - 1 ? '1px solid rgba(255,255,255,0.05)' : 'none', cursor: 'pointer', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{item.title || '无标题'}
{item.category || '-'} · {item.author_username || '未知'} · {item.post_time?.substring(0, 16) || ''}
{item.post_type === 'deal' ? '出售' : item.post_type === 'want' ? '求购' : '其他'}
)) )}
{/* 底部导航 */}
{(isAdmin ? [ { icon: '🏠', label: '首页', hash: '#/', idx: 0 }, { icon: '📚', label: '藏品', hash: '#/list', idx: 1 }, { icon: '➕', label: '添加', hash: '#/ocr', idx: 2 }, { icon: '📊', label: '统计', hash: '#/stats', idx: 3 }, { icon: '⚙️', label: '管理', hash: '#/admin', idx: 4 } ] : [ { icon: '🏠', label: '首页', hash: '#/', idx: 0 }, { icon: '📚', label: '藏品', hash: '#/list', idx: 1 }, { icon: '➕', label: '添加', hash: '#/ocr', idx: 2 }, { icon: '📊', label: '统计', hash: '#/stats', idx: 3 } ]).map((item) => (
window.location.hash = item.hash} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', cursor: 'pointer', color: currentPath === item.hash ? '#fbbf24' : '#64748b' }}>
{item.icon}
{item.label}
))}
) }