From 810cbb7d3dd2823bdb8ce2eb9aaf96a6bbe942ad Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Sun, 6 Jul 2025 22:32:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=81=8A=E5=A4=A9=E8=AE=B0=E5=BD=95):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=86=E9=A1=B5=E5=8A=A0=E8=BD=BD=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E6=B6=88=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现聊天记录的分页加载功能,当用户滚动到顶部时自动加载更多历史消息。新增加载状态指示器,包括加载中、加载失败和默认提示三种状态。优化消息列表性能,避免一次性加载所有历史记录。 --- .../agent/controllers/agent_controller.dart | 189 ++++++++++++++---- lib/modules/agent/views/agent_view.dart | 83 +++++++- 2 files changed, 231 insertions(+), 41 deletions(-) diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 455e3aeaa..ab76f6703 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -120,6 +120,13 @@ class AgentController extends GetxController { // 会话ID final String sessionId = 'default_agent'; + // 分页加载相关状态 + final isLoadingMore = false.obs; // 是否正在加载更多消息 + final hasMoreMessages = true.obs; // 是否还有更多消息 + final currentPage = 1.obs; // 当前页码 + final pageSize = 20; // 每页消息数量 + final isLoadingMoreError = false.obs; // 加载更多时是否出错 + // 是否已加载聊天记录 final isHistoryLoaded = false.obs; @@ -137,6 +144,7 @@ class AgentController extends GetxController { void onInit() { super.onInit(); _loadTtsPreference(); //加载TTS偏好设置 + _setupScrollListener(); //设置滚动监听器 _loadChatHistory(); //加载聊天历史记录 _subscribeToEvents(); //订阅代理服务事件 _initMusicStatus(); //初始化音乐状态 @@ -270,63 +278,33 @@ class AgentController extends GetxController { final result = await ChatStorage.getMessages( sessionId: sessionId, //会话ID page: 1, //从第一页开始 - pageSize: 20, //每页20条记录 + pageSize: pageSize, //每页20条记录 ); if (result.messages.isNotEmpty) { // 转换并添加消息到列表 - final historyMessages = result.messages.map((msg) { - final meta = msg.metadata != null && msg.metadata != "" - ? jsonDecode(msg.metadata!) - : null; // 如果为空,使用空对象 - - // 从元数据中提取图片信息 - final hasImage = meta != null && meta.containsKey('hasImage') - ? meta['hasImage'] as bool - : false; - final imagePath = meta != null && meta.containsKey('imagePath') - ? meta['imagePath'] as String? - : null; - - return Message( - isUser: msg.sender == 'user', - text: msg.message, - meta: meta, - timestamp: DateTime.parse(msg.timestamp), - hasImage: hasImage, - imagePath: imagePath, - ); - }).toList(); + final historyMessages = _convertChatMessagesToMessages(result.messages); // 消息按时间排序,最新的消息在底部 historyMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); messages.assignAll(historyMessages); - Logger.i(TAG, '成功加载 ${historyMessages.length} 条聊天记录'); - // 详细打印每条消息的内容和属性,便于调试 - Logger.d(TAG, '===== 历史消息详情开始 ====='); - for (int i = 0; i < historyMessages.length; i++) { - final msg = historyMessages[i]; - Logger.d( - TAG, - '消息[$i]: {' - 'isUser: ${msg.isUser}, ' - 'text: "${msg.text.length > 50 ? msg.text.substring(0, 50) + "..." : msg.text}", ' - 'time: ${msg.timestamp}, ' - 'hasImage: ${msg.hasImage}, ' - 'imagePath: ${msg.imagePath ?? "无"}, ' - 'meta: ${msg.meta != null ? "有" : "无"}' - '}'); - } - Logger.d(TAG, '===== 历史消息详情结束 ====='); + // 更新分页状态 + currentPage.value = 1; + hasMoreMessages.value = result.totalPages > 1; + + Logger.i(TAG, '成功加载 ${historyMessages.length} 条聊天记录,总页数: ${result.totalPages}'); + _logMessageDetails(historyMessages); } else { // 没有历史记录时添加欢迎消息 _addWelcomeMessage(); + hasMoreMessages.value = false; Logger.i(TAG, '没有历史记录,添加欢迎消息'); } } catch (e) { Logger.e(TAG, '加载聊天历史失败: $e'); _addWelcomeMessage(); + hasMoreMessages.value = false; } finally { isHistoryLoaded.value = true; messages.refresh(); @@ -334,6 +312,137 @@ class AgentController extends GetxController { } } + // 加载更多历史消息(分页加载) + Future loadMoreMessages() async { + // 防止重复加载 + if (isLoadingMore.value || !hasMoreMessages.value) { + Logger.d(TAG, '跳过加载更多:isLoadingMore=${isLoadingMore.value}, hasMoreMessages=${hasMoreMessages.value}'); + return; + } + + try { + isLoadingMore.value = true; + isLoadingMoreError.value = false; + + final nextPage = currentPage.value + 1; + Logger.i(TAG, '正在加载第 $nextPage 页历史消息...'); + + final result = await ChatStorage.getMessages( + sessionId: sessionId, + page: nextPage, + pageSize: pageSize, + ); + + if (result.messages.isNotEmpty) { + // 转换新消息 + final newMessages = _convertChatMessagesToMessages(result.messages); + + // 按时间排序新消息 + newMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp)); + + // 添加短暂延迟,让用户感知到加载过程 + await Future.delayed(const Duration(milliseconds: 300)); + + // 将新消息插入到现有消息列表的开头(因为是历史消息) + final updatedMessages = [...newMessages, ...messages]; + messages.assignAll(updatedMessages); + + // 更新分页状态 + currentPage.value = nextPage; + hasMoreMessages.value = nextPage < result.totalPages; + + Logger.i(TAG, '成功加载 ${newMessages.length} 条历史消息,当前页: $nextPage/${result.totalPages}'); + } else { + // 没有更多消息 + hasMoreMessages.value = false; + Logger.i(TAG, '没有更多历史消息'); + } + } catch (e) { + Logger.e(TAG, '加载更多消息失败: $e'); + isLoadingMoreError.value = true; + } finally { + isLoadingMore.value = false; + } + } + + // 转换ChatMessage到Message的通用方法 + List _convertChatMessagesToMessages(List chatMessages) { + return chatMessages.map((msg) { + final meta = msg.metadata != null && msg.metadata != "" + ? jsonDecode(msg.metadata!) + : null; // 如果为空,使用空对象 + + // 从元数据中提取图片信息 + final hasImage = meta != null && meta.containsKey('hasImage') + ? meta['hasImage'] as bool + : false; + final imagePath = meta != null && meta.containsKey('imagePath') + ? meta['imagePath'] as String? + : null; + + return Message( + isUser: msg.sender == 'user', + text: msg.message, + meta: meta, + timestamp: DateTime.parse(msg.timestamp), + hasImage: hasImage, + imagePath: imagePath, + ); + }).toList(); + } + + // 打印消息详情的通用方法 + void _logMessageDetails(List messageList) { + Logger.d(TAG, '===== 历史消息详情开始 ====='); + for (int i = 0; i < messageList.length; i++) { + final msg = messageList[i]; + Logger.d( + TAG, + '消息[$i]: {' + 'isUser: ${msg.isUser}, ' + 'text: "${msg.text.length > 50 ? msg.text.substring(0, 50) + "..." : msg.text}", ' + 'time: ${msg.timestamp}, ' + 'hasImage: ${msg.hasImage}, ' + 'imagePath: ${msg.imagePath ?? "无"}, ' + 'meta: ${msg.meta != null ? "有" : "无"}' + '}'); + } + Logger.d(TAG, '===== 历史消息详情结束 ====='); + } + + // 设置滚动监听器 + void _setupScrollListener() { + scrollController.addListener(() { + // 检查是否滚动到顶部(因为使用了reverse: true,顶部实际是最大滚动位置) + if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 100) { + // 当距离顶部100像素时触发加载更多 + if (hasMoreMessages.value && !isLoadingMore.value) { + Logger.d(TAG, '触发加载更多历史消息'); + loadMoreMessages(); + } + } + }); + } + + // 手动触发加载更多(用于测试或手动调用) + void triggerLoadMore() { + if (hasMoreMessages.value && !isLoadingMore.value) { + Logger.i(TAG, '手动触发加载更多历史消息'); + loadMoreMessages(); + } else { + Logger.w(TAG, '无法加载更多:hasMoreMessages=${hasMoreMessages.value}, isLoadingMore=${isLoadingMore.value}'); + } + } + + // 重置分页状态(用于刷新聊天记录) + void resetPagination() { + currentPage.value = 1; + hasMoreMessages.value = true; + isLoadingMore.value = false; + isLoadingMoreError.value = false; + Logger.i(TAG, '分页状态已重置'); + } + // 添加初始欢迎消息 void _addWelcomeMessage() { messages.add(Message( diff --git a/lib/modules/agent/views/agent_view.dart b/lib/modules/agent/views/agent_view.dart index 013166031..703aded40 100644 --- a/lib/modules/agent/views/agent_view.dart +++ b/lib/modules/agent/views/agent_view.dart @@ -200,8 +200,13 @@ class _AgentViewState extends State with WidgetsBindingObserver { reverse: true, padding: const EdgeInsets.symmetric( vertical: 16, horizontal: 16), - itemCount: controller.messages.length, + itemCount: controller.messages.length + (controller.hasMoreMessages.value ? 1 : 0), itemBuilder: (context, index) { + // 如果是最后一项且有更多消息,显示加载指示器 + if (index == controller.messages.length && controller.hasMoreMessages.value) { + return _buildLoadMoreIndicator(); + } + final message = controller .messages[controller.messages.length - 1 - index]; return Padding( @@ -1122,4 +1127,80 @@ class _AgentViewState extends State with WidgetsBindingObserver { ), ); } + + // 构建加载更多指示器 + Widget _buildLoadMoreIndicator() { + return Obx(() { + if (controller.isLoadingMore.value) { + // 正在加载状态 + return Container( + padding: const EdgeInsets.symmetric(vertical: 16), + child: const Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + SizedBox(width: 8), + Text( + '加载更多消息...', + style: TextStyle( + fontSize: 14, + color: Colors.grey, + ), + ), + ], + ), + ), + ); + } else if (controller.isLoadingMoreError.value) { + // 加载失败状态 + return Container( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Center( + child: GestureDetector( + onTap: () => controller.loadMoreMessages(), + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.refresh, + size: 16, + color: Colors.grey, + ), + SizedBox(width: 8), + Text( + '加载失败,点击重试', + style: TextStyle( + fontSize: 14, + color: Colors.grey, + ), + ), + ], + ), + ), + ), + ); + } else { + // 默认状态(有更多消息但未加载) + return Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: const Center( + child: Text( + '向上滑动查看更多', + style: TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ), + ), + ); + } + }); + } }