范围: chat_page.dart, 后端 API, 新增日历组件
一、功能概述
| 功能 | 入口 | 描述 |
|---|---|---|
| 会话内搜索 | AppBar 胶囊按钮组新增搜索图标 | 搜索当前会话的消息,支持上/下导航 |
| 历史日历跳转 | 右上角菜单 → “历史记录” | 日历视图选择日期,跳转到当天消息 |
二、确定的设计决策
| 决策点 | 选择 |
|---|---|
| 搜索图标位置 | 放在编辑按钮旁边(胶囊按钮组内) |
| 搜索范围 | 文本消息 + 文件名都搜索 |
| 日历页面形式 | 底部 Sheet,初始较高,可上拉成全屏(DraggableScrollableSheet) |
| 搜索结果导航起点 | 从最新结果开始 |
三、UI/UX 设计
3.1 正常模式 AppBar
┌──────────────────────────────────────────────┐│ [≡] 标题 [🔍][✎][⋯] │└──────────────────────────────────────────────┘ ↑ 新增搜索图标3.2 搜索模式 AppBar
┌──────────────────────────────────────────────┐│ [←] [搜索框____________________] 2/5 [↑][↓][×]│└──────────────────────────────────────────────┘| 元素 | 行为 |
|---|---|
| ← | 退出搜索模式 |
| 搜索框 | 实时搜索,输入时过滤消息 |
| 2/5 | 当前第 2 个结果,共 5 个 |
| ↑ | 上一个结果(更早的消息) |
| ↓ | 下一个结果(更新的消息) |
| × | 清空搜索词 |
3.3 菜单结构
┌──────────────────────────┐│ ── 拖动手柄 │├──────────────────────────┤│ ⓘ 会话信息 ││ 创建于 2024-12-20 │├──────────────────────────┤│ 📅 历史记录 【新增】 │├──────────────────────────┤│ 🗑️ 清空消息(橙色) │├──────────────────────────┤│ ❌ 删除会话(红色) │└──────────────────────────┘3.4 日历 Sheet(可拖拽)
初始状态(约 60% 屏幕高度):
┌──────────────────────────────────────┐│ ── 拖动手柄(上拉可全屏) │├──────────────────────────────────────┤│ ← 2024年12月 → │├──────────────────────────────────────┤│ 日 一 二 三 四 五 六 │├──────────────────────────────────────┤│ 1 2 3 4 5 6 │ ← 浅灰(无消息)│ 7 8 9 10 11 12 13 ││ 14 [15] [16] 17 18 [19] 20 │ ← 深色(有消息)│ 21 22 23 24 25 26 27 ││[28] │ ← 今天(圆圈高亮)│今天 │└──────────────────────────────────────┘上拉后(全屏):
- 可以看到多个月份
- 向上滚动查看更早月份
四、后端 API 设计
4.1 获取有消息的日期列表
GET /api/v1/send/sessions/:id/message-dates响应:
{
"dates": ["2024-12-28", "2024-12-25", "2024-12-20", "2024-12-15"],
"firstMessageDate": "2024-10-15",
"lastMessageDate": "2024-12-28"
}4.2 按日期查询消息(未来懒加载用)
GET /api/v1/send/sessions/:id/messages?date=2024-12-25&direction=forward&limit=50五、实现计划
Phase 1: 会话内搜索(前端本地搜索)
不需要后端改动
- 在胶囊按钮组添加搜索图标
- 实现搜索模式 AppBar
- 本地搜索逻辑(文本 + 文件名)
- 上/下导航结果
- 高亮匹配关键词
- 滚动到目标消息
Phase 2: 历史日历跳转
需要后端 API
- 后端:实现
message-datesAPI - 前端:菜单添加”历史记录”
- 前端:实现 DraggableScrollableSheet 日历
- 前端:日期选择后跳转到当天第一条消息
Phase 3: 懒加载
需要后端 API + 架构调整
- 后端:按日期/cursor 分页 API
- 前端:cursor 分页加载
- 前端:内存优化(消息回收)
六、技术细节
6.1 搜索状态变量
// 搜索模式
bool _isSearchMode = false;
String _searchQuery = '';
List<int> _searchResultIndices = []; // 匹配消息的索引
int _currentSearchResultIndex = -1; // 当前结果索引(-1 = 无结果)
final _searchController = TextEditingController();
final _searchFocus = FocusNode();6.2 本地搜索逻辑
void _performLocalSearch(String query) {
final q = query.trim().toLowerCase();
if (q.isEmpty) {
setState(() {
_searchResultIndices = [];
_currentSearchResultIndex = -1;
_highlightMessageId = null;
});
return;
}
final results = <int>[];
for (int i = 0; i < _messages.length; i++) {
final msg = _messages[i];
final text = msg.text?.toLowerCase() ?? '';
final fileName = msg.fileName?.toLowerCase() ?? '';
if (text.contains(q) || fileName.contains(q)) {
results.add(i);
}
}
setState(() {
_searchQuery = q;
_searchResultIndices = results;
// 从最新结果开始(results 是按时间升序,所以最后一个是最新的)
_currentSearchResultIndex = results.isEmpty ? -1 : results.length - 1;
if (_currentSearchResultIndex >= 0) {
_highlightMessageId = _messages[results[_currentSearchResultIndex]].id;
_scrollToMessage(_highlightMessageId!);
}
});
}6.3 DraggableScrollableSheet 日历
showModalBottomSheet(
context: context,
isScrollControlled: true, // 关键:允许全屏
builder: (context) => DraggableScrollableSheet(
initialChildSize: 0.6, // 初始 60% 高度
minChildSize: 0.4, // 最小 40%
maxChildSize: 0.95, // 最大 95%(接近全屏)
expand: false,
builder: (context, scrollController) => MessageHistoryCalendar(
scrollController: scrollController,
messageDates: _messageDates,
onDateSelected: (date) {
Navigator.pop(context);
_jumpToDate(date);
},
),
),
);七、共享逻辑
| 共享代码 | 使用场景 |
|---|---|
_highlightMessageId | 搜索高亮、日历跳转高亮、外部跳转高亮 |
_scrollToMessage() | 所有定位场景 |
| 消息高亮动画 | TweenAnimationBuilder 闪烁效果 |
八、文件变更预览
| 文件 | 变更 |
|---|---|
chat_page.dart | 添加搜索模式、搜索 AppBar、菜单项 |
message_history_calendar.dart | 新建日历组件 |
server.go | 添加 message-dates API |
api_client.dart | 添加 getMessageDates() 方法 |
十、实现进度
Phase 1: 会话内搜索 ✅ 已完成
实现摘要:
- 搜索状态变量:
_isSearchMode,_searchQuery,_searchResultIndices,_currentSearchResultIndex - 胶囊按钮组添加搜索图标 (TablerIcons.search)
- 搜索模式 AppBar: 返回按钮 + 搜索框 + 结果计数 + 上下导航
- 本地搜索逻辑: 搜索文本消息 + 文件名
- MessageBubble 添加
highlightQuery参数,支持高亮关键词 - 菜单添加 “历史记录” 选项
Phase 2: 历史日历跳转 ✅ 已完成
实现摘要:
- 后端 API:
GET /send/sessions/:id/message-dates返回有消息的日期列表 - ApiClient:
getMessageDates()方法 - MessageHistoryCalendar 组件: DraggableScrollableSheet + 日历网格
- 日历特性:
- 紧凑模式 (60%):单月视图,左右滑动切换月份 (PageView)
- 展开模式 (95%):多月视图,上下滚动浏览 (ListView)
- snap: true,snapSizes: [0.6, 0.95] 自动吸附
- 有消息日期深色可点击,无消息日期浅灰
- 今天框线高亮 + “今天” 标签
- 月份导航 + “今天” 按钮
- 展开模式滑到顶部继续下拉 -> 收回紧凑模式
- 点击日期跳转到当天第一条消息并高亮