5.9 KiB
5.9 KiB
PRD:博客 /posts 页面添加「隐藏AI日报」筛选功能
1. 需求概述
在 https://blog.ephron.ren/posts 页面的搜索区域左侧,新增一个勾选框 「隐藏AI日报」。
勾选后,文章列表(含搜索结果)中不再显示标题以 AI日报 开头、或 slug 以 ai- 开头的文章。
2. 背景与现状
2.1 页面结构
/posts路由由blog/src/routes/pages.py中的posts_list()处理。- 同一页面承担「全部文章列表」和「搜索结果展示」两种状态。
- 前端模板:
blog/templates/index.html
2.2 当前搜索参数
| 参数 | 说明 | 示例 |
|---|---|---|
q |
搜索关键词 | RAG |
mode |
搜索模式:simple / fulltext |
simple |
2.3 AI日报特征
- 标题格式:
AI日报 · YYYY-MM-DD,前两个字固定为AI日报 - Slug 格式:
ai-YYYY-MM-DD,以ai-开头 - 数据规模:目前共 70 篇文章,其中 AI日报约占 40+ 篇,占比超过一半
3. 功能需求
3.1 交互
- 在搜索框左侧(
page-header区域)增加一个 checkbox:<label class="filter-checkbox"> <input type="checkbox" name="hide_ai_daily" value="1"> 隐藏AI日报 </label> - 勾选状态变化时,自动重新请求列表(无需额外「确认」按钮)。
- 当页面存在搜索词
q时,勾选框与搜索条件共存;URL 同时携带q、mode、hide_ai_daily。
3.2 筛选规则
后端过滤条件(OR 逻辑):
if hide_ai_daily:
posts = [
p for p in posts
if not (p.title.startswith("AI日报") or p.slug.startswith("ai-"))
]
3.3 持久化
- 不使用 Cookie / 本地存储记住勾选状态。
- 仅通过 URL 参数传递,用户刷新或分享链接时状态自然保持或丢失。
4. 技术方案
4.1 前端修改(blog/templates/index.html)
在 .search-box 左侧插入 checkbox 容器:
<div class="page-header">
<div class="page-header-left">
<h1>所有文章</h1>
<p>共 {{ posts | length }} 篇文章</p>
</div>
<div class="search-area">
<label class="filter-checkbox">
<input
type="checkbox"
name="hide_ai_daily"
value="1"
{% if hide_ai_daily %}checked{% endif %}
>
隐藏AI日报
</label>
<form class="search-box" method="GET" action="/posts">
<input
type="text"
name="q"
class="input"
placeholder="搜索文章..."
value="{{ search_query or '' }}"
autocomplete="off"
>
<input type="hidden" name="mode" value="{{ search_mode or 'simple' }}">
{% if hide_ai_daily %}
<input type="hidden" name="hide_ai_daily" value="1">
{% endif %}
</form>
</div>
</div>
新增样式:
.search-area {
display: flex;
align-items: center;
gap: 0.75rem;
}
.filter-checkbox {
display: inline-flex;
align-items: center;
gap: 0.375rem;
font-size: 0.875rem;
color: var(--text-muted);
cursor: pointer;
white-space: nowrap;
}
.filter-checkbox input[type="checkbox"] {
width: 1rem;
height: 1rem;
accent-color: var(--accent);
}
交互逻辑:勾选/取消勾选后,通过 JavaScript 自动提交表单:
document.querySelector('.filter-checkbox input').addEventListener('change', function() {
document.querySelector('.search-box').submit();
});
4.2 后端修改(blog/src/routes/pages.py)
在 posts_list() 函数中新增参数解析与过滤:
@router.get("/posts", response_class=HTMLResponse)
async def posts_list(
request: Request,
q: str | None = Query(default=None, description="搜索关键词"),
mode: str | None = Query(default="simple", description="搜索模式: simple 或 fulltext"),
hide_ai_daily: bool | None = Query(default=False, description="隐藏AI日报"),
):
在获取到 posts 列表后、渲染模板前,插入过滤逻辑:
# ... 现有搜索/列表逻辑 ...
# 过滤 AI日报
if hide_ai_daily:
posts = [
p for p in posts
if not (p.title.startswith("AI日报") or p.slug.startswith("ai-"))
]
# 获取文章摘要(后续不变)
4.3 搜索服务适配
simple模式:过滤在pages.py层完成,search_posts()无需改动。fulltext模式:过滤同样在pages.py层完成,search_posts_fulltext()无需改动。
理由:两种搜索模式返回的都是 PostMeta 列表,在路由层统一过滤最简洁,避免改动 Whoosh 索引逻辑。
5. 边界情况
| 场景 | 行为 |
|---|---|
| 未勾选 + 无搜索词 | 显示全部文章(当前行为) |
| 未勾选 + 有搜索词 | 搜索结果正常展示 |
| 勾选 + 无搜索词 | 全局列表隐藏 AI日报 |
| 勾选 + 有搜索词 | 搜索结果中隐藏 AI日报 |
| 过滤后结果为空 | 显示「没有找到匹配的文章」空状态 |
| AI日报标题前缀变更 | 需同步修改过滤条件(低概率) |
6. 验收标准
- 访问
/posts,页面右上角搜索框左侧出现「隐藏AI日报」勾选框。 - 勾选后页面刷新,AI日报文章从列表中消失,URL 携带
hide_ai_daily=1。 - 取消勾选,AI日报恢复显示,URL 参数消失。
- 在有搜索词的情况下勾选,搜索结果中 AI日报被过滤。
- 过滤后结果为空时,正确显示空状态提示。
- 样式与现有设计系统一致(使用
var(--text-muted)/var(--accent)变量)。
7. 不在此次范围
- 不持久化用户偏好(Cookie / 本地存储)。
- 不修改搜索索引(Whoosh)。
- 不影响草稿(
draft)过滤逻辑。 - 不新增后端 API 接口,仅扩展现有路由参数。