e80a82bef4
- 更新 http-error-codes, user-points-chat-data-protocol - 更新 divination-run-protocol, profile-protocol - 删除废弃的后端和前端设计计划文档
7.6 KiB
7.6 KiB
Bug: 追问页面 UI 问题
日期:2026-04-08 状态:已确认(未修复)
Bug 1: 追问页发送时顶部和消息下方重复加载UI
问题描述
在追问页面,用户输入信息发送后,顶部和消息下方同时出现加载UI,但顶部的加载UI是多余的。
根因分析
文件:apps/lib/features/divination/presentation/screens/follow_up_chat_screen.dart
问题代码:
- 顶部 step 指示器(第 85-124 行):
if (_sending && _currentStepName != null)
Container(
// 显示 step 进度,如 "解读中..."、"推理中..."
child: Row(
children: [
CircularProgressIndicator(...),
Text(_stepLabel(_currentStepName!)),
],
),
)
- 消息下方 streaming placeholder(第 565-584 行):
isStreamingPlaceholder
? Row(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(...),
Text(l10n.followUpGenerating), // "生成中..."
],
)
事件触发流程:
| 顺序 | 事件 | 顶部指示器 | 消息下方 |
|---|---|---|---|
| 1 | _submitText() 调用 |
无 | streaming placeholder 显示 "生成中..." |
| 2 | STEP_STARTED(stepName='divination') |
显示 "解读中..." | 继续显示 |
| 3 | STEP_FINISHED |
消失 | 继续显示 |
| 4 | STEP_STARTED(stepName='worker') |
显示 "推理中..." | 同时显示 "生成中..." |
| 5 | STEP_FINISHED |
消失 | 继续显示 |
| 6 | TEXT_MESSAGE_END |
无 | placeholder 消失,显示真实内容 |
问题:在 worker 阶段(步骤 4),顶部显示 "推理中..." 同时消息下方显示 "生成中...",造成重复反馈。用户只需要一个加载反馈即可。
修复方向:
-
方案A:只在 divination 阶段显示顶部 step 指示器,worker 阶段隐藏(因为 streaming placeholder 已经提供反馈)
// 第 85 行修改 if (_sending && _currentStepName != null && _currentStepName != 'worker') -
方案B:移除顶部 step 指示器,完全依赖 streaming placeholder
-
方案C:在 worker 阶段隐藏 streaming placeholder,只显示顶部指示器
推荐 方案A,因为:
- divination 阶段没有 streaming placeholder,需要顶部指示器提供反馈
- worker 阶段有 streaming placeholder,不需要顶部指示器
- 改动最小,只加一个条件
Bug 2: 语音模式录制时缺少动画UI
问题描述
根据 social-app 的实现,按住说话模式下录音时应该有动画效果,但 eryao 的实现只有文字,没有动画。
根因分析
对比:
| 功能 | social-app | eryao |
|---|---|---|
| 录音中动画 | recordingAnimation widget(外部传入) |
只有文字 "录音中..." |
| 录音提示文字 | 带动画的 widget | 只有文字 |
| 录音中隐藏输入区域 | IgnorePointer + Opacity |
未实现 |
eryao 当前实现(message_composer.dart:159-172):
if (_isRecording) {
if (!showRecordingInlineFeedback) {
return Text(recordingText, ...);
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 16, height: 16), // 空白占位
const SizedBox(height: AppSpacing.xs),
Text(recordingText, ...), // "录音中..."
const SizedBox(height: AppSpacing.xs),
Text(recordingHintText, ...), // "上滑取消"
],
);
}
social-app 实现(message_composer.dart:222-239):
if (_isRecording) {
if (!showRecordingInlineFeedback) {
return Text(resolvedRecordingText, ...);
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
recordingAnimation, // 动画widget(外部传入)
const SizedBox(height: AppSpacing.xs),
Text(resolvedRecordingText, ...),
const SizedBox(height: AppSpacing.xs),
Text(resolvedRecordingHintText, ...),
],
);
}
差异:
recordingAnimationwidget 缺失 - 需要外部传入,eryao 直接用空白占位- 录音中图标/按钮没有动画效果
修复方向:
- 在
FollowUpChatScreen或其父组件中创建recordingAnimationwidget - 将
recordingAnimation通过MessageComposer传入 - 参考 social-app 的实现,使用脉冲动画或波形动画
参考实现(需要添加 recording_animation.dart):
class RecordingAnimation extends StatefulWidget {
final double size;
final Color color;
@override
State<RecordingAnimation> createState() => _RecordingAnimationState();
}
class _RecordingAnimationState extends State<RecordingAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
)..repeat(reverse: true);
_scaleAnimation = Tween<double>(begin: 1.0, end: 1.2).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _scaleAnimation,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Container(
width: widget.size,
height: widget.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.color.withValues(alpha: 0.3),
),
child: Icon(Icons.mic, color: widget.color, size: widget.size * 0.6),
),
);
},
);
}
}
Bug 2 & 3: 直接复用 social-app 输入框(不需要 plus 按钮)
需求
直接复用 social-app 的 MessageComposer,但不需要 plus 按钮。
复用清单
| 功能 | social-app | eryao | 处理 |
|---|---|---|---|
recordingAnimation |
required Widget | 空白占位 | 需要添加 |
| 双层阴影 | 有 | 无 | 需要添加 |
AppRadius.xxl |
用于圆角 | 无此值 | 需要添加 |
| Plus 按钮 | 有 | 无 | 不需要,保持无 |
| 图标 | LucideIcons |
Material Icons | 保持 Material Icons |
实现步骤
1. 添加 AppRadius.xxl 到 design_tokens.dart
class AppRadius {
static const double sm = 8;
static const double md = 12;
static const double lg = 16;
static const double xl = 20;
static const double xxl = 32; // 新增
static const double full = 999;
}
2. 创建 RecordingAnimation widget
放在 apps/lib/shared/widgets/ 下,参考 social-app 的脉冲动画效果。
3. 修改 MessageComposer
- 添加
recordingAnimation参数(required) - 添加双层阴影
- 使用
AppRadius.xxl替代AppRadius.full - 移除 plus 按钮(eryao 原有的无 plus 逻辑保持不变)
- 保持 Material Icons
4. 修改 FollowUpChatScreen
- 创建
RecordingAnimation实例 - 传入
MessageComposer
相关文件
apps/lib/features/divination/presentation/screens/follow_up_chat_screen.dartapps/lib/shared/widgets/message_composer.dartapps/lib/shared/widgets/recording_animation.dart(新建)apps/lib/shared/theme/design_tokens.dart/home/qzl/Code/social-app/apps/lib/shared/widgets/message_composer.dart
修复优先级
- 高优先级:Bug 1(重复加载UI)
- 高优先级:Bug 2 & 3(直接复用 social-app 输入框)