Skip to content

feat(article): 文章列表添加日期热力图、分类和标签筛选#33

Open
kev1nweng wants to merge 1 commit into
mainfrom
restore-article-filter
Open

feat(article): 文章列表添加日期热力图、分类和标签筛选#33
kev1nweng wants to merge 1 commit into
mainfrom
restore-article-filter

Conversation

@kev1nweng
Copy link
Copy Markdown
Member

@kev1nweng kev1nweng commented Apr 16, 2026

问题描述

现有文章列表页只有关键词搜索,无法按日期、分类、标签筛选。文章数量增多后定位某篇文章很不方便,而闪念页面已有完善的筛选交互(热力图、标签云、时间快捷筛选),值得参考。

解决方案

  1. 左侧边栏布局 - 参考闪念页面,在文章列表左侧添加 272px 宽的侧边栏,内含热力图、时间快捷筛选、分类标签云、标签标签云
  2. 日期热力图 - 复用闪念页面的 ContributionGraph 组件,点击某天按日期筛选文章
  3. 时间快捷筛选 - 全部 / 今日 / 本月三个快捷入口,带文章数量统计
  4. 分类和标签筛选 - 从文章数据中自动统计分类/标签及其计数,以标签云形式展示,点击即可筛选
  5. 筛选互斥逻辑 - 日期、标签、分类、时间范围四种筛选互斥(同闪念页逻辑),切换时自动清除其他筛选条件
  6. 筛选条件标签 - 右侧内容区顶部显示当前激活的筛选条件,可单独点击 X 清除

