import React, { useState, useEffect } from 'react' // 管理后台V2 export default function AdminV2() { const [activeTab, setActiveTab] = useState('users') const token = localStorage.getItem('token') const API_BASE = localStorage.getItem('API_BASE') || '' const userStr = localStorage.getItem('user') let user = null try { user = userStr ? JSON.parse(userStr) : null } catch (e) {} if (!user || user.role !== 'admin') { return (
🚫
无权访问
仅管理员可以访问管理后台
) } return (

⚙️ 管理后台

{[ { key: 'users', label: '👥 用户管理' }, { key: 'stats', label: '📊 统计分析' }, { key: 'publish', label: '📢 信息发布' }, { key: 'infoManage', label: '📋 信息管理' } ].map(tab => (
setActiveTab(tab.key)} style={{ flex: '1 1 45%', textAlign: 'center', padding: '10px 8px', margin: '2px', borderRadius: '6px', cursor: 'pointer', background: activeTab === tab.key ? '#3b82f6' : 'transparent', color: activeTab === tab.key ? '#fff' : '#94a3b8', fontSize: '13px', fontWeight: activeTab === tab.key ? 'bold' : 'normal' }}> {tab.label}
))}
{activeTab === 'users' && } {activeTab === 'stats' && } {activeTab === 'publish' && } {activeTab === 'infoManage' && }
) } function UserManagement({ token, API_BASE }) { const [users, setUsers] = useState([]) const [loading, setLoading] = useState(true) const [search, setSearch] = useState('') const [editingUser, setEditingUser] = useState(null) const [showAddModal, setShowAddModal] = useState(false) const [newUser, setNewUser] = useState({ username: '', password: '', phone: '', role: 'user' }) useEffect(() => { fetchUsers() }, []) const fetchUsers = async () => { setLoading(true) try { const res = await fetch(`${API_BASE}/api/admin/users?page=1&limit=100`, { headers: { 'Authorization': `Bearer ${token}` } }) const data = await res.json() setUsers(Array.isArray(data) ? data : []) } catch (e) { console.error(e) } setLoading(false) } const handleAddUser = async () => { if (!newUser.username || !newUser.password) { alert('请填写用户名和密码'); return } try { const res = await fetch(`${API_BASE}/api/admin/users`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(newUser) }) if (res.ok) { alert('添加成功'); setShowAddModal(false); setNewUser({ username: '', password: '', phone: '', role: 'user' }); fetchUsers() } } catch (e) { alert('添加失败') } } const handleUpdateUser = async () => { try { const res = await fetch(`${API_BASE}/api/admin/users/${editingUser.f99_90_id}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(editingUser) }) if (res.ok) { alert('更新成功'); setEditingUser(null); fetchUsers() } } catch (e) { alert('更新失败') } } const handleDeleteUser = async (id) => { if (!confirm('确定删除该用户?')) return try { await fetch(`${API_BASE}/api/admin/users/${id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); fetchUsers() } catch (e) { console.error(e) } } const filteredUsers = users.filter(u => u.f01_01_name?.includes(search) || u.phone?.includes(search) || u.user_code?.includes(search)) return (
setSearch(e.target.value)} style={{ flex: 1, padding: '10px', background: '#1e293b', border: '1px solid #334155', borderRadius: '6px', color: '#fff' }} />
{loading ?
加载中...
: (
{filteredUsers.map(u => (
{u.f01_01_name?.[0] || '?'}
{u.f01_01_name}
{u.user_code || '-'}
{u.role === 'admin' ? '管理员' : '用户'}
📱 {u.phone || '-'}
🏷️ 等级: {u.f99_94_level || '青铜'} | 藏品: {u.f99_97_collection_count || 0}
🔑 登录: {u.f99_98_login_count || 0}次 | AI: {u.f99_95_ai_count || 0}次
))}
)} {showAddModal && (

添加用户

setNewUser({...newUser, username: e.target.value})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px', boxSizing: 'border-box' }} /> setNewUser({...newUser, password: e.target.value})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px', boxSizing: 'border-box' }} /> setNewUser({...newUser, phone: e.target.value})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '12px', boxSizing: 'border-box' }} />
)} {editingUser && (

编辑用户

setEditingUser({...editingUser, phone: e.target.value})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px', boxSizing: 'border-box' }} /> setEditingUser({...editingUser, f99_100_points: parseInt(e.target.value)})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px', boxSizing: 'border-box' }} /> setEditingUser({...editingUser, f01_11_balance: parseFloat(e.target.value)})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '12px', boxSizing: 'border-box' }} />
)}
) } function Statistics({ token, API_BASE }) { const [stats, setStats] = useState(null) const [loading, setLoading] = useState(true) useEffect(() => { fetchStats() }, []) const fetchStats = async () => { setLoading(true) try { const collectionsRes = await fetch(`${API_BASE}/api/collections/stats`, { headers: { 'Authorization': `Bearer ${token}` } }) const collectionsData = await collectionsRes.json() const usersRes = await fetch(`${API_BASE}/api/admin/users?page=1&limit=100`, { headers: { 'Authorization': `Bearer ${token}` } }) const usersData = await usersRes.json() const totalUsers = Array.isArray(usersData) ? usersData.length : 0 const totalCollections = collectionsData.total || 0 const levelCount = {} if (Array.isArray(usersData)) { usersData.forEach(u => { const level = u.f99_94_level || '青铜'; levelCount[level] = (levelCount[level] || 0) + 1 }) } const statusCount = { in_collection: collectionsData.byStatus?.find(s => s.status === 'in_collection')?.count || 0, sold: collectionsData.byStatus?.find(s => s.status === 'sold')?.count || 0 } const totalBalance = Array.isArray(usersData) ? usersData.reduce((sum, u) => sum + (u.f01_11_balance || 0), 0) : 0 setStats({ users: { total: totalUsers, levels: levelCount }, collections: { total: totalCollections, status: statusCount }, balance: { total: totalBalance } }) } catch (e) { console.error(e) } setLoading(false) } if (loading) return
加载中...
return (
{stats?.users?.total || 0}
👥 用户总数
{stats?.collections?.total || 0}
📚 藏品总数
¥{stats?.balance?.total?.toFixed(2) || '0'}
💰 账户总余额
{stats?.collections?.status?.in_collection || 0}
✨ 收藏中

🏆 会员等级分布

{Object.entries(stats?.users?.levels || {}).map(([level, count]) => (
{level}{count}人
))}

📦 藏品状态

{stats?.collections?.status?.in_collection || 0}
收藏中
{stats?.collections?.status?.sold || 0}
已售出
) } function InfoPublish({ token, API_BASE }) { const [publishType, setPublishType] = useState('system') const [formData, setFormData] = useState({ title: '', content: '', expect_version: '', expect_packaging: '', deal_price: '' }) const [submitting, setSubmitting] = useState(false) const handlePublish = async () => { if (!formData.title) { alert('请输入标题'); return } setSubmitting(true) try { const data = { title: formData.title, content: formData.content, info_type: publishType === 'system' ? 'publish' : 'deal', deal_price: publishType === 'deal' ? parseFloat(formData.deal_price) : null, expect_version: formData.expect_version, expect_packaging: formData.expect_packaging } const res = await fetch(`${API_BASE}/api/information/`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) if (res.ok) { alert('发布成功!'); setFormData({ title: '', content: '', expect_version: '', expect_packaging: '', deal_price: '' }) } } catch (e) { alert('发布失败') } setSubmitting(false) } return (

{publishType === 'system' ? '📢 发布系统通知' : '📈 发布成交数据'}

setFormData({...formData, title: e.target.value})} placeholder={publishType === 'system' ? '请输入通知标题' : '如:2024版纪念钞成交'} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginTop: '4px', boxSizing: 'border-box' }} />