diff --git a/VERSION b/VERSION
index daba2a9..7c48e17 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.70
+VERSION=1.2.72
diff --git a/backend/VERSION b/backend/VERSION
index daba2a9..7c48e17 100644
--- a/backend/VERSION
+++ b/backend/VERSION
@@ -1 +1 @@
-1.2.70
+VERSION=1.2.72
diff --git a/config/VERSION b/config/VERSION
index fb1488c..7c48e17 100644
--- a/config/VERSION
+++ b/config/VERSION
@@ -1 +1 @@
-VERSION=1.2.70
+VERSION=1.2.72
diff --git a/frontend/index.html b/frontend/index.html
index 6525875..1894deb 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -4,7 +4,7 @@
-
加载中...
) : infoList.length === 0 ? (
- {activeTab === 'yichen' ?
: infoList.map(item => {
+ {infoList.map(item => {
// 解析正文中的号码特征和联系方式
const content = item.content || ''
const featuresMatch = content.match(/号码特征[:\s]*(.+?)(?:\n|$)/)
diff --git a/frontend/src/pages/News_YichensBoard.jsx b/frontend/src/pages/News_YichensBoard.jsx
new file mode 100644
index 0000000..7af8ba1
--- /dev/null
+++ b/frontend/src/pages/News_YichensBoard.jsx
@@ -0,0 +1,218 @@
+import React, { useState, useEffect } from 'react'
+
+export default function YichensBoard() {
+ const [todayStats, setTodayStats] = useState({ total: 0, deals: 0, wants: 0, others: 0 })
+ const [todayCategory, setTodayCategory] = useState([])
+ const [posts, setPosts] = useState([])
+ const [loading, setLoading] = useState(false)
+ const [expandedPosts, setExpandedPosts] = useState({})
+ const [postTypeFilter, setPostTypeFilter] = useState('all')
+ const [categoryFilter, setCategoryFilter] = useState('')
+ const [page, setPage] = useState(1)
+ const [totalPosts, setTotalPosts] = useState(0)
+ const [searchKeyword, setSearchKeyword] = useState('')
+ const API_BASE = localStorage.getItem('API_BASE') || ''
+
+ const today = new Date()
+ const dateStr = today.getFullYear() + '/' + (today.getMonth() + 1) + '/' + today.getDate()
+
+ useEffect(() => { fetchTodayStats(); fetchPosts(1, '') }, [])
+
+ const fetchTodayStats = async () => {
+ try {
+ const res = await fetch(API_BASE + '/api/yichens/stats/today')
+ setTodayStats(await res.json())
+ } catch(e) { console.error(e) }
+ }
+
+ const fetchTodayCategory = async () => {
+ try {
+ const res = await fetch(API_BASE + '/api/yichens/stats/today-category')
+ setTodayCategory(await res.json())
+ } catch(e) { console.error(e) }
+ }
+
+ const fetchPosts = async (p, cat) => {
+ setLoading(true)
+ const currentPage = p !== undefined ? p : page
+ const currentCat = cat !== undefined ? cat : categoryFilter
+
+ let url = API_BASE + '/api/yichens/posts?limit=390&offset=' + ((currentPage - 1) * 390)
+ if (postTypeFilter === 'deal') url += '&post_type=deal'
+ else if (postTypeFilter === 'want') url += '&post_type=want'
+ else if (postTypeFilter === 'other') url += '&post_type=normal'
+ try {
+ const res = await fetch(url)
+ let data = await res.json() || []
+ if (currentCat) {
+ if (currentCat === '龙') {
+ data = data.filter(p => p.category && (p.category.includes('龙') || p.category.includes('纪念钞')))
+ } else if (currentCat === '蛇') {
+ data = data.filter(p => p.category && p.category.includes('蛇'))
+ } else if (currentCat === '马') {
+ data = data.filter(p => p.category && p.category.includes('马'))
+ } else if (currentCat === '其他') {
+ data = data.filter(p => p.category && !p.category.includes('龙') && !p.category.includes('纪念钞') && !p.category.includes('蛇') && !p.category.includes('马'))
+ }
+ }
+ // 搜索关键词过滤 - 强匹配(完全包含)
+ if (searchKeyword) {
+ const kw = searchKeyword.trim()
+ if (kw) {
+ data = data.filter(p =>
+ (p.title && p.title.includes(kw)) ||
+ (p.content && p.content.includes(kw)) ||
+ (p.category && p.category.includes(kw)) ||
+ (p.contact && p.contact.includes(kw))
+ )
+ }
+ }
+ setPosts(data)
+ setTotalPosts(todayStats.total || 0)
+ } catch { setPosts([]) }
+ setLoading(false)
+ }
+
+ useEffect(() => { if (todayStats.total > 0) fetchTodayCategory() }, [todayStats])
+
+ // 搜索关键词变化时重新获取数据
+ useEffect(() => {
+ setPage(1)
+ fetchPosts(1, '')
+ }, [searchKeyword])
+
+ const togglePost = id => setExpandedPosts(p => ({ ...p, [id]: !p[id] }))
+
+ const StatCard = ({ label, value, color, onClick }) => (
+
+ )
+
+ return (
+
+
+
+ 📈 连体钞/纪念钞 {dateStr}
+
+
+ { setPostTypeFilter('all'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+ { setPostTypeFilter('deal'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+ { setPostTypeFilter('want'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+ { setPostTypeFilter('other'); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }} />
+
+
+
+
+
📊 今日分类统计
+
+ {todayCategory.map(cat => (
+
+ {cat.category} ({cat.count})
+
+ ))}
+
+
+
+ {/* 搜索框 */}
+
+
+ { setSearchKeyword(e.target.value); setPage(1); fetchPosts(1, '') }}
+ style={{ flex: 1, padding: '10px 14px', borderRadius: 8, border: '1px solid #374151', background: '#1f2937', color: '#fff', fontSize: 13, outline: 'none' }}
+ />
+
+
+ {searchKeyword &&
搜索: "{searchKeyword}",找到 {posts.length} 条结果
}
+
+
+
+ {[{key:'all',label:'全部'},{key:'deal',label:'出售'},{key:'want',label:'求购'},{key:'other',label:'其他'}].map(k => (
+
+ ))}
+
+
+
+ {[{key:'',label:'全部'},{key:'龙',label:'龙钞'},{key:'蛇',label:'蛇钞'},{key:'马',label:'马钞'},{key:'其他',label:'其他'}].map(k => (
+
+ ))}
+
+
+ {loading ?
加载中...
: (
+
+
+ {posts.map(post => (
+
+
togglePost(post.post_id)} style={{ cursor: 'pointer' }}>
+
+
{post.title||'无标题'}
+
+
+ {post.post_type==='deal'?'出售':post.post_type==='want'?'求购':post.post_type==='normal'?'其他':'普通'}
+
+
+ {post.category || '-'}
+
+
+
+
+ {post.author_username||'未知'}
+ {post.post_time?.substring(0,16)||''}
+
+
+ {expandedPosts[post.post_id] && post.content && (
+
+
+ {post.content}
+
+ {post.url &&
查看原帖}
+
+ )}
+
+ ))}
+
+
+ {/* 分页按钮移到页面底部 */}
+
+
+ 共 {totalPosts} 条帖子,{Math.ceil(totalPosts / 390)} 页,当前第 {page} 页
+
+
+ {page > 1 ? (
+
+ ) : (
+ 上一页
+ )}
+ {posts.length >= 390 ? (
+
+ ) : (
+ 下一页
+ )}
+
+
+
+ )}
+
+ )
+}
diff --git a/frontend/src/pages/YichensBoard.jsx b/frontend/src/pages/YichensBoard.jsx
index 7af8ba1..ccf5019 100644
--- a/frontend/src/pages/YichensBoard.jsx
+++ b/frontend/src/pages/YichensBoard.jsx
@@ -4,46 +4,54 @@ export default function YichensBoard() {
const [todayStats, setTodayStats] = useState({ total: 0, deals: 0, wants: 0, others: 0 })
const [todayCategory, setTodayCategory] = useState([])
const [posts, setPosts] = useState([])
- const [loading, setLoading] = useState(false)
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState(null)
const [expandedPosts, setExpandedPosts] = useState({})
const [postTypeFilter, setPostTypeFilter] = useState('all')
const [categoryFilter, setCategoryFilter] = useState('')
const [page, setPage] = useState(1)
const [totalPosts, setTotalPosts] = useState(0)
const [searchKeyword, setSearchKeyword] = useState('')
- const API_BASE = localStorage.getItem('API_BASE') || ''
const today = new Date()
const dateStr = today.getFullYear() + '/' + (today.getMonth() + 1) + '/' + today.getDate()
- useEffect(() => { fetchTodayStats(); fetchPosts(1, '') }, [])
+ useEffect(() => {
+ console.log('YichensBoard: 开始加载数据')
+ fetchTodayStats()
+ }, [])
const fetchTodayStats = async () => {
try {
- const res = await fetch(API_BASE + '/api/yichens/stats/today')
- setTodayStats(await res.json())
- } catch(e) { console.error(e) }
- }
-
- const fetchTodayCategory = async () => {
- try {
- const res = await fetch(API_BASE + '/api/yichens/stats/today-category')
- setTodayCategory(await res.json())
- } catch(e) { console.error(e) }
+ console.log('YichensBoard: 请求 /api/yichens/stats/today')
+ const res = await fetch('/api/yichens/stats/today')
+ if (!res.ok) throw new Error('stats API error: ' + res.status)
+ const data = await res.json()
+ console.log('YichensBoard: stats data', data)
+ setTodayStats(data)
+ } catch(e) {
+ console.error('YichensBoard: fetchTodayStats error', e)
+ setError(e.message)
+ }
}
const fetchPosts = async (p, cat) => {
setLoading(true)
+ setError(null)
const currentPage = p !== undefined ? p : page
const currentCat = cat !== undefined ? cat : categoryFilter
- let url = API_BASE + '/api/yichens/posts?limit=390&offset=' + ((currentPage - 1) * 390)
+ let url = '/api/yichens/posts?limit=390&offset=' + ((currentPage - 1) * 390)
if (postTypeFilter === 'deal') url += '&post_type=deal'
else if (postTypeFilter === 'want') url += '&post_type=want'
else if (postTypeFilter === 'other') url += '&post_type=normal'
+
try {
+ console.log('YichensBoard: 请求 posts', url)
const res = await fetch(url)
+ if (!res.ok) throw new Error('posts API error: ' + res.status)
let data = await res.json() || []
+ console.log('YichensBoard: posts data count', data.length)
if (currentCat) {
if (currentCat === '龙') {
data = data.filter(p => p.category && (p.category.includes('龙') || p.category.includes('纪念钞')))
@@ -55,7 +63,6 @@ export default function YichensBoard() {
data = data.filter(p => p.category && !p.category.includes('龙') && !p.category.includes('纪念钞') && !p.category.includes('蛇') && !p.category.includes('马'))
}
}
- // 搜索关键词过滤 - 强匹配(完全包含)
if (searchKeyword) {
const kw = searchKeyword.trim()
if (kw) {
@@ -69,13 +76,21 @@ export default function YichensBoard() {
}
setPosts(data)
setTotalPosts(todayStats.total || 0)
- } catch { setPosts([]) }
+ } catch(e) {
+ console.error('YichensBoard: fetchPosts error', e)
+ setError(e.message)
+ setPosts([])
+ }
setLoading(false)
}
- useEffect(() => { if (todayStats.total > 0) fetchTodayCategory() }, [todayStats])
+ useEffect(() => {
+ if (todayStats.total > 0) {
+ // fetchTodayCategory()
+ fetchPosts(1, '')
+ }
+ }, [todayStats])
- // 搜索关键词变化时重新获取数据
useEffect(() => {
setPage(1)
fetchPosts(1, '')
@@ -95,6 +110,17 @@ export default function YichensBoard() {
)
+ if (error) {
+ return (
+