v1.2.73 - 全局搜索后端支持

This commit is contained in:
甲辰生产 2026-04-08 22:40:46 +08:00
parent e5a5a2c48c
commit e5a6a5928e
2 changed files with 15 additions and 18 deletions

View File

@ -111,9 +111,10 @@ def get_posts(
offset: int = Query(0, ge=0), offset: int = Query(0, ge=0),
category: Optional[str] = None, category: Optional[str] = None,
post_type: Optional[str] = None, post_type: Optional[str] = None,
keyword: Optional[str] = None,
db: Session = Depends(get_coolbot_db) db: Session = Depends(get_coolbot_db)
): ):
"""获取帖子列表""" """获取帖子列表 - 支持全局搜索"""
query = """ query = """
SELECT post_id, title, content, category, post_type, price, SELECT post_id, title, content, category, post_type, price,
author_username, post_time, reply_count, view_count, url author_username, post_time, reply_count, view_count, url
@ -130,6 +131,11 @@ def get_posts(
query += " AND post_type = :post_type" query += " AND post_type = :post_type"
params["post_type"] = post_type params["post_type"] = post_type
# 全局搜索 - 在标题、内容、分类、联系方式中搜索
if keyword:
query += " AND (title ILIKE :keyword OR content ILIKE :keyword OR category ILIKE :keyword OR author_username ILIKE :keyword)"
params["keyword"] = f"%{keyword}%"
query += " ORDER BY post_time DESC LIMIT :limit OFFSET :offset" query += " ORDER BY post_time DESC LIMIT :limit OFFSET :offset"
results = db.execute(text(query), params).fetchall() results = db.execute(text(query), params).fetchall()

View File

@ -35,16 +35,18 @@ export default function YichensBoard() {
} }
} }
const fetchPosts = async (p, cat) => { const fetchPosts = async (p, cat, kw) => {
setLoading(true) setLoading(true)
setError(null) setError(null)
const currentPage = p !== undefined ? p : page const currentPage = p !== undefined ? p : page
const currentCat = cat !== undefined ? cat : categoryFilter const currentCat = cat !== undefined ? cat : categoryFilter
const searchKw = kw !== undefined ? kw : searchKeyword
let url = '/api/yichens/posts?limit=50&offset=' + ((currentPage - 1) * 390) let url = '/api/yichens/posts?limit=390&offset=' + ((currentPage - 1) * 390)
if (postTypeFilter === 'deal') url += '&post_type=deal' if (postTypeFilter === 'deal') url += '&post_type=deal'
else if (postTypeFilter === 'want') url += '&post_type=want' else if (postTypeFilter === 'want') url += '&post_type=want'
else if (postTypeFilter === 'other') url += '&post_type=normal' else if (postTypeFilter === 'other') url += '&post_type=normal'
if (searchKw && searchKw.trim()) url += '&keyword=' + encodeURIComponent(searchKw.trim())
try { try {
console.log('YichensBoard: 请求 posts', url) console.log('YichensBoard: 请求 posts', url)
@ -63,17 +65,6 @@ 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(e) { } catch(e) {
@ -218,16 +209,16 @@ export default function YichensBoard() {
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 16px', background: '#1e293b', borderRadius: 12, marginTop: 16 }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 16px', background: '#1e293b', borderRadius: 12, marginTop: 16 }}>
<div style={{ color: '#9ca3af', fontSize: 12 }}> <div style={{ color: '#9ca3af', fontSize: 12 }}>
{totalPosts} 条帖子{Math.ceil(totalPosts / 50)} 当前第 {page} {totalPosts} 条帖子{Math.ceil(totalPosts / 390)} 当前第 {page}
</div> </div>
<div style={{ display: 'flex', gap: 8 }}> <div style={{ display: 'flex', gap: 8 }}>
{page > 1 ? ( {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> <button onClick={() => { const newPage = page - 1; setPage(newPage); fetchPosts(newPage, categoryFilter, searchKeyword) }} 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> <span style={{ padding: '6px 12px', borderRadius: 6, background: '#374151', color: '#6b7280', fontSize: 12 }}>上一页</span>
)} )}
{posts.length >= 50 ? ( {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> <button onClick={() => { const newPage = page + 1; setPage(newPage); fetchPosts(newPage, categoryFilter, searchKeyword) }} 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> <span style={{ padding: '6px 12px', borderRadius: 6, background: '#374151', color: '#6b7280', fontSize: 12 }}>下一页</span>
)} )}