v1.2.45 一尘看板优化版:首页添加一尘数据模块、一尘看板搜索功能、分类统计优化
This commit is contained in:
parent
9d1b59a421
commit
f11c4a0be0
|
|
@ -195,7 +195,8 @@ async def get_today_stats(db: Session = Depends(get_coolbot_db)):
|
||||||
SUM(CASE WHEN post_type = 'normal' THEN 1 ELSE 0 END) as others,
|
SUM(CASE WHEN post_type = 'normal' THEN 1 ELSE 0 END) as others,
|
||||||
SUM(CASE WHEN category LIKE '%龙%' THEN 1 ELSE 0 END) as dragons,
|
SUM(CASE WHEN category LIKE '%龙%' THEN 1 ELSE 0 END) as dragons,
|
||||||
SUM(CASE WHEN category LIKE '%马%' THEN 1 ELSE 0 END) as horses,
|
SUM(CASE WHEN category LIKE '%马%' THEN 1 ELSE 0 END) as horses,
|
||||||
SUM(CASE WHEN category LIKE '%蛇%' THEN 1 ELSE 0 END) as snakes
|
SUM(CASE WHEN category LIKE '%蛇%' THEN 1 ELSE 0 END) as snakes,
|
||||||
|
SUM(CASE WHEN title LIKE '%天马%' OR content LIKE '%天马%' THEN 1 ELSE 0 END) as tianma
|
||||||
FROM yichens_posts
|
FROM yichens_posts
|
||||||
WHERE post_time >= CURRENT_DATE
|
WHERE post_time >= CURRENT_DATE
|
||||||
"""
|
"""
|
||||||
|
|
@ -205,9 +206,10 @@ async def get_today_stats(db: Session = Depends(get_coolbot_db)):
|
||||||
"deals": result[1] or 0,
|
"deals": result[1] or 0,
|
||||||
"wants": result[2] or 0,
|
"wants": result[2] or 0,
|
||||||
"others": result[3] or 0,
|
"others": result[3] or 0,
|
||||||
"dragons": result[3] or 0,
|
"dragons": result[4] or 0,
|
||||||
"horses": result[4] or 0,
|
"horses": result[5] or 0,
|
||||||
"snakes": result[5] or 0
|
"snakes": result[6] or 0,
|
||||||
|
"tianma": result[7] or 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@router.get("/stats/hour")
|
@router.get("/stats/hour")
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
VERSION=1.2.43
|
VERSION=1.2.45
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<title>甲辰收藏 v1.2.42</title>
|
<title>甲辰收藏 v1.2.45</title>
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/png" href="/static/icons/favicon.png" />
|
<link rel="icon" type="image/png" href="/static/icons/favicon.png" />
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export default function Home() {
|
||||||
const [user, setUser] = useState(null)
|
const [user, setUser] = useState(null)
|
||||||
const [stats, setStats] = useState({ totalCount: 0, totalCost: 0, totalRevenue: 0, expectedProfit: 0, totalProfit: 0, gradedCount: 0 })
|
const [stats, setStats] = useState({ totalCount: 0, totalCost: 0, totalRevenue: 0, expectedProfit: 0, totalProfit: 0, gradedCount: 0 })
|
||||||
const [recentCollections, setRecentCollections] = useState([])
|
const [recentCollections, setRecentCollections] = useState([])
|
||||||
|
const [yichensStats, setYichensStats] = useState({ total: 0, deals: 0, wants: 0, dragons: 0, horses: 0, tianma: 0 })
|
||||||
const currentPath = window.location.hash.slice(1) || '/'
|
const currentPath = window.location.hash.slice(1) || '/'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -54,6 +55,13 @@ export default function Home() {
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// 获取一尘看板数据
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/yichens/stats/today').then(res => res.json()).then(data => {
|
||||||
|
setYichensStats(data || {})
|
||||||
|
}).catch(() => {})
|
||||||
|
}, [])
|
||||||
|
|
||||||
// 检查是否为管理员
|
// 检查是否为管理员
|
||||||
const isAdmin = user && user.role === 'admin'
|
const isAdmin = user && user.role === 'admin'
|
||||||
|
|
||||||
|
|
@ -138,6 +146,37 @@ export default function Home() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 一尘看板数据 */}
|
||||||
|
<div style={{ marginBottom: '20px' }}>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px', marginBottom: '12px', paddingLeft: '4px' }}>📊 一尘今日数据</div>
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '8px' }}>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(59,130,246,0.15) 0%, rgba(59,130,246,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(59,130,246,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#60a5fa', fontSize: '20px', fontWeight: '700' }}>{yichensStats.total || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>总帖子</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(16,185,129,0.15) 0%, rgba(16,185,129,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(16,185,129,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#34d399', fontSize: '20px', fontWeight: '700' }}>{yichensStats.deals || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>出售</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(245,158,11,0.15) 0%, rgba(245,158,11,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(245,158,11,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#fbbf24', fontSize: '20px', fontWeight: '700' }}>{yichensStats.wants || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>求购</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(139,92,246,0.15) 0%, rgba(139,92,246,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(139,92,246,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#a78bfa', fontSize: '20px', fontWeight: '700' }}>{yichensStats.dragons || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>龙钞</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(236,72,153,0.15) 0%, rgba(236,72,153,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(236,72,153,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#f472b6', fontSize: '20px', fontWeight: '700' }}>{yichensStats.horses || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>马钞</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(20,184,166,0.15) 0%, rgba(20,184,166,0.05) 100%)', borderRadius: '12px', padding: '12px 8px', border: '1px solid rgba(20,184,166,0.2)', textAlign: 'center' }}>
|
||||||
|
<div style={{ color: '#2dd4bf', fontSize: '20px', fontWeight: '700' }}>{yichensStats.tianma || 0}</div>
|
||||||
|
<div style={{ color: 'rgba(255,255,255,0.5)', fontSize: '10px' }}>天马</div>
|
||||||
|
</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>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export default function YichensBoard() {
|
||||||
const [categoryFilter, setCategoryFilter] = useState('')
|
const [categoryFilter, setCategoryFilter] = useState('')
|
||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [totalPosts, setTotalPosts] = useState(0)
|
const [totalPosts, setTotalPosts] = useState(0)
|
||||||
|
const [searchKeyword, setSearchKeyword] = useState('')
|
||||||
const API_BASE = localStorage.getItem('API_BASE') || ''
|
const API_BASE = localStorage.getItem('API_BASE') || ''
|
||||||
|
|
||||||
const today = new Date()
|
const today = new Date()
|
||||||
|
|
@ -54,6 +55,18 @@ export default function YichensBoard() {
|
||||||
data = data.filter(p => p.category && !p.category.includes('龙') && !p.category.includes('纪念钞') && !p.category.includes('蛇') && !p.category.includes('马'))
|
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)
|
setPosts(data)
|
||||||
setTotalPosts(todayStats.total || 0)
|
setTotalPosts(todayStats.total || 0)
|
||||||
} catch { setPosts([]) }
|
} catch { setPosts([]) }
|
||||||
|
|
@ -62,6 +75,12 @@ export default function YichensBoard() {
|
||||||
|
|
||||||
useEffect(() => { if (todayStats.total > 0) fetchTodayCategory() }, [todayStats])
|
useEffect(() => { if (todayStats.total > 0) fetchTodayCategory() }, [todayStats])
|
||||||
|
|
||||||
|
// 搜索关键词变化时重新获取数据
|
||||||
|
useEffect(() => {
|
||||||
|
setPage(1)
|
||||||
|
fetchPosts(1, '')
|
||||||
|
}, [searchKeyword])
|
||||||
|
|
||||||
const togglePost = id => setExpandedPosts(p => ({ ...p, [id]: !p[id] }))
|
const togglePost = id => setExpandedPosts(p => ({ ...p, [id]: !p[id] }))
|
||||||
|
|
||||||
const StatCard = ({ label, value, color, onClick }) => (
|
const StatCard = ({ label, value, color, onClick }) => (
|
||||||
|
|
@ -90,17 +109,36 @@ export default function YichensBoard() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, marginBottom: 20, border: '1px solid #374151' }}>
|
<div style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 12, marginBottom: 20, border: '1px solid #374151' }}>
|
||||||
<div style={{ color: '#f9fafb', fontSize: 14, fontWeight: 600, marginBottom: 12 }}>📊 今日分类统计</div>
|
<div style={{ color: '#f9fafb', fontSize: 14, fontWeight: 600, marginBottom: 8 }}>📊 今日分类统计</div>
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
|
<div style={{ display: 'flex', gap: 6, overflowX: 'auto', paddingBottom: 4 }}>
|
||||||
{todayCategory.map(cat => (
|
{todayCategory.map(cat => (
|
||||||
<span key={cat.category} style={{ padding: '4px 12px', background: 'rgba(59,130,246,0.2)', borderRadius: 20, color: '#93c5fd', fontSize: 12 }}>
|
<span key={cat.category} style={{ padding: '4px 10px', background: 'rgba(59,130,246,0.2)', borderRadius: 16, color: '#93c5fd', fontSize: 11, whiteSpace: 'nowrap' }}>
|
||||||
{cat.category} ({cat.count})
|
{cat.category} ({cat.count})
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 搜索框 */}
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<div style={{ display: 'flex', gap: 8 }}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索标题、内容、分类..."
|
||||||
|
value={searchKeyword}
|
||||||
|
onChange={(e) => { 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' }}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => { setSearchKeyword(''); setPage(1); fetchPosts(1, '') }}
|
||||||
|
style={{ padding: '10px 16px', borderRadius: 8, border: 'none', background: '#4b5563', color: '#fff', cursor: 'pointer', fontSize: 13 }}>
|
||||||
|
清空
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{searchKeyword && <div style={{ color: '#9ca3af', fontSize: 12, marginTop: 6 }}>搜索: "{searchKeyword}",找到 {posts.length} 条结果</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
|
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
|
||||||
{[{key:'all',label:'全部'},{key:'deal',label:'出售'},{key:'want',label:'求购'},{key:'other',label:'其他'}].map(k => (
|
{[{key:'all',label:'全部'},{key:'deal',label:'出售'},{key:'want',label:'求购'},{key:'other',label:'其他'}].map(k => (
|
||||||
<button key={k.key} onClick={() => { setPostTypeFilter(k.key==='other'?'other':k.key); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }}
|
<button key={k.key} onClick={() => { setPostTypeFilter(k.key==='other'?'other':k.key); setCategoryFilter(''); setPage(1); fetchPosts(1, '') }}
|
||||||
|
|
@ -123,23 +161,6 @@ export default function YichensBoard() {
|
||||||
|
|
||||||
{loading ? <div style={{textAlign:'center',color:'#9ca3af',padding:40}}>加载中...</div> : (
|
{loading ? <div style={{textAlign:'center',color:'#9ca3af',padding:40}}>加载中...</div> : (
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 16px', background: '#1e293b', borderRadius: 12, marginTop: 16, marginBottom: 16 }}>
|
|
||||||
<div style={{ color: '#9ca3af', fontSize: 12 }}>
|
|
||||||
共 {totalPosts} 条帖子,{Math.ceil(totalPosts / 390)} 页,当前第 {page} 页
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', gap: 8 }}>
|
|
||||||
{page > 1 ? (
|
|
||||||
<button onClick={() => { const newPage = page - 1; setPage(newPage); fetchPosts(newPage, categoryFilter) }} style={{ padding: '6px 12px', borderRadius: 6, border: 'none', background: '#3b82f6', color: '#fff', cursor: 'pointer', fontSize: 12 }}>上一页</button>
|
|
||||||
) : (
|
|
||||||
<span style={{ padding: '6px 12px', borderRadius: 6, background: '#374151', color: '#6b7280', fontSize: 12 }}>上一页</span>
|
|
||||||
)}
|
|
||||||
{posts.length >= 390 ? (
|
|
||||||
<button onClick={() => { const newPage = page + 1; setPage(newPage); fetchPosts(newPage, categoryFilter) }} style={{ padding: '6px 12px', borderRadius: 6, border: 'none', background: '#3b82f6', color: '#fff', cursor: 'pointer', fontSize: 12 }}>下一页</button>
|
|
||||||
) : (
|
|
||||||
<span style={{ padding: '6px 12px', borderRadius: 6, background: '#374151', color: '#6b7280', fontSize: 12 }}>下一页</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
{posts.map(post => (
|
{posts.map(post => (
|
||||||
<div key={post.post_id} style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, border: '1px solid #374151' }}>
|
<div key={post.post_id} style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: 12, padding: 16, border: '1px solid #374151' }}>
|
||||||
|
|
@ -171,6 +192,25 @@ export default function YichensBoard() {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 分页按钮移到页面底部 */}
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 16px', background: '#1e293b', borderRadius: 12, marginTop: 16 }}>
|
||||||
|
<div style={{ color: '#9ca3af', fontSize: 12 }}>
|
||||||
|
共 {totalPosts} 条帖子,{Math.ceil(totalPosts / 390)} 页,当前第 {page} 页
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: 8 }}>
|
||||||
|
{page > 1 ? (
|
||||||
|
<button onClick={() => { const newPage = page - 1; setPage(newPage); fetchPosts(newPage, categoryFilter) }} style={{ padding: '6px 12px', borderRadius: 6, border: 'none', background: '#3b82f6', color: '#fff', cursor: 'pointer', fontSize: 12 }}>上一页</button>
|
||||||
|
) : (
|
||||||
|
<span style={{ padding: '6px 12px', borderRadius: 6, background: '#374151', color: '#6b7280', fontSize: 12 }}>上一页</span>
|
||||||
|
)}
|
||||||
|
{posts.length >= 390 ? (
|
||||||
|
<button onClick={() => { const newPage = page + 1; setPage(newPage); fetchPosts(newPage, categoryFilter) }} style={{ padding: '6px 12px', borderRadius: 6, border: 'none', background: '#3b82f6', color: '#fff', cursor: 'pointer', fontSize: 12 }}>下一页</button>
|
||||||
|
) : (
|
||||||
|
<span style={{ padding: '6px 12px', borderRadius: 6, background: '#374151', color: '#6b7280', fontSize: 12 }}>下一页</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue