Skip to content

Latest commit

 

History

History
324 lines (251 loc) · 7.23 KB

File metadata and controls

324 lines (251 loc) · 7.23 KB

文章已读标记功能

功能概述

实现了文章的已读/未读状态管理,当用户阅读文章后,对应的未读气泡(badge)将自动消失,提供更清晰的阅读体验。

核心特性

1. 自动标记已读

  • 用户点击文章时自动标记为已读
  • 无需手动操作,提升用户体验

2. 未读数量显示

  • 侧边栏"全部"显示总未读数量
  • 每个 RSS 源显示对应的未读数量
  • 未读数量为 0 时不显示气泡

3. 实时更新

  • 阅读文章后立即更新未读数量
  • 统计数据实时同步

技术实现

后端实现

1. 数据库变更

添加字段:在 articles 表添加 is_read 字段

ALTER TABLE articles ADD COLUMN is_read INTEGER DEFAULT 0
  • 0 = 未读
  • 1 = 已读

添加索引:优化查询性能

CREATE INDEX idx_articles_is_read ON articles(is_read)

2. 数据库方法

标记已读

def mark_article_as_read(self, article_id: int) -> bool:
    """标记文章为已读"""
    cursor.execute("""
        UPDATE articles 
        SET is_read = 1 
        WHERE id = ?
    """, (article_id,))

获取未读数量

def get_unread_count(self, source: Optional[str] = None, feed_source_id: Optional[int] = None) -> int:
    """获取未读文章数量(支持按来源或feed_source_id筛选)"""

修改统计方法

def get_statistics(self) -> Dict[str, Any]:
    """返回统计信息,包括:
    - total_articles: 总文章数
    - unread_articles: 未读文章数
    - sources_unread: 每个来源的未读数量
    """

3. API 接口

标记文章已读

POST /api/articles/{article_id}/read

获取未读数量

GET /api/articles/unread/count?source=xxx&feed_source_id=xxx

统计信息

GET /api/statistics

返回数据新增字段:

  • unread_articles: 总未读数
  • sources_unread: 每个来源的未读数

前端实现

1. 类型定义

Article 接口

export interface Article {
  id: number
  title: string
  // ... 其他字段
  is_read?: number  // 0=未读, 1=已读
}

Statistics 接口

export interface Statistics {
  total_articles: number
  unread_articles: number  // 新增
  sources: Record<string, number>
  sources_unread: Record<string, number>  // 新增
}

2. API 调用

// src/api/articles.ts
export const markArticleAsRead = (id: number): Promise<{ success: boolean; message: string }> => 
  api.post(`/api/articles/${id}/read`)

export const getUnreadCount = (params?: { source?: string; feed_source_id?: number }): Promise<{ count: number }> => 
  api.get('/api/articles/unread/count', { params })

3. Store 方法

// src/stores/article.ts
const markAsRead = async (articleId: number) => {
  try {
    await markArticleAsRead(articleId)
    // 更新本地状态
    const article = articles.value.find(a => a.id === articleId)
    if (article) {
      article.is_read = 1
    }
  } catch (error) {
    console.error('Failed to mark article as read:', error)
  }
}

4. 组件集成

ArticleItem.vue:点击文章时标记已读

const openArticle = async () => {
  // 标记为已读
  await articleStore.markAsRead(props.article.id)
  // 跳转到详情页
  router.push(`/article/${props.article.id}`)
}

Sidebar.vue:显示未读数量

<!-- 全部 - 显示总未读数 -->
<span class="count" v-if="statistics && statistics.unread_articles > 0">
  {{ statistics.unread_articles }}
</span>

<!-- RSS 源 - 显示来源未读数 -->
<span class="count" v-if="getFeedUnreadCount(feed.title) > 0">
  {{ getFeedUnreadCount(feed.title) }}
</span>

数据流程

标记已读流程

用户点击文章
    ↓
