2026-03-25 10:58:00 +08:00
|
|
|
|
import React, { useState, useEffect } from 'react'
|
|
|
|
|
|
|
2026-03-26 15:32:27 +08:00
|
|
|
|
// 资讯页面 - 展示寻配号和行情信息(所有人可见)
|
2026-03-25 10:58:00 +08:00
|
|
|
|
export default function News() {
|
2026-03-26 23:06:47 +08:00
|
|
|
|
const [activeTab, setActiveTab] = useState('deal')
|
2026-03-27 00:28:24 +08:00
|
|
|
|
const [showSeekPublish, setShowSeekPublish] = useState(false)
|
|
|
|
|
|
const [seekForm, setSeekForm] = useState({
|
2026-03-27 01:20:03 +08:00
|
|
|
|
edition: '龙钞', type: '标百', category: '寻号', price: '', matchType: '模糊', features: 'J0', content: '', title: '', contact: ''
|
2026-03-27 00:28:24 +08:00
|
|
|
|
})
|
2026-03-25 10:58:00 +08:00
|
|
|
|
const [infoList, setInfoList] = useState([])
|
|
|
|
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
const API_BASE = localStorage.getItem('API_BASE') || ''
|
|
|
|
|
|
|
2026-03-26 15:32:27 +08:00
|
|
|
|
// 获取资讯列表
|
2026-03-25 10:58:00 +08:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
fetchInfoList()
|
|
|
|
|
|
}, [activeTab])
|
|
|
|
|
|
|
2026-03-27 00:28:24 +08:00
|
|
|
|
// 自动生成寻号标题
|
|
|
|
|
|
useEffect(() => {
|
2026-03-27 01:20:03 +08:00
|
|
|
|
const title = `「寻号 ${seekForm.edition || '龙钞,马钞,蛇钞'} ${seekForm.type} ${seekForm.features || 'J0'} ${seekForm.matchType}」`
|
2026-03-27 00:28:24 +08:00
|
|
|
|
setSeekForm(prev => ({...prev, title}))
|
|
|
|
|
|
}, [seekForm.edition, seekForm.type, seekForm.features, seekForm.matchType])
|
|
|
|
|
|
|
2026-03-25 10:58:00 +08:00
|
|
|
|
const fetchInfoList = async () => {
|
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
try {
|
|
|
|
|
|
const token = localStorage.getItem('token')
|
2026-03-26 15:32:27 +08:00
|
|
|
|
// 根据tab获取不同类型的数据
|
|
|
|
|
|
const type = activeTab === 'seek' ? 'seek' : 'deal'
|
|
|
|
|
|
const res = await fetch(`${API_BASE}/api/information/list?info_type=${type}`, {
|
|
|
|
|
|
headers: { Authorization: `Bearer ${token}` }
|
2026-03-25 10:58:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
const data = await res.json()
|
|
|
|
|
|
setInfoList(data || [])
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error(e)
|
|
|
|
|
|
}
|
|
|
|
|
|
setLoading(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-27 00:28:24 +08:00
|
|
|
|
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号码特征: ' + seekForm.features : '') + '\n联系方式: ' + seekForm.contact
|
|
|
|
|
|
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' })
|
|
|
|
|
|
})
|
|
|
|
|
|
const data = await res.json()
|
|
|
|
|
|
if (data.id || data.code === 0) {
|
|
|
|
|
|
alert('发布成功!')
|
|
|
|
|
|
setShowSeekPublish(false)
|
2026-03-27 01:20:03 +08:00
|
|
|
|
setSeekForm({ edition: '龙钞', type: '标百', category: '寻号', price: '', matchType: '模糊', features: 'J0', content: '', title: '', contact: '' })
|
2026-03-27 00:28:24 +08:00
|
|
|
|
fetchInfoList()
|
|
|
|
|
|
} else { alert(data.message || '发布失败') }
|
|
|
|
|
|
} catch (e) { alert('发布失败: ' + e.message) }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-26 15:32:27 +08:00
|
|
|
|
// Tab切换
|
|
|
|
|
|
const tabs = [
|
|
|
|
|
|
{ key: 'seek', label: '🔍 寻配号' },
|
|
|
|
|
|
{ key: 'deal', label: '💰 成交行情' }
|
|
|
|
|
|
]
|
2026-03-25 10:58:00 +08:00
|
|
|
|
|
|
|
|
|
|
const formatDate = (dateStr) => {
|
2026-03-26 15:32:27 +08:00
|
|
|
|
if (!dateStr) return '-'
|
|
|
|
|
|
const date = new Date(dateStr)
|
|
|
|
|
|
return `${date.getMonth() + 1}/${date.getDate()}`
|
2026-03-25 10:58:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-26 15:32:27 +08:00
|
|
|
|
<div style={{ minHeight: '100vh', background: '#0f172a', paddingBottom: '60px' }}>
|
|
|
|
|
|
{/* Tab导航 */}
|
|
|
|
|
|
<div style={{ display: 'flex', padding: '12px 16px', background: '#1e293b', gap: '8px', overflowX: 'auto' }}>
|
|
|
|
|
|
{tabs.map(tab => (
|
2026-03-25 10:58:00 +08:00
|
|
|
|
<div
|
|
|
|
|
|
key={tab.key}
|
|
|
|
|
|
onClick={() => setActiveTab(tab.key)}
|
|
|
|
|
|
style={{
|
2026-03-26 15:32:27 +08:00
|
|
|
|
padding: '10px 20px',
|
|
|
|
|
|
borderRadius: '8px',
|
2026-03-25 10:58:00 +08:00
|
|
|
|
background: activeTab === tab.key ? '#3b82f6' : 'transparent',
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
cursor: 'pointer',
|
2026-03-26 15:32:27 +08:00
|
|
|
|
whiteSpace: 'nowrap',
|
2026-03-25 10:58:00 +08:00
|
|
|
|
fontSize: '14px'
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-03-26 15:32:27 +08:00
|
|
|
|
{tab.label}
|
2026-03-25 10:58:00 +08:00
|
|
|
|
</div>
|
2026-03-26 15:32:27 +08:00
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
2026-03-25 10:58:00 +08:00
|
|
|
|
|
|
|
|
|
|
{/* 资讯列表 */}
|
2026-03-26 15:32:27 +08:00
|
|
|
|
<div style={{ padding: '16px' }}>
|
2026-03-27 00:28:24 +08:00
|
|
|
|
{showSeekPublish && activeTab === 'seek' && (
|
|
|
|
|
|
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '16px', marginBottom: '16px' }}>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>版别</label>
|
|
|
|
|
|
<div style={{ display: 'flex', gap: '8px', marginTop: '6px' }}>
|
2026-03-27 01:20:03 +08:00
|
|
|
|
{['龙钞','马钞','蛇钞'].map(item => {
|
|
|
|
|
|
const isSelected = seekForm.edition ? seekForm.edition.split(',').includes(item) : false
|
|
|
|
|
|
return (
|
|
|
|
|
|
<button key={item} onClick={() => {
|
|
|
|
|
|
const eds = seekForm.edition ? seekForm.edition.split(',').filter(x => x) : []
|
|
|
|
|
|
if (isSelected) {
|
|
|
|
|
|
const idx = eds.indexOf(item)
|
|
|
|
|
|
if (idx > -1) eds.splice(idx, 1)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
eds.push(item)
|
|
|
|
|
|
}
|
|
|
|
|
|
setSeekForm({...seekForm, edition: eds.join(',')})
|
|
|
|
|
|
}}
|
|
|
|
|
|
style={{ flex: 1, padding: '8px', borderRadius: '6px', border: isSelected?'2px solid #10b981':'1px solid #334155', background: isSelected?'rgba(16,185,129,0.2)':'#0f172a', color: '#fff', fontSize: '12px' }}>{item}</button>
|
|
|
|
|
|
)
|
|
|
|
|
|
})}
|
2026-03-27 00:28:24 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>类型</label>
|
|
|
|
|
|
<div style={{ display: 'flex', gap: '8px', marginTop: '6px' }}>
|
|
|
|
|
|
{['标百','标十','单张'].map(e => (
|
|
|
|
|
|
<button key={e} onClick={() => setSeekForm({...seekForm, type: e})}
|
|
|
|
|
|
style={{ flex: 1, padding: '8px', borderRadius: '6px', border: seekForm.type===e?'2px solid #10b981':'1px solid #334155', background: seekForm.type===e?'rgba(16,185,129,0.2)':'#0f172a', color: '#fff', fontSize: '12px' }}>{e}</button>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>分类</label>
|
|
|
|
|
|
<div style={{ display: 'flex', gap: '8px', marginTop: '6px' }}>
|
|
|
|
|
|
{['求购','出售','寻号'].map(e => (
|
|
|
|
|
|
<button key={e} onClick={() => setSeekForm({...seekForm, category: e})}
|
|
|
|
|
|
style={{ flex: 1, padding: '8px', borderRadius: '6px', border: seekForm.category===e?'2px solid #10b981':'1px solid #334155', background: seekForm.category===e?'rgba(16,185,129,0.2)':'#0f172a', color: '#fff', fontSize: '12px' }}>{e}</button>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>价格(选填)</label>
|
|
|
|
|
|
<input type="text" value={seekForm.price} onChange={(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' }} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>匹配度</label>
|
|
|
|
|
|
<div style={{ display: 'flex', gap: '8px', marginTop: '6px' }}>
|
|
|
|
|
|
{['精准','模糊'].map(e => (
|
|
|
|
|
|
<button key={e} onClick={() => setSeekForm({...seekForm, matchType: e})}
|
|
|
|
|
|
style={{ flex: 1, padding: '8px', borderRadius: '6px', border: seekForm.matchType===e?'2px solid #10b981':'1px solid #334155', background: seekForm.matchType===e?'rgba(16,185,129,0.2)':'#0f172a', color: '#fff', fontSize: '12px' }}>{e}</button>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-03-27 01:20:03 +08:00
|
|
|
|
<div style={{ marginBottom: '8px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>号码特征(10位)</label>
|
|
|
|
|
|
<div style={{ display: 'flex', gap: '4px', marginTop: '6px', flexWrap: 'wrap' }}>
|
|
|
|
|
|
{[0,1,2,3,4,5,6,7,8,9].map(i => {
|
|
|
|
|
|
const isFixed = i < 2
|
|
|
|
|
|
const placeholder = isFixed ? (i === 0 ? 'J' : '0') : 'X'
|
|
|
|
|
|
return <input key={i} ref={el => { if (el) el.dataset.index = i }} type="text" maxLength={1}
|
|
|
|
|
|
value={isFixed ? placeholder : (seekForm.features?.[i] || '')}
|
|
|
|
|
|
readOnly={isFixed}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
if (i < 2) return
|
|
|
|
|
|
const arr = (seekForm.features || 'J0XXXXXXXX').split('')
|
|
|
|
|
|
arr[i] = e.target.value.toUpperCase()
|
|
|
|
|
|
setSeekForm({...seekForm, features: arr.join('')})
|
|
|
|
|
|
// 输入后自动跳转下一个
|
|
|
|
|
|
if (e.target.value && i < 9) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const nextInput = document.querySelector(`input[data-index="${i+1}"]`)
|
|
|
|
|
|
if (nextInput) nextInput.focus()
|
|
|
|
|
|
}, 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
|
style={{ width: '32px', height: '36px', textAlign: 'center', padding: '6px', background: '#0f172a', border: '1px solid #334155', borderRadius: '4px', color: isFixed ? '#10b981' : '#fff', fontSize: '14px', boxSizing: 'border-box' }} />
|
|
|
|
|
|
})}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ color: '#64748b', fontSize: '10px', marginTop: '6px' }}>X=任意数字 A=非4 B=非47 C=非347 D=非247 E=非2347 F=非12347 F=非23457 G=非123457</div>
|
2026-03-27 00:28:24 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>正文</label>
|
|
|
|
|
|
<textarea value={seekForm.content || ''} onChange={(e) => setSeekForm({...seekForm, content: 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', resize: 'vertical', minHeight: '60px' }} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#ef4444', fontSize: '12px' }}>联系方式 *</label>
|
|
|
|
|
|
<input type="text" value={seekForm.contact || ''} onChange={(e) => 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' }} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ marginBottom: '12px' }}><label style={{ color: '#94a3b8', fontSize: '12px' }}>标题(自动生成)</label>
|
|
|
|
|
|
<input type="text" value={seekForm.title} readOnly
|
2026-03-27 01:20:03 +08:00
|
|
|
|
style={{ width: '100%', padding: '8px', marginTop: '6px', background: '#0f172a', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '13px', boxSizing: 'border-box' }} onChange={(e) => setSeekForm({...seekForm, title: e.target.value})} />
|
2026-03-27 00:28:24 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div style={{ display: 'flex', gap: '12px' }}>
|
|
|
|
|
|
<button onClick={handleSeekPublish} style={{ flex: 1, padding: '12px', background: '#10b981', border: 'none', borderRadius: '8px', color: '#fff', fontSize: '14px', fontWeight: 'bold', cursor: 'pointer' }}>发布寻号</button>
|
|
|
|
|
|
<button onClick={() => setShowSeekPublish(false)} style={{ flex: 1, padding: '12px', background: 'transparent', border: '1px solid #334155', borderRadius: '8px', color: '#94a3b8', fontSize: '14px', cursor: 'pointer' }}>取消</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-03-26 15:32:27 +08:00
|
|
|
|
<h3 style={{ color: '#fff', marginBottom: '16px' }}>
|
|
|
|
|
|
{activeTab === 'seek' ? '🔍 寻配号信息' : '💰 成交行情信息'}
|
2026-03-27 00:28:24 +08:00
|
|
|
|
{activeTab === 'seek' && (
|
|
|
|
|
|
<div style={{ display: 'flex', gap: '8px', marginLeft: 'auto' }}>
|
|
|
|
|
|
<button onClick={() => setShowSeekPublish(!showSeekPublish)} style={{ padding: '6px 12px', background: showSeekPublish ? '#6b7280' : '#10b981', border: 'none', borderRadius: '6px', color: '#fff', fontSize: '12px', cursor: 'pointer' }}>
|
2026-03-27 01:20:03 +08:00
|
|
|
|
'+ 发布寻号'
|
2026-03-27 00:28:24 +08:00
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2026-03-26 15:32:27 +08:00
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
|
|
{loading ? (
|
|
|
|
|
|
<div style={{ color: '#94a3b8', textAlign: 'center', padding: '40px' }}>加载中...</div>
|
|
|
|
|
|
) : infoList.length === 0 ? (
|
|
|
|
|
|
<div style={{ color: '#94a3b8', textAlign: 'center', padding: '40px' }}>
|
|
|
|
|
|
暂无{activeTab === 'seek' ? '寻配号' : '成交行情'}信息
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
|
|
|
|
{infoList.map(item => (
|
|
|
|
|
|
<div key={item.id} style={{ background: '#1e293b', borderRadius: '12px', padding: '16px' }}>
|
|
|
|
|
|
<div style={{ color: '#fbbf24', fontSize: '15px', fontWeight: '500', marginBottom: '8px' }}>
|
|
|
|
|
|
{item.title}
|
2026-03-25 10:58:00 +08:00
|
|
|
|
</div>
|
2026-03-26 15:32:27 +08:00
|
|
|
|
<div style={{ color: '#94a3b8', fontSize: '12px', marginBottom: '8px' }}>
|
|
|
|
|
|
{formatDate(item.created_at)} | {item.info_source || '系统'}
|
2026-03-25 10:58:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
{item.content && (
|
2026-03-26 15:32:27 +08:00
|
|
|
|
<div style={{ color: '#e2e8f0', fontSize: '14px', lineHeight: '1.6' }}>
|
|
|
|
|
|
{item.content}
|
2026-03-25 10:58:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2026-03-26 15:32:27 +08:00
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2026-03-25 10:58:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|