From 509e37cd98d0f5c804e43b4b3d4b56f069befe2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B2=E8=BE=B0=E7=94=9F=E4=BA=A7?= Date: Sun, 29 Mar 2026 16:20:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5C=E7=8E=AF=E5=A2=83=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E4=BB=A3=E7=A0=81=20-=20v1.2.24=20(=E5=8C=85=E5=90=AB?= =?UTF-8?q?=E5=AF=BB=E5=8F=B7=E5=92=8C=E8=A1=8C=E6=83=85=E5=8C=BA=E5=88=86?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/index.html | 2 +- frontend/src/pages/Admin.jsx | 394 +++++++++++++++++++++++++-------- frontend/src/pages/AdminV2.jsx | 272 +++++++++++++++++++++++ frontend/src/pages/Info.jsx | 6 + frontend/src/pages/List.jsx | 23 +- frontend/src/pages/News.jsx | 82 +++++-- 6 files changed, 657 insertions(+), 122 deletions(-) create mode 100644 frontend/src/pages/AdminV2.jsx diff --git a/frontend/index.html b/frontend/index.html index dafb732..fb6842a 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - 甲辰收藏 v1.2.19 + 甲辰收藏 v1.2.24 diff --git a/frontend/src/pages/Admin.jsx b/frontend/src/pages/Admin.jsx index 7e7953d..653f65f 100644 --- a/frontend/src/pages/Admin.jsx +++ b/frontend/src/pages/Admin.jsx @@ -10,9 +10,12 @@ export default function Admin() { const [newUser, setNewUser] = useState({ username: '', password: '', email: '', role: 'user' }) const token = localStorage.getItem('token') + // 检查权限 const userStr = localStorage.getItem('user') let user = null - try { user = userStr ? JSON.parse(userStr) : null } catch (e) {} + try { + user = userStr ? JSON.parse(userStr) : null + } catch (e) {} if (!user || user.role !== 'admin') { return ( @@ -26,63 +29,131 @@ export default function Admin() { ) } - useEffect(() => { fetchUsers() }, []) + useEffect(() => { + fetchUsers() + }, []) const fetchUsers = async () => { try { - const res = await fetch('/api/admin/users?page=1&limit=100', { headers: { 'Authorization': `Bearer ${token}` } }) + const res = await fetch('/api/admin/users?page=1&limit=100', { + headers: { 'Authorization': `Bearer ${token}` } + }) if (!res.ok) throw new Error('获取用户列表失败') const data = await res.json() setUsers(data) - } catch (err) { setError(err.message) } finally { setLoading(false) } + } catch (err) { + setError(err.message) + } finally { + setLoading(false) + } } const handleDeleteUser = async (userId, username) => { if (!confirm(`确定要删除用户 "${username}" 吗?`)) return + try { - const res = await fetch(`/api/admin/users/${userId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }) + const res = await fetch(`/api/admin/users/${userId}`, { + method: 'DELETE', + headers: { 'Authorization': `Bearer ${token}` } + }) if (!res.ok) throw new Error('删除失败') fetchUsers() - } catch (err) { alert(err.message) } + } catch (err) { + alert(err.message) + } } const handleAddUser = async () => { - if (!newUser.username || !newUser.password) { alert('用户名和密码为必填项'); return } + if (!newUser.username || !newUser.password) { + alert('用户名和密码为必填项') + return + } + + // 如果 email 为空,不传这个字段 const payload = { ...newUser } - if (!payload.email) delete payload.email + if (!payload.email) { + delete payload.email + } + try { - const res = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(payload) }) - if (!res.ok) { const data = await res.json(); throw new Error(data.detail || '添加失败') } + const res = await fetch('/api/auth/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(payload) + }) + + if (!res.ok) { + const data = await res.json() + throw new Error(data.detail || '添加失败') + } + alert('添加成功!') setShowAddModal(false) setNewUser({ username: '', password: '', email: '', role: 'user' }) fetchUsers() - } catch (err) { alert(err.message) } + } catch (err) { + alert(err.message) + } } const handleEditUser = async () => { - if (!editingUser.username) { alert('用户名不能为空'); return } - if (editingUser.newPassword || editingUser.confirmPassword) { - if (!editingUser.newPassword || !editingUser.confirmPassword) { alert('请填写完整密码信息'); return } - if (editingUser.newPassword !== editingUser.confirmPassword) { alert('两次输入的密码不一致'); return } - if (editingUser.newPassword.length < 6) { alert('密码至少 6 个字符'); return } + if (!editingUser.username) { + alert('用户名不能为空') + return } + + // 验证密码 + if (editingUser.newPassword || editingUser.confirmPassword) { + if (!editingUser.newPassword || !editingUser.confirmPassword) { + alert('请填写完整密码信息') + return + } + if (editingUser.newPassword !== editingUser.confirmPassword) { + alert('两次输入的密码不一致') + return + } + if (editingUser.newPassword.length < 6) { + alert('密码至少 6 个字符') + return + } + } + try { + // 准备更新数据 const updateData = { username: editingUser.username, email: editingUser.email, - phone: editingUser.phone, - role: editingUser.role, - level: editingUser.level, - points: editingUser.points + role: editingUser.role } - if (editingUser.newPassword && editingUser.newPassword.trim()) updateData.password = editingUser.newPassword - const res = await fetch(`/api/admin/users/${editingUser.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(updateData) }) - if (!res.ok) { const data = await res.json(); throw new Error(data.detail || '更新失败') } + + // 如果有新密码,添加到更新数据 + if (editingUser.newPassword && editingUser.newPassword.trim()) { + updateData.password = editingUser.newPassword + } + + const res = await fetch(`/api/admin/users/${editingUser.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(updateData) + }) + + if (!res.ok) { + const data = await res.json() + throw new Error(data.detail || '更新失败') + } + alert('更新成功!') setEditingUser(null) fetchUsers() - } catch (err) { alert(err.message) } + } catch (err) { + alert(err.message) + } } if (loading) return
加载中...
@@ -91,116 +162,257 @@ export default function Admin() { return (
-

⚙️ 用户管理

- +

+ ⚙️ 用户管理 +

+
+ {/* 用户列表 - 卡片式布局 */}
- {users.map(u => ( -
+ {users.map((user) => ( +
-
#{u.user_code} {u.username}
-
- {u.role === 'admin' ? '👑 管理员' : u.role === 'editor' ? '📝 信息员' : '👤 用户'} +
{user.username}
+
+ {user.role === 'admin' ? '👑 管理员' : '👤 用户'}
- {u.level && {u.level === '青铜' ? '🥉' : u.level === '白银' ? '🥈' : u.level === '黄金' ? '🥇' : u.level === '钻石' ? '💎' : '👑'} {u.level}}
-
📧 {u.email || '未设置'} | 📱 {u.phone || '未设置'}
-
- e.currentTarget.style.background = 'rgba(34,197,94,0.2)'} onMouseOut={(e) => e.currentTarget.style.background = '#0f172a'}>
藏品
{u.collectionCount || 0}
-
AI识别
{u.aiCount || 0}
-
寻号
{u.searchCount || 0}
-
积分
{u.points || 0}
+
+ 📧 {user.email || '未设置'} | 📱 {user.phone || '未设置'}
-
余额
-
¥{u.balance || 0}
+
藏品数
+
window.location.hash = '#/list?userId=' + user.id} title='点击查看'>{user.collection_count || 0}
-
注册时间:{u.created_at ? new Date(u.created_at).toLocaleDateString('zh-CN') : '-'}
+
+ 注册时间:{user.created_at ? new Date(user.created_at).toLocaleDateString('zh-CN') : '-'} +
- - {u.role !== 'admin' && } + + {user.role !== 'admin' && ( + + )}
))}
- {users.length === 0 &&
暂无用户数据
} + {users.length === 0 && ( +
+ 暂无用户数据 +
+ )} - {/* 添加用户弹窗 */} + {/* 添加用户模态框 */} {showAddModal && (

添加用户

-
setNewUser({ ...newUser, username: e.target.value })} style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} />
-
setNewUser({ ...newUser, password: e.target.value })} style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} />
-
setNewUser({ ...newUser, email: e.target.value })} style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} />
-
-
+ +
+ + setNewUser({ ...newUser, username: e.target.value })} + style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} + /> +
+ +
+ + setNewUser({ ...newUser, password: e.target.value })} + style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} + /> +
+ +
+ + setNewUser({ ...newUser, email: e.target.value })} + style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} + /> +
+ +
+ + +
+ +
+ + +
)} - {/* 编辑用户弹窗 - 完整字段 */} + {/* 编辑用户模态框 */} {editingUser && (
-
+

编辑用户

- {/* 基本信息 */} -
-

📋 基本信息

-
-
setEditingUser({ ...editingUser, user_code: e.target.value })} style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fbbf24', fontSize: '13px' }} />
setEditingUser({ ...editingUser, username: e.target.value })} style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} />
-
setEditingUser({ ...editingUser, phone: e.target.value })} style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} />
-
setEditingUser({ ...editingUser, email: e.target.value })} style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} />
+ {/* 第一部分:基本信息 */} +
+

