jiachenlong/frontend/src/pages/News.jsx

701 lines
25 KiB
React
Raw Normal View History

import React, { useState, useEffect } from 'react'
// 资讯页面 - 寻配号、成交数据、发布中心
export default function News() {
const [activeTab, setActiveTab] = useState('seek') // seek-寻配号, deal-成交, publish-发布
const [infoList, setInfoList] = useState([])
const [loading, setLoading] = useState(false)
const [showPublish, setShowPublish] = useState(false)
const [myList, setMyList] = useState([])
// 发布表单
const [formData, setFormData] = useState({
info_type: 'seek',
title: '',
content: '',
collection_id: '',
expect_category: '',
expect_version: '',
expect_packaging: '',
expect_number: '',
expect_price_min: '',
expect_price_max: '',
deal_price: '',
deal_date: ''
})
// 我的藏品列表(用于关联)
const [myCollections, setMyCollections] = useState([])
const API_BASE = localStorage.getItem('API_BASE') || ''
useEffect(() => {
fetchInfoList()
fetchMyCollections()
fetchMyList()
}, [activeTab])
// 获取资讯列表
const fetchInfoList = async () => {
setLoading(true)
try {
const token = localStorage.getItem('token')
const res = await fetch(`${API_BASE}/api/information/list?info_type=${activeTab}`, {
headers: { 'Authorization': `Bearer ${token}` }
})
const data = await res.json()
setInfoList(data || [])
} catch (e) {
console.error(e)
}
setLoading(false)
}
// 获取我的藏品列表
const fetchMyCollections = async () => {
try {
const token = localStorage.getItem('token')
const res = await fetch(`${API_BASE}/api/collections?page=1&page_size=100`, {
headers: { 'Authorization': `Bearer ${token}` }
})
const data = await res.json()
setMyCollections(data.items || [])
} catch (e) {
console.error(e)
}
}
// 获取我的发布列表
const fetchMyList = async () => {
try {
const token = localStorage.getItem('token')
const res = await fetch(`${API_BASE}/api/information/my/list`, {
headers: { 'Authorization': `Bearer ${token}` }
})
const data = await res.json()
setMyList(data || [])
} catch (e) {
console.error(e)
}
}
// 发布资讯
const handlePublish = async () => {
if (!formData.title) {
alert('请输入标题')
return
}
try {
const token = localStorage.getItem('token')
const res = await fetch(`${API_BASE}/api/information/`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
...formData,
expect_price_min: formData.expect_price_min ? parseFloat(formData.expect_price_min) : null,
expect_price_max: formData.expect_price_max ? parseFloat(formData.expect_price_max) : null,
deal_price: formData.deal_price ? parseFloat(formData.deal_price) : null,
})
})
if (res.ok) {
alert('发布成功!')
setShowPublish(false)
setFormData({
info_type: 'seek',
title: '',
content: '',
collection_id: '',
expect_category: '',
expect_version: '',
expect_packaging: '',
expect_number: '',
expect_price_min: '',
expect_price_max: '',
deal_price: '',
deal_date: ''
})
fetchInfoList()
fetchMyList()
}
} catch (e) {
alert('发布失败')
}
}
// 删除发布
const handleDelete = async (id) => {
if (!confirm('确定删除?')) return
try {
const token = localStorage.getItem('token')
await fetch(`${API_BASE}/api/information/${id}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
})
fetchMyList()
} catch (e) {
console.error(e)
}
}
// 格式化日期
const formatDate = (dateStr) => {
if (!dateStr) return ''
return new Date(dateStr).toLocaleDateString('zh-CN')
}
// 资讯类型映射
const typeMap = {
seek: '寻配号',
deal: '成交数据',
publish: '发布'
}
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'
}}>
{[
{ key: 'seek', label: '🔍 寻配号' },
{ key: 'deal', label: '📈 成交数据' },
{ key: 'publish', label: ' 发布' }
].map(tab => (
<div
key={tab.key}
onClick={() => setActiveTab(tab.key)}
style={{
flex: 1,
textAlign: 'center',
padding: '10px',
borderRadius: '6px',
cursor: 'pointer',
background: activeTab === tab.key ? '#3b82f6' : 'transparent',
color: activeTab === tab.key ? '#fff' : '#94a3b8',
fontSize: '14px',
fontWeight: activeTab === tab.key ? 'bold' : 'normal'
}}
>
{tab.label}
</div>
))}
</div>
{/* 发布按钮 */}
{activeTab !== 'publish' && (
<div style={{ marginBottom: '16px', textAlign: 'right' }}>
<button
onClick={() => setShowPublish(!showPublish)}
style={{
background: '#3b82f6',
color: '#fff',
border: 'none',
padding: '8px 16px',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '14px'
}}
>
{showPublish ? '取消发布' : ' 发布'}
</button>
</div>
)}
{/* 发布表单 */}
{showPublish && (
<div style={{
background: '#1e293b',
borderRadius: '12px',
padding: '16px',
marginBottom: '16px'
}}>
<h3 style={{ color: '#fbbf24', marginTop: 0 }}>发布信息</h3>
{/* 选择类型 */}
<div style={{ marginBottom: '12px' }}>
<label style={{ color: '#94a3b8', fontSize: '12px' }}>类型</label>
<select
value={formData.info_type}
onChange={(e) => setFormData({...formData, info_type: e.target.value})}
style={{
width: '100%',
padding: '10px',
background: '#0f172a',
border: '1px solid #334155',
borderRadius: '6px',
color: '#fff',
marginTop: '4px'
}}
>
<option value="seek">🔍 寻配号</option>
<option value="deal">📈 成交数据</option>
<option value="publish">📝 普通发布</option>
</select>
</div>
{/* 标题 */}
<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="请输入标题"
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={3}
style={{
width: '100%',
padding: '10px',
background: '#0f172a',
border: '1px solid #334155',
borderRadius: '6px',
color: '#fff',
marginTop: '4px',
boxSizing: 'border-box',
resize: 'vertical'
}}
/>
</div>
{/* 关联藏品 */}
{formData.info_type === 'seek' && (
<div style={{ marginBottom: '12px' }}>
<label style={{ color: '#94a3b8', fontSize: '12px' }}>关联我的藏品可选</label>
<select
value={formData.collection_id}
onChange={(e) => setFormData({...formData, collection_id: e.target.value})}
style={{
width: '100%',
padding: '10px',
background: '#0f172a',
border: '1px solid #334155',
borderRadius: '6px',
color: '#fff',
marginTop: '4px'
}}
>
<option value="">不关联</option>
{myCollections.map(c => (
<option key={c.f99_90_id} value={c.f99_90_id}>
{c.f01_01_name} - {c.f01_03_category}
</option>
))}
</select>
</div>
)}
{/* 寻配号条件 */}
{formData.info_type === 'seek' && (
<>
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
<div style={{ flex: 1 }}>
<label style={{ color: '#94a3b8', fontSize: '12px' }}>期望类别</label>
<input
type="text"
value={formData.expect_category}
onChange={(e) => setFormData({...formData, expect_category: 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>
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
<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 style={{ flex: 1 }}>
<label style={{ color: '#94a3b8', fontSize: '12px' }}>期望号码</label>
<input
type="text"
value={formData.expect_number}
onChange={(e) => setFormData({...formData, expect_number: e.target.value})}
placeholder="如888"
style={{
width: '100%',
padding: '10px',
background: '#0f172a',
border: '1px solid #334155',
borderRadius: '6px',
color: '#fff',
marginTop: '4px',
boxSizing: 'border-box'
}}
/>
</div>
</div>
<div style={{ display: 'flex', gap: '8px', marginBottom: '12px' }}>
<div style={{ flex: 1 }}>
<label style={{ color: '#94a3b8', fontSize: '12px' }}>最低价格</label>
<input
type="number"
value={formData.expect_price_min}
onChange={(e) => setFormData({...formData, expect_price_min: e.target.value})}
placeholder="0"
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="number"
value={formData.expect_price_max}
onChange={(e) => setFormData({...formData, expect_price_max: e.target.value})}
placeholder="0"
style={{
width: '100%',
padding: '10px',
background: '#0f172a',
border: '1px solid #334155',
borderRadius: '6px',
color: '#fff',
marginTop: '4px',
boxSizing: 'border-box'
}}
/>
</div>
</div>
</>
)}
{/* 成交数据 */}
{formData.info_type === '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}
style={{
width: '100%',
padding: '12px',
background: '#3b82f6',
color: '#fff',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '16px',
fontWeight: 'bold'
}}
>
发布
</button>
</div>
)}
{/* 资讯列表 */}
{activeTab === 'publish' ? (
// 我的发布
<div>
<h3 style={{ color: '#fbbf24', marginBottom: '12px' }}>我的发布</h3>
{loading ? (
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>加载中...</div>
) : myList.length === 0 ? (
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>暂无发布</div>
) : (
myList.map(item => (
<div key={item.id} style={{
background: '#1e293b',
borderRadius: '12px',
padding: '16px',
marginBottom: '12px'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{
background: item.info_type === 'seek' ? '#3b82f6' : item.info_type === 'deal' ? '#10b981' : '#f59e0b',
color: '#fff',
padding: '2px 8px',
borderRadius: '4px',
fontSize: '12px'
}}>
{typeMap[item.info_type]}
</span>
<button
onClick={() => handleDelete(item.id)}
style={{
background: '#ef4444',
color: '#fff',
border: 'none',
padding: '4px 8px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px'
}}
>
删除
</button>
</div>
<h4 style={{ color: '#fff', margin: '8px 0' }}>{item.title}</h4>
<p style={{ color: '#94a3b8', fontSize: '13px', margin: 0 }}>{item.content}</p>
<div style={{ color: '#64748b', fontSize: '12px', marginTop: '8px' }}>
{formatDate(item.created_at)} · 浏览 {item.view_count} · 联系 {item.contact_count}
</div>
</div>
))
)}
</div>
) : (
// 寻配号 / 成交数据列表
<div>
{loading ? (
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>加载中...</div>
) : infoList.length === 0 ? (
<div style={{ textAlign: 'center', color: '#94a3b8', padding: '20px' }}>
暂无{activeTab === 'seek' ? '寻配号' : '成交数据'}信息
</div>
) : (
infoList.map(item => (
<div key={item.id} style={{
background: '#1e293b',
borderRadius: '12px',
padding: '16px',
marginBottom: '12px'
}}>
{/* 用户信息 */}
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '8px' }}>
<div style={{
width: '32px',
height: '32px',
borderRadius: '50%',
background: '#3b82f6',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
fontSize: '14px',
marginRight: '8px'
}}>
{item.user_name ? item.user_name[0] : '?'}
</div>
<span style={{ color: '#94a3b8', fontSize: '13px' }}>{item.user_name}</span>
<span style={{ color: '#64748b', fontSize: '12px', marginLeft: 'auto' }}>
{formatDate(item.created_at)}
</span>
</div>
{/* 标题 */}
<h4 style={{ color: '#fff', margin: '0 0 8px 0', fontSize: '16px' }}>{item.title}</h4>
{/* 内容 */}
{item.content && (
<p style={{ color: '#94a3b8', fontSize: '14px', margin: '0 0 8px 0' }}>{item.content}</p>
)}
{/* 寻配号条件 */}
{item.info_type === 'seek' && (item.expect_category || item.expect_version || item.expect_number) && (
<div style={{
background: '#0f172a',
borderRadius: '8px',
padding: '12px',
marginBottom: '8px'
}}>
<div style={{ color: '#fbbf24', fontSize: '12px', marginBottom: '4px' }}>期望条件</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
{item.expect_category && (
<span style={{ color: '#94a3b8', fontSize: '12px' }}>类别: {item.expect_category}</span>
)}
{item.expect_version && (
<span style={{ color: '#94a3b8', fontSize: '12px' }}>版别: {item.expect_version}</span>
)}
{item.expect_packaging && (
<span style={{ color: '#94a3b8', fontSize: '12px' }}>包装: {item.expect_packaging}</span>
)}
{item.expect_number && (
<span style={{ color: '#94a3b8', fontSize: '12px' }}>号码: {item.expect_number}</span>
)}
{(item.expect_price_min || item.expect_price_max) && (
<span style={{ color: '#94a3b8', fontSize: '12px' }}>
价格: {item.expect_price_min || '?'} - {item.expect_price_max || '?'}
</span>
)}
</div>
</div>
)}
{/* 成交数据 */}
{item.info_type === 'deal' && (
<div style={{
background: '#0f172a',
borderRadius: '8px',
padding: '12px',
marginBottom: '8px'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span style={{ color: '#10b981', fontSize: '18px', fontWeight: 'bold' }}>
¥{item.deal_price || '-'}
</span>
<span style={{ color: '#64748b', fontSize: '12px' }}>
{item.expect_version} / {item.expect_packaging}
</span>
</div>
</div>
)}
{/* 关联藏品 */}
{item.collection_name && (
<div style={{
background: '#3b82f620',
borderRadius: '8px',
padding: '8px 12px',
marginBottom: '8px',
borderLeft: '3px solid #3b82f6'
}}>
<span style={{ color: '#3b82f6', fontSize: '12px' }}>关联藏品: </span>
<span style={{ color: '#fff', fontSize: '13px' }}>{item.collection_name}</span>
</div>
)}
{/* 统计 */}
<div style={{ display: 'flex', gap: '16px', color: '#64748b', fontSize: '12px' }}>
<span>👁 {item.view_count}</span>
<span>📞 {item.contact_count}</span>
</div>
</div>
))
)}
</div>
)}
</div>
)
}