改动范围

  • frontend/src/views/articles/list/index.vue - 添加左侧边栏布局、筛选条件标签
  • frontend/src/views/articles/list/composables/useArticleList.ts - 新增日期/标签/分类/时间筛选状态和逻辑,热力图/标签/分类统计数据计算
  • frontend/src/locales/*.json (11个语言文件) - 新增 todayArticlesmonthArticlesallArticlesallCategories 四个 i18n 键

测试

已在本地验证:热力图日历点击某天可按日期筛选;今日/本月快捷筛选正常工作;分类和标签云点击可筛选,计数正确;筛选互斥逻辑正常(选标签清除日期,选日期清除标签等);筛选条件标签显示正确且可单独清除;分页在筛选后正常工作。
关联 Issue: #21
分支已推送至 origin/feat/article-filter。

Closes #21

@kev1nweng kev1nweng changed the title feat(article): 重新恢复文章筛选与热力图功能 feat(article): 文章列表添加日期热力图、分类和标签筛选 Apr 16, 2026
@kev1nweng kev1nweng requested a review from Tespera April 17, 2026 03:45
@Tespera
Copy link
Copy Markdown
Member

Tespera commented Jun 4, 2026

@claude review 一下代码。

@Tespera
Copy link
Copy Markdown
Member

Tespera commented Jun 4, 2026

@claude 请 review 这个 PR。

1 similar comment
@Tespera
Copy link
Copy Markdown
Member

Tespera commented Jun 4, 2026

@claude 请 review 这个 PR。

@claude
Copy link
Copy Markdown

claude Bot commented Jun 4, 2026

Claude finished @Tespera's task in 2m 0s —— View job


PR Code Review

feat(article): 文章列表添加日期热力图、分类和标签筛选

  • 读取仓库指南 (CLAUDE.md)
  • 读取变更文件
  • 分析代码质量与逻辑
  • 提交 Review 反馈

整体实现思路清晰,与闪念页面的交互模式保持一致。发现 2 个 Bug1 个 i18n 国际化问题,以及若干代码质量建议,详见下文。


🐛 Bug

1. :label prop 传入不存在的属性(index.vue:13

<ContributionGraph
  :data="heatmapData" :label="t('article.createAt')"
  @day-click="setSelectedDate" />

ContributionGraph.vue 的 Props 定义中没有 label 属性

// ContributionGraph.vue
interface Props {
    data: Record<string, number>
}

这会导致 Vue 发出 runtime 警告,且该 prop 会被忽略(作为 HTML attribute fallthrough 处理)。需要在 ContributionGraph.vue 中添加可选的 label prop 并使用,或者从调用方移除该绑定。

Fix this →


2. 标签区域的「全部」按钮文案错误(index.vue:103

<button ... @click="setSelectedTag(null)">
    {{ t('article.allArticles') }}   
</button>

此处是标签筛选的清除按钮,但用的是 article.allArticles("All Articles" / "全部文章")。对应的分类区域使用的是 article.allCategories,逻辑正确,但标签区域文案与实际功能不匹配。

建议新增 article.allTags i18n 键(参照 memos 区域已有的 memo.allTags = "All Tags"),或直接复用 memo.allTags

Fix this →


🌐 国际化问题

3. ContributionGraph.vue 硬编码中文 locale 和星期字符

该组件原本只用于闪念页面,现在被文章页复用后,以下硬编码会影响非中文用户:

// ContributionGraph.vue:85 — 强制中文 locale,影响所有日期格式
dayjs.locale('zh-cn')

// ContributionGraph.vue:99 — 星期标题全部硬编码为中文
const weekDays = ['一', '二', '三', '四', '五', '六', '日']

// ContributionGraph.vue:51 — tooltip 硬编码中文
:title="`${day.date}: ${day.count} 条记录`"

由于闪念页面也在使用这个组件,改动需谨慎,但建议至少将 title tooltip 和 weekDays 提取为 i18n 可配置项,或接受 locale 参数。


🔧 代码质量建议

4. resetFilters 函数命名误导(useArticleList.ts:21

const resetFilters = () => {
    currentPage.value = 1   // 实际上只重置了分页
}

函数名 resetFilters 暗示它会清除所有筛选条件,但实际只重置了分页。建议改名为 resetPagination,避免后续维护时产生误解。

5. 侧边栏两个 flex-1 区域同时存在时布局

Categories 和 Tags section 都使用了 class="flex-1 overflow-y-auto"。当两者都有数据时,会各自占据侧边栏可滚动区域的一半高度。目前功能上没有问题,但如果标签数量远多于分类(或反之),用户体验可能不均衡。可以考虑只让其中一个 flex-1 或者动态调整权重。


✅ 做得好的地方

  • 筛选互斥逻辑干净清晰,setter 函数将副作用集中在一处,方便维护。
  • heatmapData / tagStats / categoryStats computed 属性性能合理,只遍历一次 posts 数组。
  • **筛选条件标签(Filter chips)**的 UX 设计直观,可单独清除每个条件。
  • i18n 覆盖全部 11 个语言文件,符合项目规范。
  • 分页在筛选后正常工作(通过 watch 自动 reset page)。

总结

优先级 问题 文件
🔴 高 :label prop 不存在,会产生 Vue 警告 index.vue:13, ContributionGraph.vue
🔴 高 标签「全部」按钮文案错误(allArticles vs allTags index.vue:103, 11 个 locale 文件
🟡 中 ContributionGraph 硬编码中文 locale/星期,复用场景下影响其他语言用户 ContributionGraph.vue
🟢 低 resetFiltersresetPagination 重命名 useArticleList.ts:21

建议修复前两个高优先级问题后再合并。

@Tespera
Copy link
Copy Markdown
Member

Tespera commented Jun 4, 2026

@claude review 一下代码。

@claude
Copy link
Copy Markdown

claude Bot commented Jun 4, 2026

Claude finished @Tespera's task in 2m 19s —— View job


PR Code Review

feat(article): 文章列表添加日期热力图、分类和标签筛选

  • 读取评审规范 (REVIEW_GUIDELINES.md — 文件不存在,按通用规范进行)
  • 读取变更文件 (index.vueuseArticleList.tsContributionGraph.vue、locale 文件)
  • 分析代码质量与逻辑
  • 提交 Review 反馈

整体实现结构清晰,与闪念页面的交互模式一致,侧边栏复用思路正确。发现 3 个 Bug(其中 2 个高优先级)、1 个全局副作用问题,以及若干代码质量建议。


🐛 Bug

1. :label prop 传入 ContributionGraph 但该组件未定义此 prop(index.vue:14

<ContributionGraph
  :data="heatmapData" :label="t('article.createAt')"
  @day-click="setSelectedDate" />

ContributionGraph.vue 的 Props 接口中 只定义了 data

interface Props {
    data: Record<string, number>
}

传入未声明的 :label 会触发 Vue 运行时警告,该值会作为 HTML attribute fallthrough 处理而非被组件消费。需要在 ContributionGraph.vue 中补充 label?: string prop 并渲染,或在调用方移除该绑定。

Fix this →


2. 标签区域「全部」按钮使用了错误的 i18n key(index.vue:103

<button @click="setSelectedTag(null)">
    {{ t('article.allArticles') }}   
</button>

分类区域同类按钮正确使用了 article.allCategories("All Categories"),但标签区域误用了 article.allArticles("All Articles")。英文界面下该按钮会显示 "All Articles",与语义不符。

memo 命名空间下已有 memo.allTags = "All Tags",建议在 article 命名空间新增 allTags 键并更新全部 11 个 locale 文件,或直接复用 memo.allTags

Fix this →


3. dayjs.locale('zh-cn') 是全局副作用,影响整个应用的日期格式(ContributionGraph.vue:85

// ContributionGraph.vue:85
dayjs.locale('zh-cn')  // ❌ 全局修改,影响所有 dayjs 调用

dayjs.locale(...) 会修改全局 locale,而非仅作用于该组件。若应用其他地方(如日期显示、编辑器时间戳)依赖系统/用户语言,该调用会静默覆盖,导致非中文用户看到中文格式。

正确做法是使用局部 locale:dayjs().locale('zh-cn').format('...'),或将 locale 作为 prop 传入。此问题在 memos 页面也存在,但此 PR 让该组件被更广泛复用,影响面扩大。


🌐 国际化问题

4. ContributionGraph.vue 硬编码中文星期和 tooltip(ContributionGraph.vue:51,99

// 星期标题硬编码中文
const weekDays = ['一', '二', '三', '四', '五', '六', '日']

// tooltip 硬编码中文单位
:title="`${day.date}: ${day.count} 条记录`"

该组件现在被文章和闪念两个页面复用,但非中文用户在所有语言下都会看到中文星期和"条记录"文字。建议将 weekDays 和 tooltip 提取为 i18n key,与已有的 memo.less / memo.more 保持一致。


🔧 代码质量建议

5. resetFilters 命名误导(useArticleList.ts:21

const resetFilters = () => {
    currentPage.value = 1   // 实际只重置了分页,不清除任何筛选条件
}

函数名暗示会清除所有筛选状态,实际只重置分页到第 1 页。建议改名为 resetPagination,避免未来维护时产生误解。

6. 侧边栏两个 flex-1 区域并存时的高度分配(index.vue:53,86

<div v-if="categoryStats.length > 0" class="flex-1 overflow-y-auto ...">


<div v-if="tagStats.length > 0" class="flex-1 overflow-y-auto ...">

两个 flex-1 区域同时存在时,各占侧边栏一半可用高度。若标签远多于分类(或反之),标签区域会被压缩、需要滚动,用户体验不均衡。可考虑根据数量动态分配权重(如 flex-[1] / flex-[2]),或仅让标签区域 flex-1、分类区域固定高度。

7. ContributionGraph.vue 重复注释(ContributionGraph.vue:61-62

   
<div class="flex items-center justify-end ...">

Minor:注释重复,建议删除一处。


✅ 做得好的地方

  • 筛选互斥逻辑 干净清晰,setter 函数将副作用集中在一处,方便维护。
  • heatmapData / tagStats / categoryStats computed 性能合理,只遍历一次 posts 数组。
  • 筛选条件 chip 的 UX 设计直观,可单独清除每个条件,与底层逻辑一致。
  • 分页 watch 联动 设计正确,任何筛选变化都自动重置到第 1 页,避免空页。
  • i18n 覆盖全部 11 个语言文件,符合项目规范。

总结

优先级 问题 位置
🔴 高 :label prop 不存在,产生 Vue 运行时警告 index.vue:14, ContributionGraph.vue
🔴 高 标签「全部」按钮 i18n key 错误(allArticlesallTags index.vue:103, 11 个 locale 文件
🟡 中 dayjs.locale('zh-cn') 全局副作用影响非中文用户 ContributionGraph.vue:85
🟡 中 星期/tooltip 硬编码中文,复用场景下影响其他语言 ContributionGraph.vue:51,99
🟢 低 resetFiltersresetPagination 重命名 useArticleList.ts:21
🟢 低 两个 flex-1 侧边栏区域高度分配不均 index.vue:53,86
🟢 低 重复注释 `` ContributionGraph.vue:61-62

建议修复前两个高优先级问题后再合并。

@Tespera
Copy link
Copy Markdown
Member

Tespera commented Jun 4, 2026

@claude review 代码。

@claude
Copy link
Copy Markdown

claude Bot commented Jun 4, 2026

Claude finished @Tespera's task in 3m 31s —— View job


PR Code Review

  • 读取评审规范 (REVIEW_GUIDELINES_MAIN.md)
  • 读取变更文件(index.vueuseArticleList.tsContributionGraph.vue、locale 文件)
  • 分析代码质量与逻辑
  • 提交 Review 反馈

总体结论

⚠️ 建议修改后合并

整体方案结构清晰,与闪念页面的交互模式保持一致,筛选互斥逻辑和 computed 计算的设计都很干净。发现 2 个阻塞级问题(会影响所有非中文用户的显示)需要修复后再合并,另有若干建议项。


问题清单

🔴 阻塞(必须修)

1. 标签区域「全部」按钮使用了错误的 i18n keyindex.vue:103

<button @click="setSelectedTag(null)">
    {{ t('article.allArticles') }}   
</button>

分类区域同类按钮正确使用了 article.allCategories("All Categories"),但标签区域误用了 article.allArticles。英文等非中文界面下,用户在标签云里会看到"All Articles"按钮,语义完全错误,容易引发困惑。

memo 命名空间已有 memo.allTags = "All Tags",建议在 article 命名空间新增 allTags key 并更新全部 11 个 locale 文件,或直接复用 memo.allTags

Fix this →


2. :label prop 传入 ContributionGraph 但组件未声明此 propindex.vue:14, ContributionGraph.vue:87

<ContributionGraph
    :data="heatmapData" :label="t('article.createAt')"
    @day-click="setSelectedDate" />

ContributionGraph.vue 的 Props 接口只声明了 data

interface Props {
    data: Record<string, number>
}

传入未声明的 :label 会触发 Vue 运行时 warning,该值作为 HTML attribute fallthrough 处理,不会被组件消费——也就是说 label 根本没有被渲染,组件行为与预期不符。需要在 ContributionGraph.vue 中补充 label?: string prop 并渲染(或从调用方移除该绑定)。

Fix this →


🟡 建议修改

3. dayjs.locale('zh-cn') 是模块级全局副作用ContributionGraph.vue:85

// 模块顶层执行,影响整个应用
dayjs.locale('zh-cn')

dayjs.locale(...) 修改的是全局 dayjs locale,不是组件局部的。任何导入了该组件的页面(现在是文章和闪念两个页面)都会把全局日期格式固化为中文,导致非中文用户的应用内所有 dayjs 格式化输出变成中文。正确做法是局部使用:dayjs().locale('zh-cn').format('...'),或将 locale 作为 prop 传入。

Fix this →

4. 星期标题与 tooltip 硬编码中文ContributionGraph.vue:99, :51

const weekDays = ['一', '二', '三', '四', '五', '六', '日']  // 中文硬编码
:title="`${day.date}: ${day.count} 条记录`"   

Legend 区域已通过 memo.less / memo.more 走了 i18n,但星期标题和 tooltip 仍然硬编码。此 PR 让该组件在文章页也得到复用,影响面扩大——所有语言的用户都会看到中文星期和"条记录"文字。建议将 weekDays 和 tooltip 提取为 i18n key。


🟢 可选(锦上添花)

5. resetFilters 函数名与行为不符useArticleList.ts:21

const resetFilters = () => {
    currentPage.value = 1   // 实际只重置了分页,不清除任何筛选条件
}

函数名暗示清除所有筛选,实际只把分页重置到第 1 页。建议改名为 resetPagination,避免后续维护产生误解。

6. 两个 flex-1 侧边栏区域同时存在时高度各占一半index.vue:53, 86

当分类和标签均有数据时,两个 flex-1 区域各占侧边栏可滚动高度的一半。如果标签数量远多于分类(比如有 50 个标签但只有 3 个分类),标签区域被严重压缩,需要大量滚动,体验不佳。可以只让标签区域 flex-1、分类区域自动高度,或根据数量动态分配权重。

7. 重复注释ContributionGraph.vue:61–62

连续两个相同注释,删掉一个即可。


踩坑维度核查

维度 是否涉及 处理情况
Wails 绑定同步 不涉及 本 PR 纯前端 UI 改动,未新增/修改 Go Facade 接口或 domain struct,无需同步 wailsjs stub 和 models.ts
i18n 涉及,有问题 新增 4 个 key 已覆盖全部 11 个语言文件,各语言译文质量良好(DE/FR/JA/KO 等均为正常翻译,无明显机翻问题);但存在两处问题:① index.vue:103 标签区域误用 allArticles(见问题 1);② ContributionGraph.vue 中星期标题与 tooltip 仍有中文硬编码(见问题 4)
前后端双渲染器一致性 不涉及 纯 UI 筛选交互,未涉及 Markdown 渲染路径,两侧无需跟进

做得好的地方

  • 筛选互斥逻辑实现干净,setter 函数将副作用集中在一处,不散落在模板事件里
  • heatmapData / tagStats / categoryStats 三个 computed 单次遍历 posts 数组,性能合理
  • 筛选条件 chip 的 UX 设计直观,每个条件独立可清除,与底层 state 完全对应
  • 分页 watch 联动设计正确,任何筛选变化都自动重置到第 1 页,不会出现空页

建议修复问题 1(i18n key 错误)和问题 2(:label prop 未声明)后再合并,问题 3、4(中文硬编码全局副作用)在后续迭代中跟进。

Tespera added a commit that referenced this pull request Jun 5, 2026
PR #33 描述过简且漏写了删除 Gitee 平台的改动,易导致漏审与回归。
评审时逐项核对描述的完整性/准确性/规范性/充分性,删除功能、移除
平台、改默认行为等高回归风险改动漏写按 🔴 级指出;描述不合格时
在评审结论末尾按 PR 模板给出可直接替换的完整描述。
@Tespera
Copy link
Copy Markdown
Member

Tespera commented Jun 5, 2026

@claude review 一下代码。

@claude
Copy link
Copy Markdown

claude Bot commented Jun 5, 2026

Claude finished @Tespera's task in 2m 47s —— View job


PR Code Review

  • 读取评审规范 (REVIEW_GUIDELINES_MAIN.md)
  • 读取变更文件(index.vueuseArticleList.tsContributionGraph.vue、locale 文件)
  • 分析代码质量与逻辑
  • 提交 Review 反馈

总体结论

⚠️ 建议修改后合并

整体实现结构清晰,筛选互斥逻辑和 computed 设计干净,与闪念页面的交互模式保持一致。但有 2 个阻塞级 Bug(其中一个会直接影响全部非中文用户的 i18n 显示),以及 1 个全局副作用问题需关注。


问题清单

🔴 阻塞(必须修)

1. 标签区域「全部」按钮误用 allArticles i18n keyindex.vue:103

<button @click="setSelectedTag(null)">
    {{ t('article.allArticles') }}   
</button>

分类区域的同类按钮正确使用了 article.allCategories("All Categories"),但标签区域误用了 article.allArticles("All Articles")。英文及其他 10 种语言的用户会在标签云里看到"All Articles"按钮,与上下文语义完全不符,容易造成困惑。

memo 命名空间已有 memo.allTags = "All Tags",建议在 article 命名空间新增 allTags key 并同步更新全部 11 个 locale 文件,或直接复用 memo.allTags

Fix this →


2. :label prop 传入 ContributionGraph 但组件未声明此 propindex.vue:14

<ContributionGraph
    :data="heatmapData" :label="t('article.createAt')"
    @day-click="setSelectedDate" />

ContributionGraph.vue 的 Props 接口只声明了 data

interface Props {
    data: Record<string, number>
}

传入未声明的 :label 会触发 Vue 运行时 warning,且该值作为 HTML attribute fallthrough 处理,不会被组件渲染——也就是说创建时间标签实际上没有显示出来,组件行为与代码意图不符。需要在 ContributionGraph.vue 中补充 label?: string prop 并渲染(或从调用方移除该绑定)。

Fix this →


🟡 建议修改

3. dayjs.locale('zh-cn') 是模块级全局副作用,影响所有非中文用户ContributionGraph.vue:85

// 模块顶层执行,修改全局 dayjs locale
dayjs.locale('zh-cn')

dayjs.locale(...) 修改的是全局 dayjs locale,不是组件局部的。只要该组件被加载(无论用户用什么界面语言),应用内所有 dayjs 格式化输出都会切换为中文——包括其他页面的日期显示、编辑器时间戳等。此 PR 让该组件在文章页也得到复用,影响面进一步扩大。

正确做法是使用局部 locale:dayjs().locale('zh-cn').format('...'),或通过 prop 传入 locale 字符串。

Fix this →

4. 星期标题与 tooltip 硬编码中文ContributionGraph.vue:99, ContributionGraph.vue:51

// 星期标题全部硬编码为中文
const weekDays = ['一', '二', '三', '四', '五', '六', '日']
:title="`${day.date}: ${day.count} 条记录`"

Legend 区域已正确使用 t('memo.less') / t('memo.more'),但星期标题和 tooltip 仍硬编码。该组件现在被文章和闪念两个页面复用,所有语言的用户都会在热力图上看到中文星期和"条记录"文字。建议将 weekDays 提取为 i18n key(或通过 dayjs 的 locale 自动生成),将 tooltip 模板也提取为带参数的 i18n key。


🟢 可选(锦上添花)

5. resetFilters 函数名与行为不符useArticleList.ts:21

const resetFilters = () => {
    currentPage.value = 1   // 实际只重置分页,不清除任何筛选条件
}

函数名 resetFilters 暗示清除所有筛选状态,实际只把分页重置到第 1 页。建议改名为 resetPagination,避免后续维护时误解。

6. 两个 flex-1 侧边栏区域同时存在时高度各占一半index.vue:54, 87

<div v-if="categoryStats.length > 0" class="flex-1 overflow-y-auto ...">
<div v-if="tagStats.length > 0" class="flex-1 overflow-y-auto ...">

当分类和标签均有数据时,两个 flex-1 区域各占侧边栏可滚动高度的一半。标签数量通常远多于分类,被压缩后需要大量滚动,体验不均衡。可以只让标签区域 flex-1、分类区域使用自动高度,或根据数量动态分配 flex 权重。

7. ContributionGraph.vue:61-62 有重复注释

连续两个相同注释,删掉一个即可。


踩坑维度核查

维度 涉及情况
Wails 绑定同步(维度 4) 不涉及。本 PR 纯前端 UI 改动,未新增或修改 Go Facade 接口及 domain struct,无需同步 wailsjs stub 和 models.ts
i18n(维度 5) 涉及,有问题。新增的 4 个 key(todayArticles / monthArticles / allArticles / allCategories)已覆盖全部 11 个语言文件,DE / JA / KO 等译文质量正常,无占位符或明显机翻问题。但存在两处缺陷:① index.vue:103 标签区域误用 allArticles key(见问题 1);② ContributionGraph.vue 星期标题与 tooltip 硬编码中文(见问题 4)。
前后端双渲染器一致性(维度 6) 不涉及。本 PR 为纯前端筛选交互,未触及 Markdown 渲染路径,两侧无需跟进。

建议的 PR 描述补充

PR 描述整体合格,但 UI 改动按项目规范需附前后截图,目前缺失,建议在描述中补充热力图侧边栏的前后对比截图。此外,描述未提及 ContributionGraph.vue 被跨页面复用这一事实,导致其原有的全局 locale 副作用和硬编码问题被带入文章页;建议在「改动范围」中补充一条说明。


做得好的地方

  • 筛选互斥逻辑干净清晰,setter 函数统一管理副作用,不在模板事件里散落清除逻辑
  • heatmapData / tagStats / categoryStats 三个 computed 单次遍历 posts 数组,性能合理
  • 筛选条件 chip UX 直观,每个条件独立可清除,与底层 state 完全对应
  • 分页 watch 联动设计正确,任何筛选变化都自动重置到第 1 页,不会出现空页
  • i18n 覆盖全部 11 个语言文件,译文质量良好,符合项目规范

建议修复问题 1(allArticlesallTags)和问题 2(:label prop 未声明)后再合并;问题 3、4(全局 locale 副作用、硬编码中文)可在后续迭代中跟进,因为这些问题在 memos 页已经存在,本 PR 并非引入源头。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 文章管理增强

2 participants