From a4be63512c826d91c60c072db2eed2de02651f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B2=E8=BE=B0=E7=94=9F=E4=BA=A7?= Date: Wed, 8 Apr 2026 22:17:55 +0800 Subject: [PATCH] =?UTF-8?q?v1.2.72=20-=20=E4=BF=AE=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E5=B0=98=E7=9C=8B=E6=9D=BF=E6=B8=B2=E6=9F=93=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- backend/VERSION | 2 +- config/VERSION | 2 +- frontend/index.html | 2 +- frontend/package.json | 2 +- frontend/src/pages/Home.jsx | 1 - frontend/src/pages/News.jsx | 7 +- frontend/src/pages/News_YichensBoard.jsx | 218 +++++++++++++++++++++++ frontend/src/pages/YichensBoard.jsx | 128 +++++++------ frontend/vite.config.js | 26 +-- 10 files changed, 317 insertions(+), 73 deletions(-) create mode 100644 frontend/src/pages/News_YichensBoard.jsx 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 @@ - 甲辰收藏 v1.2.50 + 甲辰收藏 v1.2.70 diff --git a/frontend/package.json b/frontend/package.json index 91579d5..f7a2c99 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jiachenlong-frontend", - "version": "1.2.70", + "version": "1.2.72", "private": true, "description": "甲辰藏品管理系统 - 移动端前端", "scripts": { diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index 7022bb9..f2ee81e 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -6,7 +6,6 @@ export default function Home() { const [stats, setStats] = useState({ totalCount: 0, totalCost: 0, totalRevenue: 0, expectedProfit: 0, totalProfit: 0, gradedCount: 0 }) const [recentCollections, setRecentCollections] = useState([]) const [yichensStats, setYichensStats] = useState({ total: 0, deals: 0, wants: 0, dragons: 0, horses: 0, tianma: 0 }) - const [dragonStats, setDragonStats] = useState({}) const [seekStats, setSeekStats] = useState({ seekCount: 0, matchedCount: 0 }) const [recentPosts, setRecentPosts] = useState([]) const [dragonStats, setDragonStats] = useState({}) diff --git a/frontend/src/pages/News.jsx b/frontend/src/pages/News.jsx index b316cc6..0b6d882 100644 --- a/frontend/src/pages/News.jsx +++ b/frontend/src/pages/News.jsx @@ -470,7 +470,10 @@ export default function News() { )} - {loading ? ( + {/* 一尘看板独立渲染 */} + {activeTab === 'yichen' ? ( + + ) : loading ? (
加载中...
) : infoList.length === 0 ? (
@@ -478,7 +481,7 @@ export default function News() {
) : (
- {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 }) => ( +
+
{label}
+
{value}
+
+ ) + + 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 ( +
+
加载失败: {error}
+ +
+ ) + } + return (
@@ -111,16 +137,9 @@ export default function YichensBoard() {
📊 今日分类统计
-
- {todayCategory.map(cat => ( - - {cat.category} ({cat.count}) - - ))} -
+
龙: {todayStats.dragons || 0} | 马: {todayStats.horses || 0} | 蛇: {todayStats.snakes || 0} | 天马: {todayStats.tianma || 0}
- {/* 搜索框 */}
加载中...
: (
- {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 || '-'} - + {posts.length === 0 ? ( +
暂无帖子数据
+ ) : ( + 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)||''}
-
- {post.author_username||'未知'} - {post.post_time?.substring(0,16)||''} -
+ {expandedPosts[post.post_id] && post.content && ( +
+
+ {post.content} +
+ {post.url && 查看原帖} +
+ )}
- {expandedPosts[post.post_id] && post.content && ( -
-
- {post.content} -
- {post.url && 查看原帖} -
- )} -
- ))} + )) + )}
- {/* 分页按钮移到页面底部 */}
共 {totalPosts} 条帖子,{Math.ceil(totalPosts / 390)} 页,当前第 {page} 页 diff --git a/frontend/vite.config.js b/frontend/vite.config.js index c167f55..a2b9451 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -7,9 +7,12 @@ import { join } from 'path' function getVersion() { try { const versionFile = join(__dirname, '..', 'config', 'VERSION') - const content = readFileSync(versionFile, 'utf-8') - const match = content.match(/^VERSION=(.*)$/m) - return match ? match[1].trim() : '0.0.0' + const content = readFileSync(versionFile, 'utf-8').trim() + // 移除 VERSION= 前缀 + if (content.startsWith('VERSION=')) { + return content.substring(7).trim() + } + return content || '0.0.0' } catch (e) { console.error('读取 VERSION 文件失败:', e.message) return '0.0.0' @@ -17,7 +20,7 @@ function getVersion() { } const APP_VERSION = getVersion() -console.log(`📦 构建版本:v${APP_VERSION}`) +console.log('📦 构建版本:v' + APP_VERSION) // 构建时自动更新 index.html 的 title function updateHtmlTitle() { @@ -27,10 +30,10 @@ function updateHtmlTitle() { // 替换 甲辰收藏 vXXX htmlContent = htmlContent.replace( /甲辰收藏 v[\d.]+<\/title>/, - `<title>甲辰收藏 v${APP_VERSION}` + '甲辰收藏 v' + APP_VERSION + '' ) writeFileSync(htmlPath, htmlContent, 'utf-8') - console.log(`✅ 已更新 index.html title: 甲辰收藏 v${APP_VERSION}`) + console.log('✅ 已更新 index.html title: 甲辰收藏 v' + APP_VERSION) } catch (e) { console.error('更新 index.html 失败:', e.message) } @@ -47,11 +50,10 @@ export default defineConfig({ build: { rollupOptions: { output: { - entryFileNames: `assets/[name]-[hash]-[name].js`, - chunkFileNames: `assets/[name]-[hash].js`, - assetFileNames: `assets/[name]-[hash].[ext]` + entryFileNames: 'assets/[name]-[hash]-[name].js', + chunkFileNames: 'assets/[name]-[hash].js', + assetFileNames: 'assets/[name]-[hash].[ext]' } - }, - // 禁用缓存 + } } -}) +}) \ No newline at end of file