diff --git a/frontend/src/pages/News.jsx.bak b/frontend/src/pages/News.jsx.bak
deleted file mode 100644
index 95e4006..0000000
--- a/frontend/src/pages/News.jsx.bak
+++ /dev/null
@@ -1,827 +0,0 @@
-import React, { useState, useEffect } from 'react'
-// 本地获取用户手机号
-const getUserPhone = () => {
- try {
- const user = JSON.parse(localStorage.getItem('user') || '{}')
- console.log('getUserPhone user:', user)
- // Try multiple possible field names
- return user.phone || user.phoneNumber || user.mobile || user.tel || ''
- } catch { return '' }
-}
-
-// 资讯页面 - 展示寻配号和行情信息(所有人可见)
-export default function News() {
- const [activeTab, setActiveTab] = useState(localStorage.getItem('news_activeTab') || 'seek')
- const [showSeekPublish, setShowSeekPublish] = useState(false)
- const [showMySeeks, setShowMySeeks] = useState(false) // 我的寻号
- const [showContactId, setShowContactId] = useState(null) // 显示联系方式的寻号ID
- const [showCommentId, setShowCommentId] = useState(null) // 显示评论输入的寻号ID
- const [commentText, setCommentText] = useState('') // 评论内容
- const [comments, setComments] = useState({}) // 存储各寻号的评论
- const [matchedStatus, setMatchedStatus] = useState({}) // 存储各寻号的匹配状态
- const [showMatchList, setShowMatchList] = useState(false)
- const [matchCollections, setMatchCollections] = useState([])
- const [customModal, setCustomModal] = useState({show: false, title: '', content: ''})
- const [seekForm, setSeekForm] = useState({
- edition: '龙钞', type: '', category: '寻号', price: '', matchType: '模糊', features: '', content: '', title: '', contact: getUserPhone() || ''
- })
- const [infoList, setInfoList] = useState([])
- const [viewMode, setViewMode] = useState('all')
- const [loading, setLoading] = useState(false)
-
- const API_BASE = 'http://47.103.9.192:3000'
- const currentUser = localStorage.getItem('token') ? { id: 'logged_in' } : null
-
- // 获取资讯列表
- useEffect(() => {
- fetchInfoList()
- }, [activeTab])
-
- // 自动生成寻号标题
- useEffect(() => {
- const title = `「寻号 ${seekForm.edition || '龙钞,马钞,蛇钞'} J0${seekForm.features || 'XXXXXXXX'}」`
- setSeekForm(prev => ({...prev, title}))
- }, [seekForm.edition, seekForm.features])
-
- const fetchInfoList = async () => {
- setLoading(true)
- try {
- // 寻配号对所有人公开,无需登录即可查看
- const token = localStorage.getItem('token')
- // 根据tab获取不同类型的数据
- const type = activeTab === 'seek' ? 'seek' : 'deal'
- // 始终传递token,以便获取准确的matched_count
- const headers = token ? { Authorization: `Bearer ${token}` } : {}
- const res = await fetch(`${API_BASE}/api/information/list?info_type=${type}`, { headers })
- const data = await res.json()
- console.log('资讯列表:', data)
- setInfoList(data || [])
- } catch (e) {
- console.error(e)
- }
- setLoading(false)
- }
-
- // 获取匹配藏品列表
-
-
- // 获取寻号评论
- const fetchComments = async (infoId) => {
- // 每次都重新获取评论
- try {
- const res = await fetch(`${API_BASE}/api/information/comments/${infoId}`)
- const data = await res.json()
- setComments(prev => ({...prev, [infoId]: data || []}))
- } catch (e) {
- console.error('获取评论失败:', e)
- }
- }
-
- // 添加评论
- const addComment = async (infoId) => {
- if (!commentText.trim()) { alert('请输入评论内容'); return }
- const token = localStorage.getItem('token')
- if (!token) { alert('请先登录'); return }
- try {
- const res = await fetch(`${API_BASE}/api/information/comment`, {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
- body: JSON.stringify({ information_id: infoId, content: commentText })
- })
- const data = await res.json()
- if (data.message === '留言成功' || data.message === '评论成功') {
- setCommentText('')
- // 强制刷新评论列表,先清空缓存
- setComments(prev => {
- const newComments = {...prev}
- delete newComments[infoId]
- return newComments
- })
- // 然后重新获取
- setTimeout(() => fetchComments(infoId), 100)
- // 确保停留在寻号 tab
- if (activeTab !== 'seek') {
- setActiveTab('seek')
- }
- alert('留言成功!')
- } else {
- alert(data.detail || '留言失败')
- }
- } catch (e) {
- console.error('留言失败:', e)
- alert('留言失败,请重试')
- }
- }
-
- // 匹配并联系藏友
- const matchAndContact = async (infoId) => {
- const token = localStorage.getItem('token')
- if (!token) { alert('请先登录'); return }
- try {
- const res = await fetch(`${API_BASE}/api/information/seek/match-confirm`, {
- method: 'POST',
- headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
- body: JSON.stringify({ info_id: infoId })
- })
- const data = await res.json()
- if (data.message === '匹配成功,已通知发布者') {
- setMatchedStatus(prev => ({...prev, [infoId]: 'matched'}))
- setShowContactId(infoId) // 显示联系方式
- setCustomModal({show: true, title: '匹配成功', content: '已通知发布者,请等待对方联系'})
- fetchInfoList() // 刷新列表
- }
- } catch (e) {
- alert('操作失败: ' + e.message)
- }
- }
- const fetchMatchedUserInfo = async (infoId) => {
- console.log('fetchMatchedUserInfo called with:', infoId)
- let token = localStorage.getItem('token')
- if (!token) {
- // Try to refresh token
- const username = localStorage.getItem('username')
- const password = localStorage.getItem('password')
- if (username && password) {
- const form = new FormData()
- form.append('username', username)
- form.append('password', password)
- const loginRes = await fetch(`${API_BASE}/api/auth/login`, { method: 'POST', body: form })
- const loginData = await loginRes.json()
- if (loginData.access_token) {
- token = loginData.access_token
- localStorage.setItem('token', token)
- }
- }
- if (!token) { alert('请先登录'); return }
- }
- try {
- const res = await fetch(`${API_BASE}/api/information/seek/matched-user/${infoId}`, { headers: { Authorization: `Bearer ${token}` } })
- console.log('Matched user response:', res.status)
- const data = await res.json()
- console.log('Matched user data:', data)
- if (data.user_name) setCustomModal({show: true, title: '匹配者信息', content: `用户名: ${data.user_name}\n联系方式: ${data.matched_contact || '未提供'}`})
- else if (data.detail) alert(data.detail)
- } catch (e) { console.error(e) }
- }
- const fetchPublisherInfo = async (infoId) => {
- console.log('fetchPublisherInfo called with:', infoId)
- const token = localStorage.getItem('token')
- if (!token) { alert('请先登录'); return }
- try {
- const res = await fetch(`${API_BASE}/api/information/seek/publisher/${infoId}`, { headers: { Authorization: `Bearer ${token}` } })
- console.log('Publisher response:', res.status)
- const data = await res.json()
- console.log('Publisher data:', data)
- if (data.user_name) setCustomModal({show: true, title: '发布者信息', content: `用户名: ${data.user_name}\n联系方式: ${data.contact || '未提供'}`})
- else if (data.detail) alert(data.detail)
- } catch (e) { console.error(e) }
- }
- const fetchMatchCollections = async (infoId) => {
- const token = localStorage.getItem('token')
- if (!token) { alert('请先登录'); return }
- try {
- const res = await fetch(`${API_BASE}/api/information/seek/match?info_id=${infoId}`, {
- headers: { Authorization: `Bearer ${token}` }
- })
- const data = await res.json()
- console.log('匹配结果:', data)
- console.log('匹配数量:', data.matched_count)
- console.log('藏品列表:', data.collections)
- setMatchCollections(data.collections || [])
- setShowMatchList(true)
- } catch (e) {
- console.error('获取匹配藏品失败:', e)
- alert('获取匹配藏品失败: ' + e.message)
- }
- }
-
- // 获取当前用户ID
- const getUserId = () => {
- try {
- const user = JSON.parse(localStorage.getItem('user') || '{}')
- return user.id || user.user_id || user.f99_90_id || null
- } catch { return null }
- }
- const currentUserId = getUserId()
-
- // 获取我的寻号列表
- const [mySeekList, setMySeekList] = useState([])
- const [myMatchedList, setMyMatchedList] = useState([])
- const [editingSeek, setEditingSeek] = useState(null)
- const fetchMySeeks = async () => {
- const token = localStorage.getItem('token')
- if (!token) { alert('请先登录'); return }
- try {
- const res = await fetch(`${API_BASE}/api/information/my/list`, {
- headers: { Authorization: `Bearer ${token}` }
- })
- const data = await res.json()
- // 过滤出寻号类型的并筛选当前用户
- const seeks = Array.isArray(data) ? data.filter(item => item.info_type === 'seek' && item.user_id === currentUserId) : []
- setMySeekList(seeks)
- setInfoList(seeks) // 在主列表显示
- } catch (e) { console.error(e) }
- }
-
- // 获取我已匹配的寻号列表
- const fetchMyMatches = async () => {
- const token = localStorage.getItem('token')
- if (!token) { alert('请先登录'); return }
- try {
- const res = await fetch(`${API_BASE}/api/information/list?info_type=seek`, {
- headers: { Authorization: `Bearer ${token}` }
- })
- const data = await res.json()
- // 筛选我已匹配的(matched_user_id等于当前用户ID)
- const matched = data.filter(item => item.is_matched === 'matched' && item.matched_user_id === currentUserId)
- setMyMatchedList(matched)
- setInfoList(matched) // 在主列表显示
- } catch (e) { console.error(e) }
- }
-
- // 更新寻号
- const handleUpdateSeek = async () => {
- if (!editingSeek) return
- const token = localStorage.getItem('token')
- try {
- // 解析正文中的号码特征和联系方式
- const content = (editingSeek.content || '') + '\n联系方式: ' + editingSeek.contact
- await fetch(`${API_BASE}/api/information/${editingSeek.id}`, {
- method: 'PUT',
- headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
- body: JSON.stringify({
- title: editingSeek.title,
- content,
- expect_category: editingSeek.edition,
- expect_number: editingSeek.features ? 'J0' + editingSeek.features : null
- })
- })
- setEditingSeek(null)
- fetchMySeeks()
- } catch (e) { alert('更新失败') }
- }
-
- const handleSeekPublish = async () => {
- const phone = getUserPhone()
- const finalContact = seekForm.contact || phone || ''
- if (!finalContact) { alert('请填写联系方式'); return }
- const content = (seekForm.content ? seekForm.content + '\n' : '') + (seekForm.price ? `价格: ${seekForm.price}` : '') + (seekForm.features ? '\n号码特征: J0' + seekForm.features : '') + '\n联系方式: ' + finalContact
- try {
- const token = localStorage.getItem('token')
- // 从edition映射到category
- const categoryMap = { '龙钞': '龙钞', '马钞': '马钞', '蛇钞': '蛇钞' }
- const res = await fetch(`${API_BASE}/api/information/`, {
- method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
- body: JSON.stringify({
- title: seekForm.title,
- content,
- info_type: 'seek',
- expect_category: seekForm.edition,
- expect_number: seekForm.features ? 'J0' + seekForm.features : None
- })
- })
- const data = await res.json()
- if (data.id || data.code === 0) {
- alert('发布成功!')
- setShowSeekPublish(false)
- setSeekForm({ edition: '龙钞', type: '', category: '寻号', price: '', matchType: '模糊', features: '', content: '', title: '', contact: getUserPhone() || '' })
- fetchInfoList()
- } else { alert(data.message || '发布失败') }
- } catch (e) { alert('发布失败: ' + e.message) }
- }
-
- // Tab切换
- const tabs = [
- { key: 'seek', label: '🔍 寻配号' },
- { key: 'deal', label: '💰 成交行情' }
- ]
-
- const formatDate = (dateStr) => {
- if (!dateStr) return '-'
- const date = new Date(dateStr)
- return `${date.getMonth() + 1}/${date.getDate()}`
- }
-
- return (
-
- {/* Tab导航 */}
-
- {tabs.map(tab => (
-
{ setActiveTab(tab.key); localStorage.setItem('news_activeTab', tab.key); }}
- style={{
- padding: '10px 20px',
- borderRadius: '8px',
- background: activeTab === tab.key ? '#3b82f6' : 'transparent',
- color: '#fff',
- cursor: 'pointer',
- whiteSpace: 'nowrap',
- fontSize: '14px'
- }}
- >
- {tab.label}
-
- ))}
-
-
- {/* 资讯列表 */}
-
- {showSeekPublish && activeTab === 'seek' && (
-
-
-
- {['龙钞','马钞','蛇钞'].map(item => {
- const isSelected = seekForm.edition ? seekForm.edition.split(',').includes(item) : false
- return (
-
- )
- })}
-
-
-
- setSeekForm({...seekForm, price: e.target.value})} placeholder="请输入价格"
- style={{ width: '100%', padding: '8px', marginTop: '6px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '13px', boxSizing: 'border-box' }} />
-
-
-
- {[0,1,2,3,4,5,6,7,8,9].map(i => {
- const isFixed = i < 2
- const char = i === 0 ? 'J' : (i === 1 ? '0' : (seekForm.features[i - 2] || ''))
- const isDigit = /[0-9]/.test(char)
- const letterColor = isFixed ? '#10b981' : (isDigit ? '#fff' : ({ X: '#94a3b8', A: '#f472b6', B: '#60a5fa', C: '#34d399', D: '#fbbf24', E: '#a78bfa', F: '#fb923c', G: '#22d3ee' }[char] || '#fff'))
- return { if (el) el.dataset.index = i }} type="text" maxLength={1}
- value={char}
- readOnly={isFixed}
- onChange={(e) => {
- if (i < 2) return
- const val = e.target.value.toUpperCase().replace(/[^0-9XABCFG]/g, '')
- const newFeatures = (seekForm.features || '').split('')
- while (newFeatures.length < 8) newFeatures.push('')
- newFeatures[i - 2] = val
- setSeekForm({...seekForm, features: newFeatures.join('')})
- // 自动跳转下一个
- if (val && i < 9) {
- setTimeout(() => {
- const nextInput = document.querySelector(`input[data-index="${i+1}"]`)
- if (nextInput) nextInput.focus()
- }, 50)
- }
- }}
- onKeyDown={(e) => {
- // 按Delete或Backspace且当前为空时,跳回前一个
- if ((e.key === 'Backspace' || e.key === 'Delete') && !char && i > 2) {
- setTimeout(() => {
- const prevInput = document.querySelector(`input[data-index="${i-1}"]`)
- if (prevInput) prevInput.focus()
- }, 50)
- }
- }}
- style={{ width: '32px', height: '36px', textAlign: 'center', padding: '6px', background: '#0f172a', border: '1px solid #334155', borderRadius: '4px', color: letterColor, fontSize: '14px', boxSizing: 'border-box' }} />
- })}
-
-
- 备注说明:X=任意数字 A=非4 B=非47 C=非347 D=非247 E=非2347 F=非23457 G=非123457
-
-
-
- setSeekForm({...seekForm, title: e.target.value})} />
-
-
-
-
- setSeekForm({...seekForm, contact: e.target.value})} placeholder="请输入手机号或微信"
- style={{ width: '100%', padding: '8px', marginTop: '6px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '13px', boxSizing: 'border-box' }} />
-
-
-
-
-
-
- )}
-
- {activeTab === 'seek' ? '🔍 寻配号信息' : '💰 成交行情信息'}
- {activeTab === 'seek' && (
-
-
-
-
-
-
- )}
-
-
- {loading ? (
-
加载中...
- ) : infoList.length === 0 ? (
-
- 暂无{activeTab === 'seek' ? '寻配号' : '成交行情'}信息
-
- ) : (
-
- {infoList.map(item => {
- // 解析正文中的号码特征和联系方式
- const content = item.content || ''
- const featuresMatch = content.match(/号码特征[:\s]*(.+?)(?:\n|$)/)
- const contactMatch = content.match(/联系方式[:\s]*(.+?)(?:\n|$)/)
- const features = featuresMatch ? featuresMatch[1].trim() : ''
- const contact = contactMatch ? contactMatch[1].trim() : ''
- // 去除正文中的价格、号码特征、联系方式
- const cleanContent = content.replace(/价格[:\s]*.+?(\n|$)/g, '').replace(/号码特征[:\s]*.+?(\n|$)/g, '').replace(/联系方式[:\s]*.+?(\n|$)/g, '').trim()
-
- return (
-
- {/* 标题 */}
-
- {item.title}
-
- {/* 创建日期 + 用户名 */}
-
- 📅 {formatDate(item.created_at)} | 👤 {item.user_name || '匿名用户'}
-
- {/* 号码特征 */}
- {features && (
-
-
号码特征:
-
- {/* 如果features已包含J0则不再重复添加 */}
- {features.startsWith('J0') ? (
- features.split('').map((char, i) => {
- const isDigit = /[0-9]/.test(char)
- const letterColor = isDigit ? '#fff' : ({ X: '#94a3b8', A: '#f472b6', B: '#60a5fa', C: '#34d399', D: '#fbbf24', E: '#a78bfa', F: '#fb923c', G: '#22d3ee' }[char] || '#fff')
- return {char}
- })
- ) : (
- <>
- {'J0'.split('').map((char, i) => (
- {char}
- ))}
- {features.split('').map((char, i) => {
- const isDigit = /[0-9]/.test(char)
- const letterColor = isDigit ? '#fff' : ({ X: '#94a3b8', A: '#f472b6', B: '#60a5fa', C: '#34d399', D: '#fbbf24', E: '#a78bfa', F: '#fb923c', G: '#22d3ee' }[char] || '#fff')
- return {char}
- })}
- >
- )}
-
- {/* 动态显示用到的字母说明 */}
- {features.match(/[XABCFG]/) && (
-
- 备注说明:{(() => {
- const used = []
- if (features.includes('X')) used.push('X=任意数字')
- if (features.includes('A')) used.push('A=非4')
- if (features.includes('B')) used.push('B=非47')
- if (features.includes('C')) used.push('C=非347')
- if (features.includes('D')) used.push('D=非247')
- if (features.includes('E')) used.push('E=非2347')
- if (features.includes('F')) used.push('F=非23457')
- if (features.includes('G')) used.push('G=非123457')
- return used.join(' | ')
- })()}
-
- )}
-
- )}
- {/* 配号结果 - 仅寻配号显示 */}
- {activeTab === 'seek' && (
-
- fetchMatchCollections(item.id)}
- >
- 配号结果: {item.matched_count || 0}条藏品匹配成功
-
-
- )}
- {/* 正文 */}
- {cleanContent && (
-
- {cleanContent}
-
- )}
- {/* 联系方式 - 默认隐藏,显示*** */}
- {contact && (
-
-
联系方式
-
- 联系方式:{showContactId === item.id ? contact : '匹配成功后可查看'}
-
-
- {/* 是否匹配按钮 - 仅对已登录用户显示 */}
- {currentUser && (
-
-
- {(item.is_matched === 'matched' || matchedStatus[item.id] === 'matched') ? '已经匹配' : '尚未匹配'}
- {(item.is_matched === 'matched' || matchedStatus[item.id] === 'matched') && }
-
-
- )}
-
- {/* 匹配并联系藏友 / 留言藏友 按钮 - 有匹配藏品才可点击 */}
- {currentUser && (
-
-
-
-
- )}
-
- {/* 留言区域 */}
- {showCommentId === item.id && (
-
- {/* 评论列表 */}
- {comments[item.id] && comments[item.id].length > 0 && (
-
- {comments[item.id].map((c, idx) => (
-
-
{c.user_name || '匿名用户'}
-
{c.content}
-
- ))}
-
- )}
- {/* 评论输入框 */}
-
- setCommentText(e.target.value)}
- placeholder="输入留言..."
- style={{
- flex: 1,
- padding: '8px',
- background: '#1e293b',
- border: '1px solid #334155',
- borderRadius: '4px',
- color: '#fff',
- fontSize: '13px'
- }}
- />
-
-
-
- )}
-
- )}
-
- )
- })}
-
- )}
-
-
- {/* 匹配藏品列表弹窗 */}
- {showMatchList && (
-
-
-
-
匹配藏品清单
-
-
- {matchCollections.length === 0 ? (
-
暂无匹配藏品
- ) : (
-
- {matchCollections.map(c => (
-
{
- setShowMatchList(false)
- // 跳转到藏品列表页并定位到该藏品
- window.location.hash = `#/list?filter=id&value=${c.id}`
- }}
- >
-
- 编号: {c.code || c.id.substring(0,8)}
-
-
- 冠字号: {c.number}
-
-
- 状态: {c.status === 'in_collection' ? '持仓中' : c.status}
-
-
- ))}
-
- )}
-
-
- )}
-
- {/* 我的寻号弹窗 */}
- {showMySeeks && (
-
-
-
-
我发布的寻号
-
-
- {mySeekList.length === 0 ? (
-
暂无发布的寻号
- ) : (
-
- {mySeekList.map(item => (
-
- {editingSeek && editingSeek.id === item.id ? (
- // 编辑模式
-
-
-
-
-
-
-
-
- {['标百', '标十', '单张'].map(t => (
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
- setEditingSeek({...editingSeek, price: e.target.value})}
- placeholder="期望价格"
- style={{ width: '100%', padding: '8px', background: '#1e293b', border: '1px solid #334155', borderRadius: '4px', color: '#fff', marginTop: '4px' }}
- />
-
-
-
-
-
-
- setEditingSeek({...editingSeek, contact: e.target.value})}
- placeholder="手机号或微信"
- style={{ width: '100%', padding: '8px', background: '#1e293b', border: '1px solid #334155', borderRadius: '4px', color: '#fff', marginTop: '4px' }}
- />
-
-
-
-
-
-
- ) : (
- // 显示模式
-
-
{item.title}
-
创建于: {new Date(item.created_at).toLocaleDateString()}
-
配号结果: {item.matched_count || 0}条藏品匹配成功
-
-
- )}
-
- ))}
-
- )}
-
-
- )}
- {customModal.show && (
-
-
-
-
- ℹ️
-
-
{customModal.title}
-
-
- {customModal.content.split('\n').map((line, i) => (
-
- {line.split(':')[0]}
- {line.split(':')[1]}
-
- ))}
-
-
-
-
- )}
-
- )
-}