jiachenlong/frontend/src/pages/Home.jsx

563 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 [dealStats, setDealStats] = useState({ total: 0, items: [] })
const [dealVersion, setDealVersion] = useState('龙钞')
const [dealDetailItems, setDealDetailItems] = useState(null)
const currentPath = window.location.hash.slice(1) || '/'
// 成交行情数据处理
const categoryOrder = ['带4号', '带7号', '永恒号', '天马号', '天马王', '金山号', '金山王', '钻石号', '朦胧号', '朦胧王', '金马号', '金马王', '倒置号', '圆圆号']
const normalizeCat = (c) => {
if (c === '通货') return '带4号'
if (c === '无4') return '带7号'
return c
}
const calcDealAvg = (pkg, cat) => {
const items = dealStats.items.filter(item => {
const content = item.content || ''
const p = content.includes('包装:') ? content.split('包装:')[1].split('\n')[0].trim() : (item.packaging || '')
let c = content.includes('分类:') ? content.split('分类:')[1].split('\n')[0].trim() : (item.category || '')
c = normalizeCat(c)
return p === pkg && c === cat
})
if (items.length === 0) return null
const sum = items.reduce((a, b) => a + (b.deal_price || 0), 0)
return { avg: Math.round(sum / items.length), count: items.length, items }
}
// 获取今日成交数据
useEffect(() => {
// 获取最新成交数据
fetch(`/api/information/list?info_type=deal&page_size=500`)
.then(res => res.json())
.then(data => {
if (Array.isArray(data)) {
setDealStats({ total: data.length, items: data })
}
})
.catch(() => {})
}, [])
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 (
<div style={{
minHeight: '100vh', overflowY: 'auto',
background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%)',
padding: '20px',
fontFamily: '"Noto Sans SC", "PingFang SC", sans-serif'
}}>
{/* 顶部用户信息 */}
<div style={{
background: 'linear-gradient(135deg, rgba(59,130,246,0.2) 0%, rgba(139,92,246,0.2) 100%)',
borderRadius: '16px',
padding: '20px',
marginBottom: '20px',
border: '1px solid rgba(255,255,255,0.1)',
backdropFilter: 'blur(10px)'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
<img src="/static/images/jiachenlong-logo.png" alt="甲辰收藏" style={{ width: '48px', height: '48px', borderRadius: '50%', objectFit: 'cover' }} />
<div>
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '600' }}>{user?.username || '用户'} <span style={{ color: 'rgba(255,255,255,0.4)', fontSize: '11px', fontWeight: 'normal' }}>ID:{user?.user_code || '-'}</span></div>
<div style={{ color: 'rgba(255,255,255,0.6)', fontSize: '12px' }}>欢迎回来 👋</div>
</div>
<div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: '12px' }}>
<div style={{ color: 'rgba(255,255,255,0.3)', fontSize: '10px' }}>v{APP_VERSION}</div>
<div onClick={() => 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' }}>设置</div>
<div onClick={handleLogout} 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' }}>退出</div>
</div>
</div>
</div>
{/* 统计卡片网格 */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '12px',
marginBottom: '20px'
}}>
{statCards.map((card, idx) => (
<div key={idx} style={{
background: 'linear-gradient(135deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.02) 100%)',
borderRadius: '16px',
padding: '16px 12px',
border: '1px solid rgba(255,255,255,0.08)',
backdropFilter: 'blur(10px)',
textAlign: 'center',
transition: 'transform 0.2s, box-shadow 0.2s',
cursor: 'pointer'
}}
onMouseOver={e => { e.currentTarget.style.transform = 'translateY(-2px)' }}
onMouseOut={e => { e.currentTarget.style.transform = 'translateY(0)' }}
onClick={() => window.location.hash = '#/stats'}
>
<div style={{ color: card.color, fontSize: '18px', fontWeight: '700', marginBottom: '4px' }}>{card.value}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '11px' }}>{card.label}</div>
</div>
))}
</div>
{/* 快捷操作 */}
<div style={{ marginBottom: '20px' }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>快捷操作</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '12px' }}>
<div onClick={() => 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'
}}>
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>藏品录入</div>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>AI识别添加</div>
</div>
<div onClick={() => 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'
}}>
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>行情录入</div>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>录入成交行情</div>
</div>
<div onClick={() => 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'
}}>
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>发布寻号</div>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>发布寻配需求</div>
</div>
<div onClick={() => 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'
}}>
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>发布藏品</div>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>手动发布</div>
</div>
</div>
</div>
{/* 寻配号数据 */}
<div style={{ marginBottom: '20px' }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>🔍 寻配号数据</div>
<div onClick={() => 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'
}}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px', textAlign: 'center' }}>
<div>
<div style={{ color: '#fbbf24', fontSize: '18px', fontWeight: '700' }}>{seekStats.seekCount || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '11px' }}>寻号需求</div>
</div>
<div>
<div style={{ color: '#34d399', fontSize: '18px', fontWeight: '700' }}>{seekStats.userMatchedCount || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '11px' }}>我的匹配</div>
</div>
<div>
<div style={{ color: '#a78bfa', fontSize: '18px', fontWeight: '700' }}>{seekStats.totalMatchedCount || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '11px' }}>总共匹配</div>
</div>
</div>
</div>
</div>
{/* 今日成交数据统计 */}
{dealStats.total > 0 && (
<div style={{ marginBottom: '20px' }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>📈 最新成交信息统计(均价)</div>
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '16px' }}>
<div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap', marginBottom: '12px' }}>
{['龙钞', '马钞', '蛇钞', '其他'].map(v => (
<button key={v} onClick={() => setDealVersion(v)}
style={{ padding: '6px 16px', borderRadius: '8px', border: 'none', cursor: 'pointer',
background: dealVersion === v ? '#3b82f6' : 'rgba(255,255,255,0.1)',
color: '#fff', fontSize: '13px', fontWeight: dealVersion === v ? '600' : '400' }}>
{v}
</button>
))}
</div>
<div style={{ overflowX: 'auto' }}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '13px' }}>
<thead>
<tr>
<th style={{ padding: '8px', textAlign: 'left', color: '#94a3b8', borderBottom: '1px solid rgba(255,255,255,0.1)' }}></th>
{['标百', '标十', '单张'].map(p => (
<th key={p} style={{ padding: '8px', textAlign: 'center', color: '#94a3b8', borderBottom: '1px solid rgba(255,255,255,0.1)' }}>{p}</th>
))}
</tr>
</thead>
<tbody>
{categoryOrder.map(cat => {
const rowData = ['标百', '标十', '单张'].map(pkg => calcDealAvg(pkg, cat))
const hasData = rowData.some(d => d !== null)
if (!hasData) return null
return (
<tr key={cat}>
<td style={{ padding: '8px', color: '#fbbf24', fontWeight: '500', borderBottom: '1px solid rgba(255,255,255,0.05)', whiteSpace: 'nowrap' }}>{cat}</td>
{rowData.map((d, i) => (
<td key={i} style={{ padding: '8px', textAlign: 'center', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
{d ? (
<div style={{ color: '#22c55e', fontWeight: '600', cursor: 'pointer' }}
onClick={() => setDealDetailItems(d.items)}>
¥{d.avg.toLocaleString()}
<span style={{ color: '#64748b', fontSize: '11px', marginLeft: '4px' }}>({d.count})</span>
</div>
) : <span style={{ color: '#475569' }}>-</span>}
</td>
))}
</tr>
)
})}
</tbody>
</table>
</div>
</div>
</div>
)}
{/* 一尘今日数据 */}
<div style={{ marginBottom: '20px' }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>📊 一尘今日连体纪念钞数据</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '8px' }}>
<div style={{ background: 'linear-gradient(135deg, rgba(59,130,246,0.15) 0%, rgba(59,130,246,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(59,130,246,0.2)', textAlign: 'center' }}>
<div style={{ color: '#60a5fa', fontSize: '20px', fontWeight: '700' }}>{yichensStats.total || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>总帖子</div>
</div>
<div style={{ background: 'linear-gradient(135deg, rgba(16,185,129,0.15) 0%, rgba(16,185,129,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(16,185,129,0.2)', textAlign: 'center' }}>
<div style={{ color: '#34d399', fontSize: '20px', fontWeight: '700' }}>{yichensStats.deals || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>出售</div>
</div>
<div style={{ background: 'linear-gradient(135deg, rgba(245,158,11,0.15) 0%, rgba(245,158,11,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(245,158,11,0.2)', textAlign: 'center' }}>
<div style={{ color: '#fbbf24', fontSize: '20px', fontWeight: '700' }}>{yichensStats.wants || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>求购</div>
</div>
<div style={{ background: 'linear-gradient(135deg, rgba(139,92,246,0.15) 0%, rgba(139,92,246,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(139,92,246,0.2)', textAlign: 'center' }}>
<div style={{ color: '#a78bfa', fontSize: '20px', fontWeight: '700' }}>{yichensStats.dragons || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>龙钞</div>
</div>
<div style={{ background: 'linear-gradient(135deg, rgba(236,72,153,0.15) 0%, rgba(236,72,153,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(236,72,153,0.2)', textAlign: 'center' }}>
<div style={{ color: '#f472b6', fontSize: '20px', fontWeight: '700' }}>{yichensStats.horses || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>马钞</div>
</div>
<div style={{ background: 'linear-gradient(135deg, rgba(20,184,166,0.15) 0%, rgba(20,184,166,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(20,184,166,0.2)', textAlign: 'center' }}>
<div style={{ color: '#2dd4bf', fontSize: '20px', fontWeight: '700' }}>{yichensStats.tianma || 0}</div>
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>天马</div>
</div>
</div>
</div>
{/* 今日龙钞帖子数据统计 */}
<div style={{ marginBottom: '20px' }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>🐉 今日龙钞帖子数据统计</div>
<div style={{ background: 'linear-gradient(180deg, rgba(99,102,241,0.15) 0%, rgba(99,102,241,0.05) 100%)', borderRadius: '12px', padding: '16px', border: '1px solid rgba(99,102,241,0.2)' }}>
{/* 表头 */}
<div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 1fr 1fr', gap: '8px', marginBottom: '8px' }}>
<div></div>
<div style={{ color: '#22c55e', fontSize: '14px', fontWeight: '600', textAlign: 'center' }}>出售</div>
<div style={{ color: '#f97316', fontSize: '14px', fontWeight: '600', textAlign: 'center' }}>求购</div>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', fontWeight: '600', textAlign: 'center' }}>合计</div>
</div>
{/* 数据行 */}
<div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 1fr 1fr', gap: '8px', marginBottom: '6px' }}>
<div style={{ color: '#ef4444', fontSize: '14px', fontWeight: '500' }}>带4</div>
<div style={{ color: '#22c55e', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.dai4?.deals || 0}</div>
<div style={{ color: '#f97316', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.dai4?.wants || 0}</div>
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{(dragonStats.dai4?.deals || 0) + (dragonStats.dai4?.wants || 0)}</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 1fr 1fr', gap: '8px', marginBottom: '6px' }}>
<div style={{ color: '#06b6d4', fontSize: '14px', fontWeight: '500' }}>带7号</div>
<div style={{ color: '#22c55e', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu4?.deals || 0}</div>
<div style={{ color: '#f97316', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu4?.wants || 0}</div>
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{(dragonStats.wu4?.deals || 0) + (dragonStats.wu4?.wants || 0)}</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 1fr 1fr', gap: '8px', marginBottom: '6px' }}>
<div style={{ color: '#3b82f6', fontSize: '14px', fontWeight: '500' }}>无47</div>
<div style={{ color: '#22c55e', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu47?.deals || 0}</div>
<div style={{ color: '#f97316', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu47?.wants || 0}</div>
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{(dragonStats.wu47?.deals || 0) + (dragonStats.wu47?.wants || 0)}</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 1fr 1fr', gap: '8px', marginBottom: '6px' }}>
<div style={{ color: '#a855f7', fontSize: '14px', fontWeight: '500' }}>无247</div>
<div style={{ color: '#22c55e', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu247?.deals || 0}</div>
<div style={{ color: '#f97316', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu247?.wants || 0}</div>
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{(dragonStats.wu247?.deals || 0) + (dragonStats.wu247?.wants || 0)}</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 1fr 1fr', gap: '8px' }}>
<div style={{ color: '#ec4899', fontSize: '14px', fontWeight: '500' }}>无347</div>
<div style={{ color: '#22c55e', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu347?.deals || 0}</div>
<div style={{ color: '#f97316', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{dragonStats.wu347?.wants || 0}</div>
<div style={{ color: '#fff', fontSize: '18px', fontWeight: '700', textAlign: 'center' }}>{(dragonStats.wu347?.deals || 0) + (dragonStats.wu347?.wants || 0)}</div>
</div>
</div>
</div>
{/* 最新一尘发帖 */}
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px', paddingLeft: '4px' }}>
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px' }}>📝 最新一尘发帖</div>
<div onClick={() => window.location.hash = '#/news'} style={{ color: '#3b82f6', fontSize: '12px', cursor: 'pointer' }}>查看全部 </div>
</div>
<div style={{
background: 'rgba(255,255,255,0.03)',
borderRadius: '12px',
border: '1px solid rgba(255,255,255,0.05)',
overflow: 'hidden'
}}>
{recentPosts.length === 0 ? (
<div style={{ padding: '40px', textAlign: 'center', color: 'rgba(255,255,255,0.3)' }}>暂无帖子</div>
) : (
recentPosts.map((item, idx) => (
<div key={item.post_id || idx} onClick={() => 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'
}}>
<div style={{ flex: 1 }}>
<div style={{ color: '#fff', fontSize: '14px' }}>{item.title || '无标题'}</div>
<div style={{ color: 'rgba(255,255,255,0.4)', fontSize: '12px', marginTop: '2px' }}>{item.category || '-'} · {item.author_username || '未知'} · {item.post_time?.substring(0, 16) || ''}</div>
</div>
<div style={{
color: item.post_type === 'deal' ? '#10b981' : item.post_type === 'want' ? '#f59e0b' : '#8b5cf6',
fontSize: '12px',
padding: '2px 8px',
borderRadius: '4px',
background: item.post_type === 'deal' ? 'rgba(16,185,129,0.2)' : item.post_type === 'want' ? 'rgba(245,158,11,0.2)' : 'rgba(139,92,246,0.2)'
}}>
{item.post_type === 'deal' ? '出售' : item.post_type === 'want' ? '求购' : '其他'}
</div>
</div>
))
)}
</div>
</div>
{/* 底部导航 */}
<div style={{
position: 'fixed',
bottom: '0',
left: '0',
right: '0',
background: 'rgba(15, 23, 42, 0.95)',
borderTop: '1px solid rgba(255,255,255,0.1)',
display: 'flex',
justifyContent: 'space-around',
padding: '12px 0',
backdropFilter: 'blur(10px)'
}}>
{(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) => (
<div key={item.idx} onClick={() => window.location.hash = item.hash} style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
cursor: 'pointer',
color: currentPath === item.hash ? '#fbbf24' : '#64748b'
}}>
<div style={{ fontSize: '18px', fontWeight: currentPath === item.hash ? 'bold' : 'normal' }}>{item.icon}</div>
<div style={{ fontSize: '10px', marginTop: '2px', fontWeight: currentPath === item.hash ? 'bold' : 'normal' }}>{item.label}</div>
</div>
))}
</div>
<div style={{ height: '70px' }}></div>
{/* 成交详情弹窗 */}
{dealDetailItems && (
<div style={{
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
background: 'rgba(0,0,0,0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center',
zIndex: 1000, padding: '20px'
}} onClick={() => setDealDetailItems(null)}>
<div style={{
background: '#1e293b', borderRadius: '12px', padding: '20px', maxWidth: '600px', width: '100%', maxHeight: '80vh', overflow: 'auto'
}} onClick={e => e.stopPropagation()}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px' }}>
<div style={{ color: '#fff', fontSize: '16px', fontWeight: '600' }}>成交详情列表</div>
<button onClick={() => setDealDetailItems(null)}
style={{ background: 'none', border: 'none', color: '#94a3b8', fontSize: '20px', cursor: 'pointer' }}></button>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
{([...dealDetailItems].sort((a, b) => (a.deal_price || 0) - (b.deal_price || 0))).map((item, idx) => {
const content = item.content || ''
const grade = content.includes('评级:') ? content.split('评级:')[1].split('\n')[0].trim() : (item.grading_score || '')
const sizeMatch = content.match(/大小号:\s*(.+?)(?:\n|$)/)
const size = sizeMatch ? sizeMatch[1].trim() : ''
const platformMatch = content.match(/平台:\s*(.+?)(?:\n|$)/)
const platform = platformMatch ? platformMatch[1].trim() : '-'
return (
<div key={idx} style={{
background: 'rgba(255,255,255,0.05)', borderRadius: '8px', padding: '12px',
display: 'flex', justifyContent: 'space-between', alignItems: 'center'
}}>
<div>
<div style={{ color: '#fbbf24', fontWeight: '600', marginBottom: '4px' }}>
{(item.title || '').split('-')[0]}
{size && <span style={{ color: '#94a3b8', marginLeft: '8px' }}>| {size}</span>}
{grade && <span style={{ color: '#06b6d4', marginLeft: '8px' }}>| {grade}</span>}
</div>
<div style={{ color: '#94a3b8', fontSize: '12px' }}>
{item.deal_date} | {item.category} | {item.packaging} | {platform}
</div>
</div>
<div style={{ color: '#22c55e', fontSize: '18px', fontWeight: '700' }}>
¥{item.deal_price?.toLocaleString()}
</div>
</div>
)
})}
</div>
</div>
</div>
)}
</div>
)
}