ArticleItem.openArticle()
    ↓
articleStore.markAsRead(id)
    ↓
API: POST /api/articles/{id}/read
    ↓
数据库: UPDATE articles SET is_read=1
    ↓
更新本地 article.is_read = 1
    ↓
跳转到文章详情页

未读数量更新流程

页面加载
    ↓
statisticsStore.fetchStatistics()
    ↓
API: GET /api/statistics
    ↓
数据库: 查询未读数量
    ↓
返回统计数据(含未读数)
    ↓
侧边栏显示未读气泡

UI/UX 设计

未读气泡样式

.count {
  margin-left: auto;
  font-size: 11px;
  color: var(--text-tertiary);
  background: rgba(255, 107, 53, 0.1);  // 橙色淡背景
  padding: 2px 6px;
  border-radius: 10px;
  min-width: 18px;
  text-align: center;
}

显示规则

  • 未读数 > 0: 显示橙色气泡
  • 未读数 = 0: 不显示气泡(视觉更清爽)
  • 收藏: 始终显示数量(不受已读影响)

使用场景

场景 1:阅读新文章

  1. 侧边栏显示"全部 23"(23篇未读)
  2. 用户点击某篇文章
  3. 文章自动标记为已读
  4. 刷新统计后显示"全部 22"

场景 2:RSS 源筛选

  1. 侧边栏显示"TechCrunch 5"(5篇未读)
  2. 用户点击进入 TechCrunch
  3. 阅读一篇文章
  4. 返回后显示"TechCrunch 4"

场景 3:全部已读

  1. 用户阅读完所有未读文章
  2. 侧边栏不再显示任何未读气泡
  3. 界面清爽,阅读体验佳

性能优化

数据库优化

  • ✅ 在 is_read 字段上创建索引
  • ✅ 统计查询使用 SUM(CASE) 而非多次查询
  • ✅ 避免 N+1 查询问题

前端优化

  • ✅ 标记已读后立即更新本地状态
  • ✅ 避免频繁刷新统计数据
  • ✅ 使用计算属性缓存未读数量

后续优化建议

短期优化

  1. 批量标记已读 - 支持"全部标记为已读"
  2. 未读筛选 - 添加"仅显示未读"选项
  3. 标记未读 - 支持重新标记为未读

中期优化

  1. 键盘快捷键 - m 键标记已读/未读
  2. 滚动自动标记 - 滚动到文章时自动标记
  3. 时间衰减 - 超过 N 天自动标记为已读

长期优化

  1. 多设备同步 - 跨设备同步已读状态
  2. 阅读进度 - 记录文章阅读百分比
  3. 阅读历史 - 完整的阅读记录

测试建议

功能测试

  • 点击文章是否正确标记为已读
  • 未读数量是否正确更新
  • 刷新页面后状态是否保持
  • 侧边栏气泡显示是否正确

边界测试

  • 未读数为 0 时不显示气泡
  • 已读文章再次点击不重复标记
  • 数据库连接失败的错误处理
  • API 调用失败的降级处理

性能测试

  • 大量文章(10000+)的查询性能
  • 统计查询的响应时间
  • 并发标记已读的处理

相关文件

后端文件

  • backend/modules/database.py - 数据库方法
  • backend/api_server.py - API 接口

前端文件

  • frontend/src/types/article.ts - 文章类型定义
  • frontend/src/types/api.ts - 统计类型定义
  • frontend/src/api/articles.ts - API 调用
  • frontend/src/stores/article.ts - 文章状态管理
  • frontend/src/components/Article/ArticleItem.vue - 文章项组件
  • frontend/src/components/Layout/Sidebar.vue - 侧边栏组件

版本信息

  • 版本: v1.0
  • 实现日期: 2025-11-02
  • 状态: ✅ 已完成

注意: 此功能需要重启后端服务以应用数据库变更。旧数据的 is_read 字段默认为 0(未读)。