diff --git a/backend/app/models/models.py b/backend/app/models/models.py
index 049c25f..b4ba6e7 100644
--- a/backend/app/models/models.py
+++ b/backend/app/models/models.py
@@ -182,6 +182,12 @@ class Information(Base):
# 状态: active-有效, closed-已关闭, expired-已过期
status = Column(String(20), default="active", index=True)
+ # 匹配状态: pending-尚未匹配, matched-已经匹配
+ is_matched = Column(String(20), default="pending", index=True)
+
+ # 匹配的用户ID(当用户愿意交换联系方式时)
+ matched_user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="SET NULL"), nullable=True)
+
# 浏览/联系次数
view_count = Column(Integer, default=0)
contact_count = Column(Integer, default=0)
@@ -193,6 +199,31 @@ class Information(Base):
collection = relationship("Collection")
+# 资讯评论/留言
+class InformationComment(Base):
+ __tablename__ = "information_comments"
+
+ id = Column(String(36), primary_key=True, default=generate_uuid)
+ information_id = Column(String(36), ForeignKey("information.id", ondelete="CASCADE"), nullable=False, index=True)
+ user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
+ content = Column(Text, nullable=False)
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
+
+ user = relationship("User")
+
+
+# 资讯联系方式查看记录
+class InformationContactView(Base):
+ __tablename__ = "information_contact_views"
+
+ id = Column(String(36), primary_key=True, default=generate_uuid)
+ information_id = Column(String(36), ForeignKey("information.id", ondelete="CASCADE"), nullable=False, index=True)
+ viewer_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
+
+ viewer = relationship("User")
+
+
# 资讯关联用户 (收藏/点赞)
class InformationLike(Base):
__tablename__ = "information_likes"
diff --git a/backend/app/routers/information.py b/backend/app/routers/information.py
index ef65024..94a3d4f 100644
--- a/backend/app/routers/information.py
+++ b/backend/app/routers/information.py
@@ -681,3 +681,82 @@ def get_my_information(
"created_at": i.created_at.isoformat() if i.created_at else None
} for i in infos]
}
+
+
+# ============ 匹配寻号 ============
+class MatchSeekRequest(BaseModel):
+ info_id: str
+ collection_id: Optional[str] = None
+
+
+@router.post("/seek/match-confirm")
+def match_seek(
+ request: MatchSeekRequest,
+ current_user: User = Depends(get_current_user),
+ db: Session = Depends(get_db)
+):
+ """确认匹配寻号 - 用户愿意交换联系方式给发布者"""
+ info = db.query(Information).filter(
+ Information.id == request.info_id,
+ Information.info_type == "seek",
+ Information.status == "active"
+ ).first()
+
+ if not info:
+ raise HTTPException(status_code=404, detail="寻配号信息不存在")
+
+ # 更新匹配状态
+ info.is_matched = "matched"
+ info.matched_user_id = current_user.f99_90_id
+
+ # 更新发布寻号者的内容,显示有藏品被匹配
+ original_content = info.content or ""
+ # 添加匹配信息:藏品被XX藏友匹配,联系方式为:xxx
+ match_info = f"\n藏品被{current_user.f01_01_name}匹配成功!联系方式:{request.collection_id or '已交换'}"
+ info.content = original_content + match_info
+
+ db.commit()
+
+ return {"message": "匹配成功,已通知发布者", "is_matched": "matched"}
+
+
+# ============ 添加留言 ============
+class CommentRequest(BaseModel):
+ information_id: str
+ content: str
+
+
+@router.post("/comment")
+def add_comment(
+ request: CommentRequest,
+ current_user: User = Depends(get_current_user),
+ db: Session = Depends(get_db)
+):
+ """添加留言"""
+ info = db.query(Information).filter(
+ Information.id == request.information_id
+ ).first()
+
+ if not info:
+ raise HTTPException(status_code=404, detail="资讯不存在")
+
+ # 创建留言
+ from app.models.models import InformationComment
+ comment = InformationComment(
+ information_id=request.information_id,
+ user_id=current_user.f99_90_id,
+ content=request.content
+ )
+ db.add(comment)
+ db.commit()
+
+ return {
+ "message": "留言成功",
+ "comment": {
+ "id": comment.id,
+ "content": comment.content,
+ "user_name": current_user.f01_01_name,
+ "user_avatar": current_user.avatar,
+ "created_at": comment.created_at
+ }
+ }
diff --git a/frontend/index.html b/frontend/index.html
index 9190919..7cebb1a 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -4,7 +4,7 @@
-
甲辰收藏 v1.2.18
+ 甲辰收藏 v1.2.17
diff --git a/frontend/src/pages/News.jsx b/frontend/src/pages/News.jsx
index 231066a..2628b9a 100644
--- a/frontend/src/pages/News.jsx
+++ b/frontend/src/pages/News.jsx
@@ -5,6 +5,11 @@ export default function News() {
const [activeTab, setActiveTab] = useState('deal')
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 [seekForm, setSeekForm] = useState({
@@ -14,6 +19,7 @@ export default function News() {
const [loading, setLoading] = useState(false)
const API_BASE = localStorage.getItem('API_BASE') || ''
+ const currentUser = localStorage.getItem('token') ? { id: 'logged_in' } : null
// 获取资讯列表
useEffect(() => {
@@ -22,9 +28,9 @@ export default function News() {
// 自动生成寻号标题
useEffect(() => {
- const title = `「寻号 ${seekForm.edition || '龙钞,马钞,蛇钞'} ${seekForm.type || '不限'} J0${seekForm.features || 'XXXXXXXX'} ${seekForm.matchType}」`
+ const title = `「寻号 ${seekForm.edition || '龙钞,马钞,蛇钞'} J0${seekForm.features || 'XXXXXXXX'}」`
setSeekForm(prev => ({...prev, title}))
- }, [seekForm.edition, seekForm.type, seekForm.features, seekForm.matchType])
+ }, [seekForm.edition, seekForm.features])
const fetchInfoList = async () => {
setLoading(true)
@@ -46,6 +52,63 @@ export default function News() {
}
// 获取匹配藏品列表
+
+
+ // 获取寻号评论
+ const fetchComments = async (infoId) => {
+ if (comments[infoId]) return; // 已加载则跳过
+ 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 === '留言成功') {
+ setCommentText('')
+ setComments(prev => ({...prev, [infoId]: [...(prev[infoId] || []), data.comment]}))
+ alert('留言成功!')
+ }
+ } catch (e) {
+ alert('留言失败: ' + e.message)
+ }
+ }
+
+ // 匹配并联系藏友
+ 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) // 显示联系方式
+ alert('匹配成功!')
+ fetchInfoList() // 刷新列表
+ }
+ } catch (e) {
+ alert('操作失败: ' + e.message)
+ }
+ }
const fetchMatchCollections = async (infoId) => {
const token = localStorage.getItem('token')
if (!token) { alert('请先登录'); return }
@@ -191,34 +254,10 @@ export default function News() {
})}
-
-
- {['标百','标十','单张'].map(e => (
-
- ))}
-
-
-
-
- {['求购','出售','寻号'].map(e => (
-
- ))}
-
-
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' }} />
-
-
- {['精准','模糊'].map(e => (
-
- ))}
-
-
{[0,1,2,3,4,5,6,7,8,9].map(i => {
@@ -260,6 +299,10 @@ export default function News() {
备注说明:X=任意数字 A=非4 B=非47 C=非347 D=非247 E=非2347 F=非23457 G=非123457
+
+ setSeekForm({...seekForm, title: e.target.value})} />
+
-
- setSeekForm({...seekForm, title: e.target.value})} />
-
@@ -381,10 +420,124 @@ export default function News() {
{cleanContent}
)}
- {/* 联系方式 */}
+ {/* 联系方式 - 默认隐藏,显示*** */}
{contact && (
-
- 联系方式:{contact}
+
+
联系方式
+
+ 联系方式:{showContactId === item.id ? contact : '***'}
+
+
+ {/* 是否匹配按钮 - 仅对已登录用户显示 */}
+ {currentUser && (
+
+
+ {(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'
+ }}
+ />
+
+
+
+ )}
)}
diff --git a/frontend/src/pages/News.jsx.backup_20260328 b/frontend/src/pages/News.jsx.backup_20260328
new file mode 100644
index 0000000..231066a
--- /dev/null
+++ b/frontend/src/pages/News.jsx.backup_20260328
@@ -0,0 +1,554 @@
+import React, { useState, useEffect } from 'react'
+
+// 资讯页面 - 展示寻配号和行情信息(所有人可见)
+export default function News() {
+ const [activeTab, setActiveTab] = useState('deal')
+ const [showSeekPublish, setShowSeekPublish] = useState(false)
+ const [showMySeeks, setShowMySeeks] = useState(false) // 我的寻号
+ const [showMatchList, setShowMatchList] = useState(false)
+ const [matchCollections, setMatchCollections] = useState([])
+ const [seekForm, setSeekForm] = useState({
+ edition: '龙钞', type: '', category: '寻号', price: '', matchType: '模糊', features: '', content: '', title: '', contact: ''
+ })
+ const [infoList, setInfoList] = useState([])
+ const [loading, setLoading] = useState(false)
+
+ const API_BASE = localStorage.getItem('API_BASE') || ''
+
+ // 获取资讯列表
+ useEffect(() => {
+ fetchInfoList()
+ }, [activeTab])
+
+ // 自动生成寻号标题
+ useEffect(() => {
+ const title = `「寻号 ${seekForm.edition || '龙钞,马钞,蛇钞'} ${seekForm.type || '不限'} J0${seekForm.features || 'XXXXXXXX'} ${seekForm.matchType}」`
+ setSeekForm(prev => ({...prev, title}))
+ }, [seekForm.edition, seekForm.type, seekForm.features, seekForm.matchType])
+
+ 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 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)
+ }
+ }
+
+ // 获取我的寻号列表
+ const [mySeekList, setMySeekList] = 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') : []
+ setMySeekList(seeks)
+ } 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 () => {
+ if (!seekForm.contact) { alert('请填写联系方式'); return }
+ // 类型改为非必选
+ try {
+ const token = localStorage.getItem('token')
+ const content = (seekForm.content ? seekForm.content + '\n' : '') + (seekForm.price ? `价格: ${seekForm.price}` : '') + (seekForm.features ? '\n号码特征: J0' + seekForm.features : '') + '\n联系方式: ' + seekForm.contact
+ // 从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: '' })
+ 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)}
+ 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 (
+
+ )
+ })}
+
+
+
+
+ {['标百','标十','单张'].map(e => (
+
+ ))}
+
+
+
+
+ {['求购','出售','寻号'].map(e => (
+
+ ))}
+
+
+
+ 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' }} />
+
+
+
+ {['精准','模糊'].map(e => (
+
+ ))}
+
+
+
+
+ {[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, 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' }} />
+
+
+ setSeekForm({...seekForm, title: e.target.value})} />
+
+
+
+
+
+
+ )}
+
+ {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 && (
+
+ 联系方式:{contact}
+
+ )}
+
+ )
+ })}
+
+ )}
+
+
+ {/* 匹配藏品列表弹窗 */}
+ {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}条藏品匹配成功
+
+
+ )}
+
+ ))}
+
+ )}
+
+
+ )}
+
+ )
+}
diff --git a/frontend/src/pages/News_original_backup.jsx b/frontend/src/pages/News_original_backup.jsx
new file mode 100644
index 0000000..bef5194
--- /dev/null
+++ b/frontend/src/pages/News_original_backup.jsx
@@ -0,0 +1,31 @@
+import React, { useState, useEffect } from 'react'
+
+// 资讯页面 - 展示寻配号和行情信息(所有人可见)
+export default function News() {
+ const [activeTab, setActiveTab] = useState('deal')
+ const [showSeekPublish, setShowSeekPublish] = useState(false)
+ const [showMySeeks, setShowMySeeks] = useState(false)
+ const [showMatchList, setShowMatchList] = useState(false)
+ const [matchCollections, setMatchCollections] = useState([])
+ const [listFilter, setListFilter] = useState('')
+ const [currentUserId, setCurrentUserId] = useState('')
+ const [matchedItems, setMatchedItems] = useState(() => {
+ try { return JSON.parse(localStorage.getItem('matchedSeekItems') || '{}') } catch { return {} }
+ })
+ const [showCommentId, setShowCommentId] = useState(null)
+ const [commentText, setCommentText] = useState('')
+ const [comments, setComments] = useState({})
+
+ useEffect(() => {
+ const token = localStorage.getItem('token')
+ if (token) { try { const payload = JSON.parse(atob(token.split('.')[1])); setCurrentUserId(payload.sub) } catch {} }
+ }, [])
+
+ const API_BASE = 'http://47.103.9.192:3000'
+
+ return (
+
+
News Page TEST
+
+ )
+}
diff --git a/frontend/src/test.js b/frontend/src/test.js
new file mode 100644
index 0000000..2703c10
--- /dev/null
+++ b/frontend/src/test.js
@@ -0,0 +1 @@
+export const TEST_CONST = "test_value_12345";
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index e5d8607..c167f55 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -53,6 +53,5 @@ export default defineConfig({
}
},
// 禁用缓存
- manifest: true
}
})