主题
RVH "我的" 页面功能迁移参考文档
用途:供 ReadingBrowser (RB) 桌面端按需迁移 RVH 功能时参考 生成时间:2026-04-08 数据来源:RVH 代码库 (
~/reading_vocab_helper) 实际代码分析
目录
1. 概览
Me 页面包含三个功能入口,通过卡片/链接导航:
| 功能模块 | 入口样式 | 导航路由 | 核心价值 |
|---|---|---|---|
| 词库管理 | 左列大卡片 | 直接 push NotebookListPage | 管理 8614 核心词的三种状态:生词本 / 排除词 / 待收集 |
| 阅读笔记 | 右列上卡片 | /reading-note-list | 管理阅读材料(实体书/电子书/网页),组织学习来源 |
| 来源图片 | 右列文本链接 | /storage-management | 管理 OCR 拍照产生的图片存储,智能清理释放空间 |
三者的数据关系:
reading_notes (阅读笔记)
└─ 1:N ─→ reading_sources (来源图片/文本)
└─ N:M ─→ notebook_entries (生词本) ←─ vocabulary (核心词库)
←─ excluded_words (排除词)2. 词库管理
2.1 入口页:词库管理首页
页面:lib/shared/presentation/pages/vocabulary_management_page.dart
布局:
┌─────────────────────────────────────┐
│ 核心词库统计卡片 │
│ 总词数: 8614 │
│ [生词本: 120] [排除词: 213] [待收集: 8281] │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 菜单项: │
│ 📚 生词本管理 → NotebookListPage │
│ 🚫 排除词管理 → ExcludedWordsPage │
│ 🔍 核心词浏览 → WordLookupPage │
└─────────────────────────────────────┘统计数据:
sql
totalCount = SELECT COUNT(*) FROM vocabulary
notebookCount = SELECT COUNT(DISTINCT vocabulary_id) FROM notebook_entries WHERE deleted_at IS NULL
excludedCount = SELECT COUNT(*) FROM excluded_words
pendingCount = totalCount - notebookCount - excludedCount2.2 生词本管理 (NotebookListPage)
功能点:
| 功能 | 说明 |
|---|---|
| 词汇列表 | 分页显示所有生词本条目(每页 20 条) |
| 搜索 | 按 word / definition / nativeTranslation 实时搜索(300ms 防抖) |
| 掌握状态过滤 | 多选:学习中 / 已掌握(空选=全部) |
| CEFR 级别过滤 | 多选:A1-C2(空选=全部) |
| 阅读笔记过滤 | 下拉选择某本笔记,只显示该笔记关联的词 |
| 左滑删除 | 软删除条目(设置 deleted_at) |
| 左滑标记已掌握 | 单条标记 mastery_level → level5 |
| 撤销操作 | 删除和标记已掌握都支持 SnackBar 撤销 |
| 批量选择模式 | 顶栏切换,支持全选/反选 |
| 批量标记已掌握 | 按 CEFR 级别批量标记(弹出 BottomSheet 选择级别) |
| 批量删除 | 确认后批量软删除 |
| 统计概览 | 顶部显示:学习中 / 已掌握 / 总计 |
列表项显示:
[word] [IPA] [音频播放按钮]
[释义(首个 POS 的 definition)]
[CEFR 级别标签] [掌握度标签]
← 左滑:[删除] [标记已掌握]关键 Provider:
dart
// 带参数的 Provider(支持路由传入 initialNoteId)
notebookListWithNoteProvider = StateNotifierProvider.autoDispose.family<
NotebookListNotifier, NotebookListState, NotebookListParams>2.3 排除词管理 (ExcludedWordsPage)
功能点:
| 功能 | 说明 |
|---|---|
| 双类型列表 | 系统停用词(只读)+ 用户排除词(可管理) |
| 类型过滤 | 切换显示:全部 / 系统 / 用户 |
| 搜索 | 按 word 搜索 |
| 添加排除词 | 对话框输入 word + 可选 reason |
| 左滑删除 | 仅用户排除词可删除 |
| 批量选择 | 支持多选 + 批量删除用户排除词 |
| 统计概览 | 系统停用词数 / 用户排除词数 / 总计 |
数据结构:
dart
ExcludedWordEntity {
word: String // 单词
source: String // 'system' | 'user'
vocabularyId: String? // 关联的 vocabulary.id
reason: String? // 排除原因(仅 user)
createdAt: DateTime
}2.4 核心词浏览 (WordLookupPage)
功能点:
| 功能 | 说明 |
|---|---|
| 浏览核心词库 | 分页展示 vocabulary 表所有 8614 词 |
| 搜索 | 按 word / definition / nativeTranslation 搜索 |
| CEFR 过滤 | 多选 A1-C2 |
| 点击查看详情 | 进入 WordDetailPage(完整词汇信息) |
| 左滑加入生词本 | 创建 notebook_entry |
| 左滑加入排除词 | 创建 excluded_word |
| 统计概览 | 总计 / 生词本中 / 排除词中 |
双向操作:从核心词库可以将词汇分流到生词本或排除词库。
3. 阅读笔记
3.1 笔记列表 (NoteListPage)
页面:lib/features/reading_notes/presentation/pages/reading_note_list_page.dart
功能点:
| 功能 | 说明 |
|---|---|
| 笔记列表 | 按 updated_at 降序,显示所有阅读笔记 |
| 搜索 | 按 title + author 搜索(case-insensitive) |
| 新建按钮 | 顶栏 "+New" 导航到创建页 |
| 空状态 | "Your library is empty" + 添加按钮 |
| 列表项 | 封面缩略图(36×48) + 书名 + 右侧统计指标 |
| 右侧指标 | 来源数量 / 单词数 / 掌握进度条(%) |
| 左滑编辑 | 进入编辑模式 |
| 左滑删除 | 确认对话框(提示:来源不会被删除) |
| 点击进入详情 | 导航到 NoteDetailPage |
列表项数据:
dart
NoteDisplayDto {
note: ReadingNoteEntity // 笔记实体
sourceCount: int // 来源图片/文本数量
wordCount: int // 关联的生词数
masteredCount: int // 已掌握的生词数
masteryPercent: double // 掌握率 (0.0-1.0)
}3.2 笔记详情 (BookDetailPage)
页面:lib/features/reading_notes/presentation/pages/reading_note_detail_page.dart
布局:
[AppBar: "Note Details" [编辑] [导出PDF]]
↓
[书籍信息卡片]
封面(80×120) | 书名(titleLarge) + 作者(bodyMedium)
↓
[学习统计卡片] ← 可点击,跳转到生词本(按该笔记筛选)
Total Words: 450
[████████░░░░] 75%
Learning: 120 | Mastered: 330
↓
[CEFR 分布图] ← 条件显示(仅 totalWords > 0)
水平堆叠柱状图:A1-C2,绿色=已掌握,灰色=未掌握
↓
[来源统计卡片] ← 可点击,跳转到来源列表
45 images · 12.3MB功能点:
| 功能 | 说明 |
|---|---|
| 书籍信息展示 | 封面 + 书名 + 作者 |
| 学习统计 | 总词数、学习中、已掌握、进度条 |
| CEFR 分布图 | 按级别显示掌握/未掌握分布 |
| 来源统计 | 图片数量 + 存储大小 |
| 导航到生词本 | 点击 Total Words → NotebookListPage(selectedNoteId) |
| 导航到来源列表 | 点击来源卡片 → ReadingSourceListPage |
| 编辑按钮 | 进入编辑模式 |
| PDF 导出 | 生成包含所有单词的 PDF + SharePlus 分享 |
数据加载(5 个并行查询):
① getBookById(noteId) → 笔记基本信息
② getBookWords(noteId) → 单词列表 + 掌握统计
③ getSourceCountByBook(noteId) → 来源图片数
④ getBookCefrDistribution(noteId) → CEFR 级别分布
⑤ getStorageSizeByBook(noteId) → 存储大小(bytes)3.3 创建/编辑笔记 (CreateBookPage)
页面:lib/features/reading_notes/presentation/pages/create_reading_note_page.dart
功能点:
🗑️ 2026-08-02 更新:下表中 Google Books 相关的 3 项(搜索互联网书籍 / 选择搜索结果 / 后台下载封面)RVH 已删除,不要迁移。UI 入口早已拆除、管道自此不可达,RB 侧
google-books-proxyedge function 也已下线。封面现在只有「本地选图 → 压 192×192 JPEG q85 → 存 BLOB」一条路径(跨端红线 #6c)。
| 功能 | 说明 |
|---|---|
| 书名输入 | TextFormField,必填验证 |
| 本地选封面 | ImagePicker (maxWidth:800, maxHeight:1200, quality:85) |
| 删除/更换封面 | 清除当前封面 |
| 创建模式 | 生成 UUID → createBook() → 返回 result |
| 编辑模式 | 加载现有数据 → updateBook() → 返回 true |
搜索结果 BottomSheet(🗑️ 2026-08-02 已随 Google Books 管道删除,仅存档):
DraggableScrollableSheet (初始 60%, 最大 85%)
├─ loading → CircularProgressIndicator
├─ loaded → ListView [小封面 + 书名 + 作者]
└─ error → 错误提示
用户选择 → 回填标题和封面 URL → 关闭 Sheet笔记实体:
dart
ReadingNoteEntity {
id: String // UUID
title: String // 书名(必填)
author: String? // 作者
coverImagePath: String? // 本地封面路径
description: String? // 简介
readingProgress: double // 阅读进度 (0.0-1.0)
bookType: NoteType // physical / ebook / webArticle / courseMaterial
fullTextPath: String? // 电子书原文文件路径
createdAt: DateTime
updatedAt: DateTime
}4. 来源图片/存储管理
4.1 存储管理首页 (StorageManagementPage)
页面:lib/features/reading_tracking/presentation/pages/storage_management_page.dart
布局:
[AppBar: "Storage"]
↓
[存储概览卡片]
📊 XXX images · XXX MB
[████████░░░░] 可清理: XX MB (XX%)
[Clean Up 按钮]
↓
[分隔符: "By Note"]
↓
[BookStorageCard 列表] ← 每项可点击进入详情
[封面] 书名 · XX images · XX MB
YY safe to clean
↓
[分隔符: "Cache"]
↓
[缓存管理卡片]
临时文件: XX MB [Clear Cache]功能点:
| 功能 | 说明 |
|---|---|
| 全局存储统计 | 总图片数 + 总大小 + 可清理大小 + 百分比 |
| 一键清理 | 遍历所有笔记的可清理来源,批量删除(文件+DB) |
| 按笔记分组 | 每本笔记显示:图片数、大小、可清理数 |
| 进入详情页 | 点击某本笔记 → BookStorageDetailPage |
| 缓存清理 | 清理 temp 目录 |
全局清理流程:
① 计算可清理的笔记数和图片数
② 确认对话框:"Delete X images (XX MB) from Y notes.
Word records and learning progress will be preserved."
③ 遍历每本笔记 → getCleanableSourcesByBook(noteId) → 收集 source.id
④ deleteSourcesCompletely(cleanableIds) — 删除文件 + DB 记录 (CASCADE)
⑤ SnackBar: "Cleaned up X images, freed XX MB"存储统计实体:
dart
StorageStatsEntity {
totalSourceCount: int // 总图片数
totalStorageBytes: int // 总大小
cleanableSourceCount: int // 可删除图片数
cleanableBytes: int // 可删除大小
activeSourceCount: int // 活跃图片数
activeBytes: int // 活跃大小
bookBreakdown: List<BookStorageInfo> // 按笔记分组
tempCacheBytes: int // 缓存大小
}4.2 单本笔记存储详情 (BookStorageDetailPage)
页面:lib/features/reading_tracking/presentation/pages/book_storage_detail_page.dart
功能点:
| 功能 | 说明 |
|---|---|
| 来源列表 | 平铺显示该笔记下所有来源图片 |
| 三种排序 | 按价值(默认) / 按大小 / 按日期 |
| 价值评分 | 每个来源 0-100 分,圆形徽章显示 |
| 价值分类 | 🔴 可删除 / 🟡 低价值 / 🟢 有价值 |
| 图片预览 | 点击 → 全屏预览(InteractiveViewer 支持缩放 0.5x-4x) |
| 长按多选 | 长按列表项进入多选模式 |
| 快速选择 | "Select Deletable" 一键选中所有可删除项 |
| 全选/反选 | 多选工具栏按钮 |
| 批量删除 | 底部浮动栏显示 "已选择: X 项 · XX MB" + Delete 按钮 |
来源卡片布局:
[多选☑️] [缩略图 48×48] [日期 yyyy-MM-dd] [文件大小]
[词数 · 未掌握数 · 独占数] [评分徽章]价值评分算法:
score = unmasteredRatio × 0.4
+ exclusiveScore × 0.3
+ totalWordsScore × 0.2
+ freshnessScore × 0.1
其中:
unmasteredRatio = (total - mastered) / total × 100
exclusiveScore = min(exclusiveCount × 20, 100)
totalWordsScore = min(totalWords × 10, 100)
freshnessScore = 基于创建天数(7天内=100, 14天=80, 30天=60, 90天=40, 180天=20, 更久=10)
分类规则:
deletable (可删除) : totalWords == 0 OR score < 20
lowValue (低价值) : 20 ≤ score ≤ 50
valuable (有价值) : score > 50安全保障:
删除操作只删除:
✅ 图片文件(originalImagePath, coverImagePath)
✅ DB 中的 reading_source 记录 + 关联的 word_sources
保留不删除:
✅ notebook_entries(生词本条目和学习进度)
✅ vocabulary(核心词库数据)
✅ ocrText(OCR 识别的文本内容)5. 共享数据结构与表关系
5.1 核心表结构
sql
-- 阅读笔记(原 books 表,v39 重命名)
reading_notes (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
author TEXT,
cover_image_path TEXT,
description TEXT,
reading_progress REAL DEFAULT 0.0,
book_type TEXT DEFAULT 'physical', -- physical/ebook/webArticle/courseMaterial
full_text_path TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
synced_at TEXT
)
-- 来源图片/文本
reading_sources (
id TEXT PRIMARY KEY,
note_id TEXT NOT NULL, -- FK → reading_notes.id
name TEXT NOT NULL,
source_type TEXT DEFAULT 'image', -- image/text/web
source_ref TEXT, -- URL/文件路径
image_path TEXT,
linked_at TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
synced_at TEXT
)
-- 核心词库(原 vocabulary_items,v39 重命名)
vocabulary (
id TEXT PRIMARY KEY,
word TEXT UNIQUE NOT NULL,
ipa_pronunciation TEXT,
pronunciation_url TEXT,
primary_cefr_level TEXT,
pos_definitions TEXT, -- JSON: [{pos, definition, translation, examples}]
frequency_rank INTEGER,
source TEXT DEFAULT 'preinstalled',
word_forms TEXT, -- JSON
word_family TEXT,
etymology TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
-- 生词本条目
notebook_entries (
id TEXT PRIMARY KEY,
vocabulary_id TEXT NOT NULL, -- FK → vocabulary.id
user_id TEXT NOT NULL,
mastery_level TEXT DEFAULT 'level0', -- level0-level5
next_review_date TEXT,
last_review_date TEXT,
review_count INTEGER DEFAULT 0,
correct_count INTEGER DEFAULT 0,
easy_factor REAL DEFAULT 2.5, -- SM-2 参数
interval INTEGER DEFAULT 0,
repetitions INTEGER DEFAULT 0,
added_at TEXT NOT NULL,
deleted_at TEXT, -- 软删除
synced_at TEXT
)
-- 单词-来源关联(N:M)
word_sources (
id TEXT PRIMARY KEY,
notebook_entry_id TEXT NOT NULL, -- FK → notebook_entries.id
reading_source_id TEXT NOT NULL, -- FK → reading_sources.id
lemma TEXT, -- 词形还原后的词
created_at TEXT NOT NULL,
synced_at TEXT
)
-- 排除词
excluded_words (
id TEXT PRIMARY KEY,
word TEXT NOT NULL UNIQUE,
source TEXT DEFAULT 'user', -- system/user
vocabulary_id TEXT,
reason TEXT,
created_at TEXT NOT NULL
)5.2 关键关联查询
获取某笔记的所有生词:
sql
SELECT DISTINCT ne.*, vi.word, vi.pos_definitions, vi.primary_cefr_level
FROM notebook_entries ne
JOIN word_sources wsr ON ne.id = wsr.notebook_entry_id
JOIN reading_sources rs ON wsr.reading_source_id = rs.id
JOIN vocabulary vi ON ne.vocabulary_id = vi.id
WHERE rs.note_id = ? AND ne.deleted_at IS NULL获取某来源的独占词数(只在该来源出现的词):
sql
SELECT wsr.notebook_entry_id, COUNT(*) as source_count
FROM word_sources wsr
WHERE wsr.notebook_entry_id IN (
SELECT notebook_entry_id FROM word_sources
WHERE reading_source_id IN (
SELECT id FROM reading_sources WHERE note_id = ?
)
)
GROUP BY wsr.notebook_entry_id
HAVING COUNT(DISTINCT wsr.reading_source_id) = 1获取可清理的来源:
sql
-- 来源中所有关联词都已掌握(level4/level5),则该来源可清理
SELECT rs.* FROM reading_sources rs
WHERE rs.note_id = ? AND rs.image_path IS NOT NULL
AND NOT EXISTS (
SELECT 1 FROM word_sources wsr
INNER JOIN notebook_entries ne ON wsr.notebook_entry_id = ne.id
WHERE wsr.reading_source_id = rs.id
AND ne.mastery_level NOT IN ('level4', 'level5')
)5.3 Supabase 同步表映射
| 本地表 | Supabase 表 | 同步方向 |
|---|---|---|
reading_notes | user_reading_notes | 双向 |
reading_sources | user_reading_sources | 双向 |
notebook_entries | user_notebook_entries | 双向(SM-2 merge) |
word_sources | user_word_sources | 双向 |
vocabulary | — | 仅本地(预装词库) |
excluded_words | — | 仅本地 |
6. 完整功能清单
词库管理
- [ ] 词库统计首页(总数 / 生词本 / 排除词 / 待收集)
- [ ] 生词本列表(分页、搜索、多维过滤)
- [ ] 生词本条目左滑删除(软删除 + 撤销)
- [ ] 生词本条目左滑标记已掌握(+ 撤销)
- [ ] 批量选择模式(全选/反选)
- [ ] 批量标记已掌握(按 CEFR 级别)
- [ ] 批量删除
- [ ] 按阅读笔记过滤生词
- [ ] 排除词管理(系统停用词 + 用户排除词)
- [ ] 添加/删除排除词
- [ ] 批量删除排除词
- [ ] 核心词浏览(vocabulary 全表)
- [ ] 核心词搜索 + CEFR 过滤
- [ ] 从核心词加入生词本 / 排除词
阅读笔记
- [ ] 笔记列表(搜索、排序)
- [ ] 创建笔记(标题必填 + 可选封面)
[ ] Google Books 搜索 + 自动回填标题和封面(🗑️ 2026-08-02 RVH 已删除,勿迁)- [ ] 本地选择封面(ImagePicker)
[ ] 后台下载 Google Books 封面(🗑️ 2026-08-02 RVH 已删除,勿迁)- [ ] 编辑笔记
- [ ] 删除笔记(确认对话框)
- [ ] 笔记详情页(统计 + CEFR 分布图 + 来源统计)
- [ ] 从详情页跳转到生词本(按该笔记筛选)
- [ ] 从详情页跳转到来源列表
- [ ] PDF 导出(含所有单词 + SharePlus)
来源图片/存储管理
- [ ] 全局存储统计(总量 / 可清理 / 百分比)
- [ ] 按笔记分组统计
- [ ] 全局一键清理
- [ ] 缓存大小显示 + 清理
- [ ] 单本笔记来源详情列表
- [ ] 三种排序(价值 / 大小 / 日期)
- [ ] 来源价值评分算法(0-100)
- [ ] 价值分类标签(可删除 / 低价值 / 有价值)
- [ ] 全屏图片预览(可缩放)
- [ ] 长按进入多选模式
- [ ] 快速选择可删除项
- [ ] 批量删除(保留学习记录)