diff --git a/api/app/routers/jokes.py b/api/app/routers/jokes.py index c7a1603..87544c7 100644 --- a/api/app/routers/jokes.py +++ b/api/app/routers/jokes.py @@ -107,11 +107,19 @@ def list_jokes( page_size: int = 20, type_ids: str | None = Query(None, description="逗号分隔的类型 ID,如 1,3,5"), crowd_ids: str | None = Query(None, description="逗号分隔的人群 ID,如 2,4"), + keyword: str | None = Query(None, description="关键词搜索(标题+内容)"), db: Session = Depends(get_db), ): """获取笑话列表(仅返回已审核通过的笑话)""" query = db.query(Joke).filter(Joke.status == "approved") + # 关键词搜索:标题和内容模糊匹配 + if keyword: + kw = f"%{keyword}%" + query = query.filter( + (Joke.title.like(kw)) | (Joke.content.like(kw)) + ) + # 支持多选过滤:逗号分隔的 ID # 使用自定义 JSON 匹配函数避免 LIKE %N% 的错误匹配 if type_ids: diff --git a/web/src/App.vue b/web/src/App.vue index 2b1825e..4cd981a 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -15,7 +15,7 @@ @@ -119,7 +119,7 @@ const handleRandom = async () => { } catch (e) { /* ignore */ } } -const handleSelectType = (typeId) => { +const handleSelectType = (typeId, closeMenu = false) => { // 单选:点同一个取消,点别的切换 if (filterTypeIds.value.length === 1 && filterTypeIds.value[0] === typeId) { filterTypeIds.value = [] @@ -129,6 +129,7 @@ const handleSelectType = (typeId) => { filterCrowdIds.value = [] const query = filterTypeIds.value.length ? { type_ids: filterTypeIds.value.join(',') } : {} router.push({ path: '/', query }) + if (closeMenu) menuOpen.value = false } const handleSelectCrowd = (crowdId) => { @@ -141,21 +142,6 @@ const handleSelectCrowd = (crowdId) => { filterTypeIds.value = [] const query = filterCrowdIds.value.length ? { crowd_ids: filterCrowdIds.value.join(',') } : {} router.push({ path: '/', query }) - // 移动端关闭菜单 - menuOpen.value = false -} - -// 抽屉菜单选择分类(单选) -const handleDrawerSelectType = (typeId) => { - if (filterTypeIds.value.length === 1 && filterTypeIds.value[0] === typeId) { - filterTypeIds.value = [] - } else { - filterTypeIds.value = [typeId] - } - filterCrowdIds.value = [] - const query = filterTypeIds.value.length ? { type_ids: filterTypeIds.value.join(',') } : {} - router.push({ path: '/', query }) - // 移动端关闭菜单 menuOpen.value = false } @@ -173,30 +159,38 @@ watch(() => route.query, (q) => { }, { immediate: true }) onMounted(async () => { - console.log('[APP] mounting, route:', route.path) - try { - allTypes.value = await getTypes() - allCrowds.value = await getCrowds() - console.log('[APP] categories loaded:', allTypes.value.length, allCrowds.value.length) - } catch (e) { - console.warn('[APP] Failed to load categories:', e) + // 并行加载:分类、热门、统计互不依赖 + const results = await Promise.allSettled([ + Promise.all([getTypes(), getCrowds()]), + getHotMonthly(), + getStats(), + ]) + + // 分类数据 + const [catResult, hotResult, statsResult] = results + if (catResult.status === 'fulfilled') { + const [types, crowds] = catResult.value + allTypes.value = types + allCrowds.value = crowds + } else { + console.warn('[APP] Failed to load categories:', catResult.reason) } - try { - hotJokes.value = await getHotMonthly() - console.log('[APP] monthly hot jokes loaded:', hotJokes.value.length) - } catch (e) { - console.warn('[APP] Failed to load hot jokes:', e) + // 当月热门 + if (hotResult.status === 'fulfilled') { + hotJokes.value = hotResult.value + } else { + console.warn('[APP] Failed to load hot jokes:', hotResult.reason) hotJokes.value = [] } - try { - stats.value = await getStats() - console.log('[APP] stats loaded:', stats.value) - } catch (e) { - console.warn('[APP] Failed to load stats:', e) + // 统计数据 + if (statsResult.status === 'fulfilled') { + stats.value = statsResult.value + } else { + console.warn('[APP] Failed to load stats:', statsResult.reason) } - }) +}) diff --git a/web/src/utils/joke.js b/web/src/utils/joke.js new file mode 100644 index 0000000..a9e258e --- /dev/null +++ b/web/src/utils/joke.js @@ -0,0 +1,10 @@ +/** AI 质量等级文本映射 */ +export function getLevelText(level) { + const map = { + 'excellent': '⭐⭐⭐ 精品', + 'good': '⭐⭐ 良好', + 'ordinary': '⭐ 普通', + 'poor': '⚠️ 待优化' + } + return map[level] || '' +} \ No newline at end of file diff --git a/web/src/views/detail/index.vue b/web/src/views/detail/index.vue index 137a82f..464f00f 100644 --- a/web/src/views/detail/index.vue +++ b/web/src/views/detail/index.vue @@ -68,10 +68,11 @@