From cab4d6a9bd9d33899cdd996a50e80e05ad38541d Mon Sep 17 00:00:00 2001 From: bwstudio Date: Fri, 12 Jun 2026 08:58:23 +0800 Subject: [PATCH] =?UTF-8?q?refactor(web):=20=E5=89=8D=E5=8F=B0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=85=A8=E9=9D=A2=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 搜索逻辑改为后端关键词搜索(修复前端100条限制) - 合并重复的 handleSelectType/handleDrawerSelectType - 提取 getLevelText 到公共工具 utils/joke.js - 删除废弃组件 Header.vue - 删除重复 API searchJokes - 修复 AppHeader 滚动事件监听未清理 - 统一 Rightbar URL 参数为 type_ids/crowd_ids - 清除调试 console.log 代码 - 清理未使用导入 watchEffect - onMounted 请求并行化(Promise.allSettled) --- api/app/routers/jokes.py | 8 +++ web/src/App.vue | 64 +++++++++---------- web/src/api/joke.js | 1 - web/src/components/AppHeader.vue | 17 +++--- web/src/components/AppRightbar.vue | 4 +- web/src/components/Header.vue | 71 ---------------------- web/src/utils/joke.js | 10 +++ web/src/views/detail/index.vue | 13 +--- web/src/views/home/components/JokeCard.vue | 11 +--- web/src/views/home/index.vue | 2 - web/src/views/search/index.vue | 26 +++++--- 11 files changed, 80 insertions(+), 147 deletions(-) delete mode 100644 web/src/components/Header.vue create mode 100644 web/src/utils/joke.js 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 @@