From bc23e9569198e16fdf1971d36df2ec6674176154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B2=E8=BE=B0=E7=94=9F=E4=BA=A7?= Date: Thu, 26 Mar 2026 20:26:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=A1=E6=81=AF=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=96=84=EF=BC=8C?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Info.jsx | 448 ++++++++++++++---------------------- 1 file changed, 172 insertions(+), 276 deletions(-) diff --git a/frontend/src/pages/Info.jsx b/frontend/src/pages/Info.jsx index 68d307c..64780ad 100644 --- a/frontend/src/pages/Info.jsx +++ b/frontend/src/pages/Info.jsx @@ -1,25 +1,20 @@ import React, { useState, useEffect } from 'react' -// 信息发布页面 - 仅信息员/管理员可用 export default function Info() { const [activeTab, setActiveTab] = useState('publish') const [showPublish, setShowPublish] = useState(false) const [myList, setMyList] = useState([]) const [loading, setLoading] = useState(false) + const [editingItem, setEditingItem] = useState(null) - // 表单数据 const [formData, setFormData] = useState({ - edition: '龙钞', - type: '标百', - isGraded: true, - category: '成交', - source: '直播', - prices: {}, - title: '', - content: '' + edition: '龙钞', type: '标百', isGraded: true, category: '成交', source: '直播', title: '', content: '' }) - // 自动生成标题 + useEffect(() => { + if (activeTab === 'manage') fetchMyList() + }, [activeTab]) + useEffect(() => { const now = new Date() const date = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}` @@ -30,121 +25,66 @@ export default function Info() { const API_BASE = localStorage.getItem('API_BASE') || '' - // 获取我的发布列表 - useEffect(() => { - if (activeTab === 'manage') { - fetchMyList() - } - }, [activeTab]) - const fetchMyList = async () => { setLoading(true) try { const token = localStorage.getItem('token') - const res = await fetch(`${API_BASE}/api/information/my`, { - headers: { Authorization: `Bearer ${token}` } - }) - const data = await res.json() - setMyList(data || []) - } catch (e) { - console.error(e) - } + const res = await fetch(`${API_BASE}/api/information/my/list`, { headers: { Authorization: `Bearer ${token}` } }) + setMyList(res.ok ? await res.json() : []) + } catch (e) { console.error(e) } setLoading(false) } - // 发布信息 const handlePublish = async () => { - if (!formData.title) { - alert('请填写标题') - return - } - + if (!formData.title) { alert('请填写标题'); return } try { const token = localStorage.getItem('token') - const gradeNote = formData.isGraded ? '(评级币)' : '(裸钞)' - - let priceContent = '' - if (formData.prices && Object.keys(formData.prices).length > 0) { - priceContent = '\n价格:' - const priceLabels = { - price_4: '带4', - price_no4: '无4', - price_no47: '无47', - price_no347: '无347', - price_no1347: '无1347', - price_no247: '无247', - price_no1247: '无1247', - price_no12347: '无12347' - } - Object.entries(formData.prices).forEach(([key, value]) => { - if (value) { - priceContent += `${priceLabels[key] || key}:¥${value} ` - } - }) - } - - const res = await fetch(`${API_BASE}/api/information/publish`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - title: formData.title, - content: formData.content + priceContent, - info_type: formData.category === '成交' ? 'deal' : 'seek' - }) + const res = await fetch(`${API_BASE}/api/information/`, { + method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ title: formData.title, content: formData.content, info_type: formData.category === '成交' ? 'deal' : 'seek' }) }) - const data = await res.json() if (data.id || data.code === 0) { alert('发布成功!') setShowPublish(false) - setFormData({ - edition: '龙钞', - type: '标百', - isGraded: true, - category: '成交', - source: '直播', - prices: {}, - title: '', - content: '' - }) + setFormData({ edition: '龙钞', type: '标百', isGraded: true, category: '成交', source: '直播', title: '', content: '' }) fetchMyList() - } else { - alert(data.message || '发布失败') - } - } catch (e) { - alert('发布失败: ' + e.message) - } + } else { alert(data.message || '发布失败') } + } catch (e) { alert('发布失败: ' + e.message) } } - // Tab切换 - const tabs = [ - { key: 'publish', label: '📝 发布行情' }, - { key: 'manage', label: '📋 发布管理' } - ] + const handleDelete = async (id) => { + if (!confirm('确定删除这条信息吗?')) return + try { + const token = localStorage.getItem('token') + const res = await fetch(`${API_BASE}/api/information/${id}`, { method: 'DELETE', headers: { Authorization: `Bearer ${token}` } }) + if (res.ok) { alert('删除成功'); fetchMyList() } else { alert('删除失败') } + } catch (e) { alert('删除失败: ' + e.message) } + } - // 按钮选择组件 - const ButtonGroup = ({ options, value, onChange, label }) => ( -
- {label && } -
+ const handleEdit = (item) => setEditingItem({ id: item.id, title: item.title, content: item.content }) + + const handleSaveEdit = async () => { + if (!editingItem) return + try { + const token = localStorage.getItem('token') + const res = await fetch(`${API_BASE}/api/information/${editingItem.id}`, { + method: 'PUT', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ title: editingItem.title, content: editingItem.content }) + }) + if (res.ok) { alert('保存成功'); setEditingItem(null); fetchMyList() } else { alert('保存失败') } + } catch (e) { alert('保存失败: ' + e.message) } + } + + const BtnGroup = ({ options, value, onChange, label }) => ( +
+
{label}
+
{options.map(opt => ( - ))} @@ -152,199 +92,155 @@ export default function Info() {
) - // 价格网格 - const PriceGrid = () => { - const priceItems = [ - { key: 'price_4', label: '带4' }, - { key: 'price_no4', label: '无4' }, - { key: 'price_no47', label: '无47' }, - { key: 'price_no347', label: '无347' }, - { key: 'price_no1347', label: '无1347' }, - { key: 'price_no247', label: '无247' }, - { key: 'price_no1247', label: '无1247' }, - { key: 'price_no12347', label: '无12347' } - ] - - return ( -
- -
- {priceItems.map(item => ( -
- - setFormData({ - ...formData, - prices: {...formData.prices, [item.key]: e.target.value} - })} - placeholder="¥" - style={{ - width: '100%', - padding: '8px', - background: '#0f172a', - border: '1px solid #334155', - borderRadius: '4px', - color: '#fff', - boxSizing: 'border-box', - fontSize: '13px' - }} - /> + const Card = ({ children, title, action }) => ( +
+ {title &&
+

{title}

+ {action} +
} + {children} +
+ ) + + const formatDate = (d) => d ? new Date(d).toLocaleString('zh-CN').slice(0, 16) : '-' + + return ( +
+ {/* 顶部标签 */} +
+
+ {[{ key: 'publish', label: '📝 发布行情', icon: '📝' }, { key: 'manage', label: '📋 发布管理', icon: '📋' }].map(t => ( +
setActiveTab(t.key)} + style={{ flex: 1, padding: '12px 16px', borderRadius: '10px', background: activeTab === t.key ? 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)' : 'transparent', + color: activeTab === t.key ? '#fff' : '#9ca3af', cursor: 'pointer', textAlign: 'center', fontSize: '14px', fontWeight: '600', transition: 'all 0.3s', boxShadow: activeTab === t.key ? '0 2px 10px rgba(59,130,246,0.4)' : 'none' }}> + {t.label}
))}
- ) - } - const formatDate = (dateStr) => { - if (!dateStr) return '-' - const date = new Date(dateStr) - return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}` - } +
+ {activeTab === 'publish' && ( +
+ {/* 新增按钮 */} +
+ +
- return ( -
- {/* Tab导航 */} -
- {tabs.map(tab => ( -
setActiveTab(tab.key)} - style={{ - padding: '10px 20px', - borderRadius: '8px', - background: activeTab === tab.key ? '#3b82f6' : 'transparent', - color: '#fff', - cursor: 'pointer', - whiteSpace: 'nowrap', - fontSize: '14px' - }} - > - {tab.label} -
- ))} -
+ {showPublish && ( + + setFormData({...formData, edition: v})} /> - {/* 发布行情 */} - {activeTab === 'publish' && ( -
-
- -
- - {showPublish && ( -
-

📝 发布行情信息

- - setFormData({...formData, edition: v})} /> - -
-
+
- -
+
类型
+
{['标百', '标十', '单张'].map(t => ( + style={{ flex: 1, padding: '10px', borderRadius: '8px', border: formData.type === t ? '2px solid #10b981' : '1px solid #374151', + background: formData.type === t ? 'rgba(16,185,129,0.15)' : '#1f2937', color: formData.type === t ? '#10b981' : '#d1d5db', cursor: 'pointer', fontSize: '13px' }}>{t} ))}
- +
是否评级
-
- setFormData({...formData, category: v})} /> + setFormData({...formData, category: v})} /> - setFormData({...formData, source: v})} /> + setFormData({...formData, source: v})} /> - - -
- - -
- -
- -