📋 基本信息

+ +
+ + setEditingUser({ ...editingUser, username: e.target.value })} + style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, email: e.target.value })} + style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} + /> +
+ +
+ +
- - {/* 会员信息 */} -
-

⭐ 会员信息

-
-
-
-
setEditingUser({ ...editingUser, points: parseInt(e.target.value) || 0 })} style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} />
-
setEditingUser({ ...editingUser, balance: parseFloat(e.target.value) || 0 })} style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} />
+ + {/* 第二部分:修改密码 */} +
+

🔐 修改密码(可选)

+ +
+ + setEditingUser({ ...editingUser, newPassword: e.target.value })} + placeholder="请输入新密码(至少 6 位)" + style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} + /> +
+ +
+ + setEditingUser({ ...editingUser, confirmPassword: e.target.value })} + placeholder="请再次输入新密码" + style={{ width: '100%', padding: '10px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }} + />
- - {/* 统计信息(只读) */} -
-

📊 统计数据(仅展示)

-
-
藏品
{editingUser.collectionCount || 0}
-
AI识别
{editingUser.aiCount || 0}
-
寻号
{editingUser.searchCount || 0}
-
登录
{editingUser.loginCount || 0}
-
-
- - {/* 修改密码 */} -
-

🔐 修改密码(可选)

-
-
setEditingUser({ ...editingUser, newPassword: e.target.value })} placeholder="留空则不修改" style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} />
-
setEditingUser({ ...editingUser, confirmPassword: e.target.value })} placeholder="再次输入" style={{ width: '100%', padding: '8px', background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '6px', color: '#fff', fontSize: '13px' }} />
-
-
- +
- - + +
)} + + {/* 版本号 - 移到表格下方,避免被底部导航遮挡 */} +
+ v{APP_VERSION} +
) } diff --git a/frontend/src/pages/AdminV2.jsx b/frontend/src/pages/AdminV2.jsx new file mode 100644 index 0000000..1e3d355 --- /dev/null +++ b/frontend/src/pages/AdminV2.jsx @@ -0,0 +1,272 @@ +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' }} />
+