jiachenlong/frontend/src/pages/YichensBoard.jsx

114 lines
6.7 KiB
React
Raw Normal View History

2026-04-06 21:17:15 +08:00
import React, { useState, useEffect } from 'react'
export default function YichensBoard() {
const [stats, setStats] = useState({ posts: null, categories: [], users: null })
const [posts, setPosts] = useState([])
const [loading, setLoading] = useState(false)
const [expandedPosts, setExpandedPosts] = useState({})
const [postTypeFilter, setPostTypeFilter] = useState('all')
const API_BASE = localStorage.getItem('API_BASE') || ''
useEffect(() => { fetchStats(); fetchPosts() }, [])
const fetchStats = async () => {
try {
const [p, c, u] = await Promise.all([
fetch(`${API_BASE}/api/yichens/stats/posts?days=30`),
fetch(`${API_BASE}/api/yichens/stats/categories?days=30`),
fetch(`${API_BASE}/api/yichens/stats/users`)
])
setStats({ posts: await p.json(), categories: await c.json(), users: await u.json() })
} catch(e) { console.error(e) }
}
const fetchPosts = async () => {
setLoading(true)
let url = `${API_BASE}/api/yichens/posts?limit=50`
if (postTypeFilter !== 'all') url += `&post_type=${postTypeFilter}`
try {
const res = await fetch(url)
setPosts((await res.json()) || [])
} catch { setPosts([]) }
setLoading(false)
}
const togglePost = id => setExpandedPosts(p => ({ ...p, [id]: !p[id] }))
const filtered = postTypeFilter === 'all' ? posts : posts.filter(p => p.post_type === postTypeFilter)
return (
<div style={{ padding: '0' }}>
{stats.posts && (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 12, marginBottom: 20 }}>
<div style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, border: '1px solid #374151' }}>
<div style={{ color: '#9ca3af', fontSize: 12, marginBottom: 4 }}>30天帖子</div>
<div style={{ color: '#fff', fontSize: 24, fontWeight: 'bold' }}>{stats.posts.total_posts}</div>
<div style={{ color: '#6b7280', fontSize: 11 }}>出售{stats.posts.total_deals} 求购{stats.posts.total_wants}</div>
</div>
<div style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, border: '1px solid #374151' }}>
<div style={{ color: '#9ca3af', fontSize: 12, marginBottom: 4 }}>浏览量</div>
<div style={{ color: '#fff', fontSize: 24, fontWeight: 'bold' }}>{(stats.posts.total_views/10000).toFixed(1)}</div>
<div style={{ color: '#6b7280', fontSize: 11 }}>回复{stats.posts.total_replies}</div>
</div>
<div style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, border: '1px solid #374151' }}>
<div style={{ color: '#9ca3af', fontSize: 12, marginBottom: 4 }}>用户数</div>
<div style={{ color: '#fff', fontSize: 24, fontWeight: 'bold' }}>{stats.users?.total_users||0}</div>
<div style={{ color: '#6b7280', fontSize: 11 }}>今日新增{stats.users?.new_users_today||0}</div>
</div>
</div>
)}
<div style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, marginBottom: 20, border: '1px solid #374151' }}>
<div style={{ color: '#f9fafb', fontSize: 14, fontWeight: 600, marginBottom: 12 }}>📊 分类统计</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
{stats.categories.map(cat => (
<span key={cat.category} style={{ padding: '4px 12px', background: 'rgba(59,130,246,0.2)', borderRadius: 20, color: '#93c5fd', fontSize: 12 }}>
{cat.category} ({cat.count})
</span>
))}
</div>
</div>
<div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
{[{key:'all',label:'全部'},{key:'deal',label:'出售'},{key:'want',label:'求购'}].map(k => (
<button key={k.key} onClick={() => { setPostTypeFilter(k.key); fetchPosts() }}
style={{ padding: '8px 16px', borderRadius: 8, border: 'none',
background: postTypeFilter===k.key ? 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)' : '#374151', color: '#fff', cursor: 'pointer', fontSize: 13 }}>
{k.label}
</button>
))}
</div>
{loading ? <div style={{textAlign:'center',color:'#9ca3af',padding:40}}>加载中...</div> :
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
{filtered.map(post => (
<div key={post.post_id} style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, border: '1px solid #374151' }}>
<div onClick={() => togglePost(post.post_id)} style={{ cursor: 'pointer' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
<span style={{ color: '#fff', fontSize: 15, fontWeight: 500 }}>{post.title||'无标题'}</span>
<span style={{ color: post.post_type==='deal'?'#10b981':post.post_type==='want'?'#f59e0b':'#9ca3af', fontSize: 12 }}>
{post.post_type==='deal'?'出售':post.post_type==='want'?'求购':'普通'}
</span>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', color: '#9ca3af', fontSize: 12 }}>
<span>{post.author_username||'未知'}</span>
<span>{post.post_time?.substring(0,16)||''}</span>
</div>
</div>
{expandedPosts[post.post_id] && (
<div style={{ marginTop: 12, paddingTop: 12, borderTop: '1px solid #374151' }}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, fontSize: 13 }}>
<div><span style={{color:'#6b7280'}}>分类: </span><span style={{color:'#fff'}}>{post.category||'-'}</span></div>
<div><span style={{color:'#6b7280'}}>价格: </span><span style={{color:'#10b981'}}>{post.price?post.price.toLocaleString()+'元':'待询'}</span></div>
<div><span style={{color:'#6b7280'}}>浏览: </span><span style={{color:'#fff'}}>{post.view_count||0}</span></div>
<div><span style={{color:'#6b7280'}}>回复: </span><span style={{color:'#fff'}}>{post.reply_count||0}</span></div>
</div>
{post.url && <a href={post.url} target="_blank" rel="noopener noreferrer" style={{ display:'inline-block', marginTop:12, padding:'8px 16px', background:'rgba(59,130,246,0.2)', borderRadius:8, color:'#60a5fa', textDecoration:'none', fontSize:13 }}>查看原帖</a>}
</div>
)}
</div>
))}
</div>}
</div>
)
}