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: '12px 16px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center'
}}>
藏品录入
AI识别添加
window.location.hash = '#/add?mode=deal'} style={{
background: 'linear-gradient(135deg, #ec4899 0%, #db2777 100%)',
borderRadius: '12px',
padding: '12px 16px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center'
}}>
行情录入
录入成交行情
window.location.hash = '#/news?type=seek'} style={{
background: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)',
borderRadius: '12px',
padding: '12px 16px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center'
}}>
发布寻号
发布寻配需求
window.location.hash = '#/add?mode=manual'} style={{
background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
borderRadius: '12px',
padding: '12px 16px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center'
}}>
发布藏品
手动发布
{/* 寻配号数据 */}
🔍 寻配号数据
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)}
带7号
{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}
))}
)
}