Compare commits
34 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
3ae188da93 | |
|
|
8189903d13 | |
|
|
7db66d5a7b | |
|
|
dd1790b0be | |
|
|
29c2d6f3a9 | |
|
|
bc1485663e | |
|
|
bfd412e726 | |
|
|
5e8c0af131 | |
|
|
a5251a2794 | |
|
|
d18e269193 | |
|
|
f7fd2ee77d | |
|
|
4429d4a2a1 | |
|
|
ce25eee450 | |
|
|
607c5b440f | |
|
|
7736164e5d | |
|
|
a60a54709a | |
|
|
b681eebdb1 | |
|
|
17c4bb2788 | |
|
|
1e68553ff6 | |
|
|
c7ab6bd3da | |
|
|
ed86d4b367 | |
|
|
8e0d93e9a6 | |
|
|
0a59a1f463 | |
|
|
db84cf44fa | |
|
|
ec44d1c5d6 | |
|
|
5190a6ef2e | |
|
|
f20a535470 | |
|
|
4e30a39377 | |
|
|
8ff8a31b2b | |
|
|
c02fdf8269 | |
|
|
407d05e3fd | |
|
|
6c1e927f42 | |
|
|
c813e82232 | |
|
|
c7e8052194 |
|
|
@ -1 +1 @@
|
||||||
VERSION=1.2.79
|
1.2.87
|
||||||
|
|
|
||||||
|
|
@ -452,6 +452,31 @@ def create_information(
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
"""发布资讯"""
|
"""发布资讯"""
|
||||||
|
# 验证并矫正冠字号:必须是J0开头 + 8位数字 = 共10位
|
||||||
|
if data.title:
|
||||||
|
import re
|
||||||
|
# 提取冠字号(J0开头后面跟数字)
|
||||||
|
match = re.search(r'J0(\d+)', data.title)
|
||||||
|
if match:
|
||||||
|
num = match.group(1)
|
||||||
|
# 必须是8位数字
|
||||||
|
if len(num) > 8:
|
||||||
|
# 多于8位:取前8位
|
||||||
|
num = num[:8]
|
||||||
|
elif len(num) < 8:
|
||||||
|
# 少于8位:前面补0
|
||||||
|
num = num.zfill(8)
|
||||||
|
# 重新构建title,确保是J0开头
|
||||||
|
original = match.group(0)
|
||||||
|
data.title = data.title.replace(original, 'J0' + num, 1)
|
||||||
|
else:
|
||||||
|
# 如果不是J0开头,尝试转换
|
||||||
|
other_match = re.search(r'J([1-9]\d{0,8})', data.title)
|
||||||
|
if other_match:
|
||||||
|
# 非J0开头的,尝试补0变成J0开头
|
||||||
|
num = other_match.group(1).zfill(8)[:8]
|
||||||
|
original = other_match.group(0)
|
||||||
|
data.title = data.title.replace(original, 'J0' + num, 1)
|
||||||
# 生成行情编号:日期 + 5位自然数(从00001开始)
|
# 生成行情编号:日期 + 5位自然数(从00001开始)
|
||||||
deal_no = None
|
deal_no = None
|
||||||
if data.info_type == 'deal':
|
if data.info_type == 'deal':
|
||||||
|
|
@ -1277,7 +1302,45 @@ async def batch_parse_deals(text: str = Body(..., embed=True)):
|
||||||
|
|
||||||
# 尝试直接解析
|
# 尝试直接解析
|
||||||
data = json.loads(content.strip())
|
data = json.loads(content.strip())
|
||||||
return {"success": True, "data": data}
|
# 对AI返回的数据进行冠字号矫正 - 确保J0开头+8位数字
|
||||||
|
def normalize_serial_ai(num_str):
|
||||||
|
"""矫正冠字号:J0开头,8位数字,共10位"""
|
||||||
|
if not num_str.startswith('J0'):
|
||||||
|
return None
|
||||||
|
num = num_str[2:] # 去掉J0
|
||||||
|
diff = 8 - len(num)
|
||||||
|
# 位数正好8位,不需要矫正
|
||||||
|
if diff == 0:
|
||||||
|
return num_str
|
||||||
|
elif diff == -1:
|
||||||
|
# 多1位:取前4位+最后4位
|
||||||
|
if len(num) >= 4:
|
||||||
|
result = num[:4] + num[-4:]
|
||||||
|
if len(result) == 8:
|
||||||
|
return 'J0' + result
|
||||||
|
elif diff == 1:
|
||||||
|
# 少1位:J0 + 0 + 数字
|
||||||
|
return 'J0' + '0' + num
|
||||||
|
elif diff == -2:
|
||||||
|
# 多2位:取前4位+最后4位
|
||||||
|
if len(num) >= 4:
|
||||||
|
result = num[:4] + num[-4:]
|
||||||
|
if len(result) == 8:
|
||||||
|
return 'J0' + result
|
||||||
|
elif diff == 2:
|
||||||
|
# 少2位:J0 + 00 + 数字
|
||||||
|
return 'J0' + '00' + num
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 矫正每条记录的冠字号
|
||||||
|
corrected_data = []
|
||||||
|
for item in data:
|
||||||
|
if 'serial' in item:
|
||||||
|
normalized = normalize_serial_ai(item['serial'])
|
||||||
|
if normalized:
|
||||||
|
item['serial'] = normalized
|
||||||
|
corrected_data.append(item)
|
||||||
|
return {"success": True, "data": corrected_data}
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
# 尝试用正则提取
|
# 尝试用正则提取
|
||||||
match = re.search(r'\[.*\]', content, re.DOTALL)
|
match = re.search(r'\[.*\]', content, re.DOTALL)
|
||||||
|
|
@ -1337,15 +1400,46 @@ def parse_deals_locally(text: str, default_packaging: str = '', default_date: st
|
||||||
if not line or 'J0' not in line:
|
if not line or 'J0' not in line:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 提取冠字号 J0 + 8-9位数字
|
# 提取冠字号 J0 + 8-11位数字(可能有多位或少位)
|
||||||
serial_match = re.search(r'J0(\d{8,9})', line)
|
serial_match = re.search(r'J0(\d{7,11})', line)
|
||||||
if not serial_match:
|
if not serial_match:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 冠字号矫正函数 - 确保是J0开头+8位数字
|
||||||
|
def normalize_serial(num_str):
|
||||||
|
"""矫正冠字号:J0开头,8位数字,共10位"""
|
||||||
|
num = num_str
|
||||||
|
diff = 8 - len(num)
|
||||||
|
|
||||||
|
# 位数正好8位,不需要矫正
|
||||||
|
if diff == 0:
|
||||||
|
return 'J0' + num
|
||||||
|
# 位数不对才需要矫正
|
||||||
|
elif diff == -1:
|
||||||
|
# 多1位(9位数字)→ 取前4位+最后4位
|
||||||
|
if len(num) >= 4:
|
||||||
|
result = num[:4] + num[-4:]
|
||||||
|
if len(result) == 8:
|
||||||
|
return 'J0' + result
|
||||||
|
elif diff == 1:
|
||||||
|
# 少1位(7位数字)→ J0 + 0 + 数字
|
||||||
|
return 'J0' + '0' + num
|
||||||
|
elif diff == -2:
|
||||||
|
# 多2位(10位数字)→ 取前4位+最后4位
|
||||||
|
if len(num) >= 4:
|
||||||
|
result = num[:4] + num[-4:]
|
||||||
|
if len(result) == 8:
|
||||||
|
return 'J0' + result
|
||||||
|
elif diff == 2:
|
||||||
|
# 少2位(6位数字)→ J0 + 00 + 数字
|
||||||
|
return 'J0' + '00' + num
|
||||||
|
return None # 无法矫正
|
||||||
|
|
||||||
serial_num = serial_match.group(1)
|
serial_num = serial_match.group(1)
|
||||||
if len(serial_num) == 9:
|
normalized = normalize_serial(serial_num)
|
||||||
serial_num = serial_num[:8]
|
if not normalized:
|
||||||
serial = 'J0' + serial_num
|
continue # 跳过无法矫正的数据
|
||||||
|
serial = normalized
|
||||||
|
|
||||||
# 提取价格 ¥xxx,xxx 或 xxx,xxx(必须在J0之后)
|
# 提取价格 ¥xxx,xxx 或 xxx,xxx(必须在J0之后)
|
||||||
serial_pos = line.find(serial)
|
serial_pos = line.find(serial)
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
VERSION=1.2.81
|
1.2.87
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "jiachenlong-frontend",
|
"name": "jiachenlong-frontend",
|
||||||
"version": "1.2.82",
|
"version": "1.2.87",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "甲辰藏品管理系统 - 移动端前端",
|
"description": "甲辰藏品管理系统 - 移动端前端",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ export default function Add() {
|
||||||
// 根据 URL 参数确定默认标签
|
// 根据 URL 参数确定默认标签
|
||||||
const params = new URLSearchParams(window.location.hash.split('?')[1] || '')
|
const params = new URLSearchParams(window.location.hash.split('?')[1] || '')
|
||||||
const modeParam = params.get('mode')
|
const modeParam = params.get('mode')
|
||||||
const defaultTab = modeParam === 'manual' ? 'manual' : 'ai'
|
const defaultTab = modeParam === 'manual' ? 'manual' : modeParam === 'deal' ? 'deal' : 'ai'
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState(defaultTab) // ai, manual, deal
|
const [activeTab, setActiveTab] = useState(defaultTab) // ai, manual, deal
|
||||||
const [form, setForm] = useState(getDefaultForm())
|
const [form, setForm] = useState(getDefaultForm())
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,46 @@ export default function Home() {
|
||||||
const [seekStats, setSeekStats] = useState({ seekCount: 0, matchedCount: 0 })
|
const [seekStats, setSeekStats] = useState({ seekCount: 0, matchedCount: 0 })
|
||||||
const [recentPosts, setRecentPosts] = useState([])
|
const [recentPosts, setRecentPosts] = useState([])
|
||||||
const [dragonStats, setDragonStats] = useState({})
|
const [dragonStats, setDragonStats] = useState({})
|
||||||
|
const [dealStats, setDealStats] = useState({ total: 0, items: [] })
|
||||||
|
const [dealVersion, setDealVersion] = useState('龙钞')
|
||||||
|
const [dealDetailItems, setDealDetailItems] = useState(null)
|
||||||
const currentPath = window.location.hash.slice(1) || '/'
|
const currentPath = window.location.hash.slice(1) || '/'
|
||||||
|
|
||||||
|
// 成交行情数据处理
|
||||||
|
const categoryOrder = ['带4号', '带7号', '永恒号', '天马号', '天马王', '金山号', '金山王', '钻石号', '朦胧号', '朦胧王', '金马号', '金马王', '倒置号', '圆圆号']
|
||||||
|
|
||||||
|
const normalizeCat = (c) => {
|
||||||
|
if (c === '通货') return '带4号'
|
||||||
|
if (c === '无4') return '带7号'
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcDealAvg = (pkg, cat) => {
|
||||||
|
const items = dealStats.items.filter(item => {
|
||||||
|
const content = item.content || ''
|
||||||
|
const p = content.includes('包装:') ? content.split('包装:')[1].split('\n')[0].trim() : (item.packaging || '')
|
||||||
|
let c = content.includes('分类:') ? content.split('分类:')[1].split('\n')[0].trim() : (item.category || '')
|
||||||
|
c = normalizeCat(c)
|
||||||
|
return p === pkg && c === cat
|
||||||
|
})
|
||||||
|
if (items.length === 0) return null
|
||||||
|
const sum = items.reduce((a, b) => a + (b.deal_price || 0), 0)
|
||||||
|
return { avg: Math.round(sum / items.length), count: items.length, items }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取今日成交数据
|
||||||
|
useEffect(() => {
|
||||||
|
// 获取最新成交数据
|
||||||
|
fetch(`/api/information/list?info_type=deal&page_size=500`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
setDealStats({ total: data.length, items: data })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('token')
|
const token = localStorage.getItem('token')
|
||||||
const userData = localStorage.getItem('user')
|
const userData = localStorage.getItem('user')
|
||||||
|
|
@ -73,6 +111,7 @@ export default function Home() {
|
||||||
setDragonStats(data || {})
|
setDragonStats(data || {})
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
|
|
||||||
|
// 获取最新一尘帖子
|
||||||
fetch('/api/yichens/posts?limit=20&offset=0').then(res => res.json()).then(data => {
|
fetch('/api/yichens/posts?limit=20&offset=0').then(res => res.json()).then(data => {
|
||||||
setRecentPosts(data.posts || data || [])
|
setRecentPosts(data.posts || data || [])
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
|
|
@ -174,32 +213,58 @@ export default function Home() {
|
||||||
<div onClick={() => window.location.hash = '#/add?mode=ocr'} style={{
|
<div onClick={() => window.location.hash = '#/add?mode=ocr'} style={{
|
||||||
background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',
|
background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',
|
||||||
borderRadius: '12px',
|
borderRadius: '12px',
|
||||||
padding: '16px',
|
padding: '12px 16px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '12px'
|
justifyContent: 'center',
|
||||||
|
textAlign: 'center'
|
||||||
}}>
|
}}>
|
||||||
<div style={{ fontSize: '24px' }}>📷</div>
|
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>藏品录入</div>
|
||||||
<div>
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>AI识别添加</div>
|
||||||
<div style={{ color: '#fff', fontSize: '15px', fontWeight: '600' }}>AI识别</div>
|
</div>
|
||||||
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '11px' }}>拍照识别藏品</div>
|
<div onClick={() => window.location.hash = '#/add?mode=deal'} style={{
|
||||||
</div>
|
background: 'linear-gradient(135deg, #ec4899 0%, #db2777 100%)',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '12px 16px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}>
|
||||||
|
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>行情录入</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>录入成交行情</div>
|
||||||
|
</div>
|
||||||
|
<div onClick={() => window.location.hash = '#/news?type=seek'} style={{
|
||||||
|
background: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)',
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '12px 16px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}>
|
||||||
|
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>发布寻号</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>发布寻配需求</div>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={() => window.location.hash = '#/add?mode=manual'} style={{
|
<div onClick={() => window.location.hash = '#/add?mode=manual'} style={{
|
||||||
background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
|
background: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)',
|
||||||
borderRadius: '12px',
|
borderRadius: '12px',
|
||||||
padding: '16px',
|
padding: '12px 16px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '12px'
|
justifyContent: 'center',
|
||||||
|
textAlign: 'center'
|
||||||
}}>
|
}}>
|
||||||
<div style={{ fontSize: '24px' }}>✏️</div>
|
<div style={{ color: '#fff', fontSize: '14px', fontWeight: '600', whiteSpace: 'nowrap' }}>发布藏品</div>
|
||||||
<div>
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '10px', marginTop: '2px' }}>手动发布</div>
|
||||||
<div style={{ color: '#fff', fontSize: '15px', fontWeight: '600' }}>手动录入</div>
|
|
||||||
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '11px' }}>添加新藏品</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -232,6 +297,60 @@ export default function Home() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 今日成交数据统计 */}
|
||||||
|
{dealStats.total > 0 && (
|
||||||
|
<div style={{ marginBottom: '20px' }}>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>📈 最新成交信息统计(均价)</div>
|
||||||
|
<div style={{ background: '#1e293b', borderRadius: '12px', padding: '16px' }}>
|
||||||
|
<div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap', marginBottom: '12px' }}>
|
||||||
|
{['龙钞', '马钞', '蛇钞', '其他'].map(v => (
|
||||||
|
<button key={v} onClick={() => setDealVersion(v)}
|
||||||
|
style={{ padding: '6px 16px', borderRadius: '8px', border: 'none', cursor: 'pointer',
|
||||||
|
background: dealVersion === v ? '#3b82f6' : 'rgba(255,255,255,0.1)',
|
||||||
|
color: '#fff', fontSize: '13px', fontWeight: dealVersion === v ? '600' : '400' }}>
|
||||||
|
{v}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div style={{ overflowX: 'auto' }}>
|
||||||
|
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '13px' }}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style={{ padding: '8px', textAlign: 'left', color: '#94a3b8', borderBottom: '1px solid rgba(255,255,255,0.1)' }}></th>
|
||||||
|
{['标百', '标十', '单张'].map(p => (
|
||||||
|
<th key={p} style={{ padding: '8px', textAlign: 'center', color: '#94a3b8', borderBottom: '1px solid rgba(255,255,255,0.1)' }}>{p}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{categoryOrder.map(cat => {
|
||||||
|
const rowData = ['标百', '标十', '单张'].map(pkg => calcDealAvg(pkg, cat))
|
||||||
|
const hasData = rowData.some(d => d !== null)
|
||||||
|
if (!hasData) return null
|
||||||
|
return (
|
||||||
|
<tr key={cat}>
|
||||||
|
<td style={{ padding: '8px', color: '#fbbf24', fontWeight: '500', borderBottom: '1px solid rgba(255,255,255,0.05)', whiteSpace: 'nowrap' }}>{cat}</td>
|
||||||
|
{rowData.map((d, i) => (
|
||||||
|
<td key={i} style={{ padding: '8px', textAlign: 'center', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
|
{d ? (
|
||||||
|
<div style={{ color: '#22c55e', fontWeight: '600', cursor: 'pointer' }}
|
||||||
|
onClick={() => setDealDetailItems(d.items)}>
|
||||||
|
¥{d.avg.toLocaleString()}
|
||||||
|
<span style={{ color: '#64748b', fontSize: '11px', marginLeft: '4px' }}>({d.count})</span>
|
||||||
|
</div>
|
||||||
|
) : <span style={{ color: '#475569' }}>-</span>}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 一尘今日数据 */}
|
{/* 一尘今日数据 */}
|
||||||
<div style={{ marginBottom: '20px' }}>
|
<div style={{ marginBottom: '20px' }}>
|
||||||
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>📊 一尘今日连体纪念钞数据</div>
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>📊 一尘今日连体纪念钞数据</div>
|
||||||
|
|
@ -389,6 +508,55 @@ export default function Home() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: '70px' }}></div>
|
<div style={{ height: '70px' }}></div>
|
||||||
|
|
||||||
|
{/* 成交详情弹窗 */}
|
||||||
|
{dealDetailItems && (
|
||||||
|
<div style={{
|
||||||
|
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
|
||||||
|
background: 'rgba(0,0,0,0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
zIndex: 1000, padding: '20px'
|
||||||
|
}} onClick={() => setDealDetailItems(null)}>
|
||||||
|
<div style={{
|
||||||
|
background: '#1e293b', borderRadius: '12px', padding: '20px', maxWidth: '600px', width: '100%', maxHeight: '80vh', overflow: 'auto'
|
||||||
|
}} onClick={e => e.stopPropagation()}>
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '16px' }}>
|
||||||
|
<div style={{ color: '#fff', fontSize: '16px', fontWeight: '600' }}>成交详情列表</div>
|
||||||
|
<button onClick={() => setDealDetailItems(null)}
|
||||||
|
style={{ background: 'none', border: 'none', color: '#94a3b8', fontSize: '20px', cursor: 'pointer' }}>✕</button>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
|
{([...dealDetailItems].sort((a, b) => (a.deal_price || 0) - (b.deal_price || 0))).map((item, idx) => {
|
||||||
|
const content = item.content || ''
|
||||||
|
const grade = content.includes('评级:') ? content.split('评级:')[1].split('\n')[0].trim() : (item.grading_score || '')
|
||||||
|
const sizeMatch = content.match(/大小号:\s*(.+?)(?:\n|$)/)
|
||||||
|
const size = sizeMatch ? sizeMatch[1].trim() : ''
|
||||||
|
const platformMatch = content.match(/平台:\s*(.+?)(?:\n|$)/)
|
||||||
|
const platform = platformMatch ? platformMatch[1].trim() : '-'
|
||||||
|
return (
|
||||||
|
<div key={idx} style={{
|
||||||
|
background: 'rgba(255,255,255,0.05)', borderRadius: '8px', padding: '12px',
|
||||||
|
display: 'flex', justifyContent: 'space-between', alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<div>
|
||||||
|
<div style={{ color: '#fbbf24', fontWeight: '600', marginBottom: '4px' }}>
|
||||||
|
{(item.title || '').split('-')[0]}
|
||||||
|
{size && <span style={{ color: '#94a3b8', marginLeft: '8px' }}>| {size}</span>}
|
||||||
|
{grade && <span style={{ color: '#06b6d4', marginLeft: '8px' }}>| {grade}</span>}
|
||||||
|
</div>
|
||||||
|
<div style={{ color: '#94a3b8', fontSize: '12px' }}>
|
||||||
|
{item.deal_date} | {item.category} | {item.packaging} | {platform}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ color: '#22c55e', fontSize: '18px', fontWeight: '700' }}>
|
||||||
|
¥{item.deal_price?.toLocaleString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,12 @@ const getStoredUserId = () => {
|
||||||
|
|
||||||
// 资讯页面 - 展示寻配号和行情信息(所有人可见)
|
// 资讯页面 - 展示寻配号和行情信息(所有人可见)
|
||||||
export default function News() {
|
export default function News() {
|
||||||
const [activeTab, setActiveTab] = useState(localStorage.getItem('news_activeTab') || 'yichen')
|
// 根据URL参数确定默认tab
|
||||||
|
const params = new URLSearchParams(window.location.hash.split('?')[1] || '')
|
||||||
|
const typeParam = params.get('type')
|
||||||
|
const defaultTab = typeParam === 'seek' ? 'seek' : typeParam === 'deal' ? 'deal' : localStorage.getItem('news_activeTab') || 'yichen'
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState(defaultTab)
|
||||||
const [showSeekPublish, setShowSeekPublish] = useState(false)
|
const [showSeekPublish, setShowSeekPublish] = useState(false)
|
||||||
const [showMySeeks, setShowMySeeks] = useState(false) // 我的寻号
|
const [showMySeeks, setShowMySeeks] = useState(false) // 我的寻号
|
||||||
const [showContactId, setShowContactId] = useState(null) // 显示联系方式的寻号ID
|
const [showContactId, setShowContactId] = useState(null) // 显示联系方式的寻号ID
|
||||||
|
|
@ -66,7 +71,7 @@ export default function News() {
|
||||||
// 获取资讯列表
|
// 获取资讯列表
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchInfoList()
|
fetchInfoList()
|
||||||
}, [activeTab, dealDate])
|
}, [activeTab])
|
||||||
|
|
||||||
// 自动生成寻号标题
|
// 自动生成寻号标题
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -82,14 +87,7 @@ export default function News() {
|
||||||
const headers = token ? { Authorization: `Bearer ${token}` } : {}
|
const headers = token ? { Authorization: `Bearer ${token}` } : {}
|
||||||
|
|
||||||
// 构建URL参数
|
// 构建URL参数
|
||||||
let url = `${API_BASE}/api/information/list?info_type=${type}`
|
let url = `${API_BASE}/api/information/list?info_type=${type}&page_size=500`
|
||||||
|
|
||||||
// 如果是成交行情tab,获取当天所有数据(不分页)
|
|
||||||
if (activeTab === 'deal' && dealDate) {
|
|
||||||
url += `&deal_date=${dealDate}&page_size=500`
|
|
||||||
} else {
|
|
||||||
url += `&page=${currentPage}&page_size=50`
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(url, { headers })
|
const res = await fetch(url, { headers })
|
||||||
// 从响应头获取总页数
|
// 从响应头获取总页数
|
||||||
|
|
@ -495,7 +493,7 @@ export default function News() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h3 style={{ color: '#fff', marginBottom: '16px' }}>
|
<h3 style={{ color: '#fff', marginBottom: '16px' }}>
|
||||||
{activeTab === 'seek' ? '寻配号信息' : activeTab === 'deal' ? `成交行情信息 (${dealDate})` : '一尘看板'}
|
{activeTab === 'seek' ? '寻配号信息' : activeTab === 'deal' ? '最新成交信息统计(均价)' : '一尘看板'}
|
||||||
{activeTab === 'seek' && (
|
{activeTab === 'seek' && (
|
||||||
<div style={{ display: 'flex', gap: '8px', marginLeft: 'auto' }}>
|
<div style={{ display: 'flex', gap: '8px', marginLeft: 'auto' }}>
|
||||||
<button onClick={() => { setViewMode('all'); fetchInfoList(); }} style={{ padding: '6px 12px', background: viewMode==='all'?'#3b82f6':'#1e293b', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '12px' }}>全部</button>
|
<button onClick={() => { setViewMode('all'); fetchInfoList(); }} style={{ padding: '6px 12px', background: viewMode==='all'?'#3b82f6':'#1e293b', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '12px' }}>全部</button>
|
||||||
|
|
@ -504,12 +502,6 @@ export default function News() {
|
||||||
<button onClick={() => setShowSeekPublish(!showSeekPublish)} style={{ padding: '6px 12px', background: showSeekPublish ? '#6b7280' : '#10b981', border: 'none', borderRadius: '6px', color: '#fff', fontSize: '12px', cursor: 'pointer' }}>发布寻号</button>
|
<button onClick={() => setShowSeekPublish(!showSeekPublish)} style={{ padding: '6px 12px', background: showSeekPublish ? '#6b7280' : '#10b981', border: 'none', borderRadius: '6px', color: '#fff', fontSize: '12px', cursor: 'pointer' }}>发布寻号</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{activeTab === 'deal' && (
|
|
||||||
<div style={{ display: 'flex', gap: '8px', marginLeft: 'auto' }}>
|
|
||||||
<input type="date" value={dealDate} onChange={e => setDealDate(e.target.value)}
|
|
||||||
style={{ padding: '6px 12px', background: '#1e293b', border: '1px solid #334155', borderRadius: '6px', color: '#fff', fontSize: '12px' }} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* 一尘看板独立渲染 */}
|
{/* 一尘看板独立渲染 */}
|
||||||
|
|
@ -608,7 +600,7 @@ export default function News() {
|
||||||
if (!hasData) return null
|
if (!hasData) return null
|
||||||
return (
|
return (
|
||||||
<tr key={cat}>
|
<tr key={cat}>
|
||||||
<td style={{ padding: '8px', color: '#fbbf24', fontWeight: '500', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>{cat}</td>
|
<td style={{ padding: '8px', color: '#fbbf24', fontWeight: '500', borderBottom: '1px solid rgba(255,255,255,0.05)', whiteSpace: 'nowrap' }}>{cat}</td>
|
||||||
{rowData.map((d, i) => (
|
{rowData.map((d, i) => (
|
||||||
<td key={i} style={{ padding: '8px', textAlign: 'center', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
|
<td key={i} style={{ padding: '8px', textAlign: 'center', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
{d ? (
|
{d ? (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue