jiachenlong/frontend/src/pages/AdminV2.jsx

273 lines
24 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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'
// 管理后台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 (
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#0f172a', color: '#ef4444', padding: '20px' }}>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '48px', marginBottom: '16px' }}>🚫</div>
<div style={{ fontSize: '18px', fontWeight: 'bold', marginBottom: '8px' }}>无权访问</div>
<div style={{ color: '#94a3b8', fontSize: '14px' }}>仅管理员可以访问管理后台</div>
</div>
</div>
)
}
return (
<div style={{ padding: '16px', paddingBottom: '80px', minHeight: '100vh', background: '#0f172a' }}>
<div style={{ textAlign: 'center', marginBottom: '16px' }}>
<h1 style={{ fontSize: '20px', color: '#fbbf24', margin: 0 }}> 管理后台</h1>
</div>
<div style={{ display: 'flex', background: '#1e293b', borderRadius: '8px', padding: '4px', marginBottom: '16px', flexWrap: 'wrap' }}>
{[
{ key: 'users', label: '👥 用户管理' },
{ key: 'stats', label: '📊 统计分析' },
{ key: 'publish', label: '📢 信息发布' },
{ key: 'infoManage', label: '📋 信息管理' }
].map(tab => (
<div key={tab.key} onClick={() => 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}
</div>
))}
</div>
{activeTab === 'users' && <UserManagement token={token} API_BASE={API_BASE} />}
{activeTab === 'stats' && <Statistics token={token} API_BASE={API_BASE} />}
{activeTab === 'publish' && <InfoPublish token={token} API_BASE={API_BASE} />}
{activeTab === 'infoManage' && <InfoManage token={token} API_BASE={API_BASE} />}
</div>
)
}
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.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.username?.includes(search) || u.phone?.includes(search) || u.user_code?.includes(search))
return (
<div>
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
<input type="text" placeholder="搜索用户..." value={search} onChange={(e) => setSearch(e.target.value)} style={{ flex: 1, padding: '10px', background: '#1e293b', border: '1px solid #334155', borderRadius: '6px', color: '#fff' }} />
<button onClick={() => setShowAddModal(true)} style={{ padding: '10px 16px', background: '#3b82f6', color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer' }}>+ 添加</button>
</div>
{loading ? <div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>加载中...</div> : (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: '12px' }}>
{filteredUsers.map(u => (
<div key={u.id} style={{ background: '#1e293b', borderRadius: '12px', padding: '16px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<div style={{ width: '40px', height: '40px', borderRadius: '50%', background: '#3b82f6', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: '16px' }}>{u.username?.[0] || '?'}</div>
<div><div style={{ color: '#fff', fontWeight: 'bold' }}>{u.username}</div><div style={{ color: '#64748b', fontSize: '12px' }}>{u.user_code || '-'}</div></div>
</div>
<span style={{ background: u.role === 'admin' ? '#f59e0b' : '#10b981', color: '#fff', padding: '2px 8px', borderRadius: '4px', fontSize: '12px' }}>{u.role === 'admin' ? '管理员' : '用户'}</span>
</div>
<div style={{ fontSize: '12px', color: '#94a3b8', marginBottom: '8px' }}>
<div>📱 {u.phone || '-'}</div><div>🏷 : {u.level || ''} | : {u.collectionCount || 0}</div>
<div>🔑 登录: {u.loginCount || 0} | AI: {u.aiCount || 0}</div>
</div>
<div style={{ display: 'flex', gap: '8px' }}>
<button onClick={() => setEditingUser(u)} style={{ flex: 1, padding: '6px', background: '#3b82f6', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '12px' }}>编辑</button>
<button onClick={() => handleDeleteUser(u.id)} style={{ flex: 1, padding: '6px', background: '#ef4444', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '12px' }}>删除</button>
</div>
</div>
))}
</div>
)}
{showAddModal && (
<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 }}>
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '20px', width: '90%', maxWidth: '400px' }}>
<h3 style={{ color: '#fbbf24', marginTop: 0 }}>添加用户</h3>
<input type="text" placeholder="用户名" value={newUser.username} onChange={(e) => 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' }} />
<input type="password" placeholder="密码" value={newUser.password} onChange={(e) => 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' }} />
<input type="text" placeholder="手机号" value={newUser.phone} onChange={(e) => 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' }} />
<select value={newUser.role} onChange={(e) => setNewUser({...newUser, role: e.target.value})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '12px' }}><option value="user">普通用户</option><option value="admin"></option></select>
<div style={{ display: 'flex', gap: '8px' }}>
<button onClick={handleAddUser} style={{ flex: 1, padding: '10px', background: '#3b82f6', color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer' }}>添加</button>
<button onClick={() => setShowAddModal(false)} style={{ flex: 1, padding: '10px', background: '#64748b', color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer' }}>取消</button>
</div>
</div>
</div>
)}
{editingUser && (
<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 }}>
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '20px', width: '90%', maxWidth: '400px' }}>
<h3 style={{ color: '#fbbf24', marginTop: 0 }}>编辑用户</h3>
<input type="text" placeholder="手机号" value={editingUser.phone || ''} onChange={(e) => 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' }} />
<select value={editingUser.role} onChange={(e) => setEditingUser({...editingUser, role: e.target.value})} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginBottom: '8px' }}><option value="user">普通用户</option><option value="admin"></option></select>
<input type="number" placeholder="积分" value={editingUser.points || 0} onChange={(e) => setEditingUser({...editingUser, 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' }} />
<input type="number" placeholder="余额" value={editingUser.balance || 0} onChange={(e) => setEditingUser({...editingUser, 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' }} />
<div style={{ display: 'flex', gap: '8px' }}>
<button onClick={handleUpdateUser} style={{ flex: 1, padding: '10px', background: '#3b82f6', color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer' }}>保存</button>
<button onClick={() => setEditingUser(null)} style={{ flex: 1, padding: '10px', background: '#64748b', color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer' }}>取消</button>
</div>
</div>
</div>
)}
</div>
)
}
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.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.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 <div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>加载中...</div>
return (
<div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '12px', marginBottom: '16px' }}>
<div style={{ background: 'linear-gradient(135deg, #3b82f6, #1d4ed8)', borderRadius: '12px', padding: '16px', textAlign: 'center' }}><div style={{ fontSize: '32px', fontWeight: 'bold', color: '#fff' }}>{stats?.users?.total || 0}</div><div style={{ color: 'rgba(255,255,255,0.8)', fontSize: '14px' }}>👥 </div></div>
<div style={{ background: 'linear-gradient(135deg, #10b981, #059669)', borderRadius: '12px', padding: '16px', textAlign: 'center' }}><div style={{ fontSize: '32px', fontWeight: 'bold', color: '#fff' }}>{stats?.collections?.total || 0}</div><div style={{ color: 'rgba(255,255,255,0.8)', fontSize: '14px' }}>📚 </div></div>
<div style={{ background: 'linear-gradient(135deg, #f59e0b, #d97706)', borderRadius: '12px', padding: '16px', textAlign: 'center' }}><div style={{ fontSize: '32px', fontWeight: 'bold', color: '#fff' }}>¥{stats?.balance?.total?.toFixed(2) || '0'}</div><div style={{ color: 'rgba(255,255,255,0.8)', fontSize: '14px' }}>💰 </div></div>
<div style={{ background: 'linear-gradient(135deg, #8b5cf6, #7c3aed)', borderRadius: '12px', padding: '16px', textAlign: 'center' }}><div style={{ fontSize: '32px', fontWeight: 'bold', color: '#fff' }}>{stats?.collections?.status?.in_collection || 0}</div><div style={{ color: 'rgba(255,255,255,0.8)', fontSize: '14px' }}> </div></div>
</div>
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '16px', marginBottom: '12px' }}><h3 style={{ color: '#fbbf24', marginTop: 0, marginBottom: '12px', fontSize: '16px' }}>🏆 会员等级分布</h3><div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>{Object.entries(stats?.users?.levels || {}).map(([level, count]) => (<div key={level} style={{ background: '#0f172a', padding: '8px 12px', borderRadius: '8px' }}><span style={{ color: '#fff', fontWeight: 'bold' }}>{level}</span><span style={{ color: '#64748b', fontSize: '12px', marginLeft: '8px' }}>{count}</span></div>))}</div></div>
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '16px' }}><h3 style={{ color: '#fbbf24', marginTop: 0, marginBottom: '12px', fontSize: '16px' }}>📦 藏品状态</h3><div style={{ display: 'flex', gap: '12px' }}><div style={{ flex: 1, background: '#10b98120', padding: '12px', borderRadius: '8px', textAlign: 'center' }}><div style={{ fontSize: '24px', fontWeight: 'bold', color: '#10b981' }}>{stats?.collections?.status?.in_collection || 0}</div><div style={{ color: '#94a3b8', fontSize: '12px' }}></div></div><div style={{ flex: 1, background: '#ef444420', padding: '12px', borderRadius: '8px', textAlign: 'center' }}><div style={{ fontSize: '24px', fontWeight: 'bold', color: '#ef4444' }}>{stats?.collections?.status?.sold || 0}</div><div style={{ color: '#94a3b8', fontSize: '12px' }}></div></div></div></div>
</div>
)
}
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 (
<div>
<div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
<button onClick={() => setPublishType('system')} style={{ flex: 1, padding: '12px', background: publishType === 'system' ? '#3b82f6' : '#1e293b', color: '#fff', border: 'none', borderRadius: '8px', cursor: 'pointer' }}>📢 系统通知</button>
<button onClick={() => setPublishType('deal')} style={{ flex: 1, padding: '12px', background: publishType === 'deal' ? '#10b981' : '#1e293b', color: '#fff', border: 'none', borderRadius: '8px', cursor: 'pointer' }}>📈 成交数据</button>
</div>
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '16px' }}>
<h3 style={{ color: '#fbbf24', marginTop: 0, marginBottom: '16px' }}>{publishType === 'system' ? '📢 发布系统通知' : '📈 发布成交数据'}</h3>
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>标题 *</label><input type="text" value={formData.title} onChange={(e) => 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' }} /></div>
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>内容</label><textarea value={formData.content} onChange={(e) => setFormData({...formData, content: e.target.value})} placeholder="请输入详细内容..." rows={4} style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginTop: '4px', boxSizing: 'border-box', resize: 'vertical' }} /></div>
{publishType === 'deal' && (<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}><div style={{ flex: 1 }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>成交价()</label><input type="number" value={formData.deal_price} onChange={(e) => setFormData({...formData, deal_price: e.target.value})} placeholder="成交金额" style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginTop: '4px', boxSizing: 'border-box' }} /></div><div style={{ flex: 1 }}><label style={{ color: '#94a3b8', fontSize: '12px' }}></label><input type="text" value={formData.expect_version} onChange={(e) => setFormData({...formData, expect_version: e.target.value})} placeholder="2024" style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginTop: '4px', boxSizing: 'border-box' }} /></div><div style={{ flex: 1 }}><label style={{ color: '#94a3b8', fontSize: '12px' }}></label><input type="text" value={formData.expect_packaging} onChange={(e) => setFormData({...formData, expect_packaging: e.target.value})} placeholder="" style={{ width: '100%', padding: '10px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', marginTop: '4px', boxSizing: 'border-box' }} /></div></div>)}
<button onClick={handlePublish} disabled={submitting} style={{ width: '100%', padding: '12px', background: '#3b82f6', color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '16px', fontWeight: 'bold', opacity: submitting ? 0.6 : 1 }}>{submitting ? '发布中...' : '发布'}</button>
</div>
</div>
)
}
function InfoManage({ token, API_BASE }) {
const [infoList, setInfoList] = useState([])
const [loading, setLoading] = useState(true)
const [filterType, setFilterType] = useState('all')
useEffect(() => { fetchInfoList() }, [])
const fetchInfoList = async () => {
setLoading(true)
try { const res = await fetch(`${API_BASE}/api/information/list?status=active`, { headers: { 'Authorization': `Bearer ${token}` } }); const data = await res.json(); setInfoList(data || []) } catch (e) { console.error(e) }
setLoading(false)
}
const handleDelete = async (id) => { if (!confirm('确定删除该信息?')) return; try { await fetch(`${API_BASE}/api/information/${id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); fetchInfoList() } catch (e) { console.error(e) } }
const handleClose = async (id) => { try { await fetch(`${API_BASE}/api/information/${id}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ status: 'closed' }) }); fetchInfoList() } catch (e) { console.error(e) } }
const filteredList = filterType === 'all' ? infoList : infoList.filter(i => i.info_type === filterType)
const typeLabels = { seek: '🔍 寻配号', deal: '📈 成交', publish: '📝 发布' }
return (
<div>
<div style={{ display: 'flex', gap: '8px', marginBottom: '16px', overflowX: 'auto' }}>{['all', 'seek', 'deal', 'publish'].map(type => (<button key={type} onClick={() => setFilterType(type)} style={{ padding: '8px 12px', background: filterType === type ? '#3b82f6' : '#1e293b', color: '#fff', border: 'none', borderRadius: '6px', cursor: 'pointer', whiteSpace: 'nowrap', fontSize: '13px' }}>{type === 'all' ? '全部' : typeLabels[type]}</button>))}</div>
{loading ? <div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>加载中...</div> : filteredList.length === 0 ? <div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}></div> : (
filteredList.map(item => (
<div key={item.id} style={{ background: '#1e293b', borderRadius: '12px', padding: '16px', marginBottom: '12px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
<span style={{ background: item.info_type === 'seek' ? '#3b82f6' : item.info_type === 'deal' ? '#10b981' : '#f59e0b', color: '#fff', padding: '2px 8px', borderRadius: '4px', fontSize: '12px' }}>{typeLabels[item.info_type]}</span>
<span style={{ color: '#64748b', fontSize: '12px' }}>{new Date(item.created_at).toLocaleDateString('zh-CN')}</span>
</div>
<h4 style={{ color: '#fff', margin: '0 0 8px 0' }}>{item.title}</h4>
{item.content && <p style={{ color: '#94a3b8', fontSize: '13px', margin: '0 0 8px 0' }}>{item.content}</p>}
<div style={{ color: '#64748b', fontSize: '12px', marginBottom: '8px' }}>👤 {item.user_name} | 👁 {item.view_count} | 📞 {item.contact_count}</div>
<div style={{ display: 'flex', gap: '8px' }}><button onClick={() => handleClose(item.id)} style={{ flex: 1, padding: '6px', background: '#f59e0b', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '12px' }}>关闭</button><button onClick={() => handleDelete(item.id)} style={{ flex: 1, padding: '6px', background: '#ef4444', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '12px' }}></button></div>
</div>
))
)}
</div>
)
}