已添加消息发送状态控制,防止用户在上一条消息处理完成前重复发送。
let isSending = false; // 发送状态标志在 processNaturalLanguage 函数中:
- 发送前检查:如果
isSending = true,直接返回,不处理请求 - 设置发送中状态:开始处理时设置
isSending = true - 更新UI状态:调用
updateSendButtonState()禁用按钮和输入框 - 恢复状态:在
finally块中恢复isSending = false
updateSendButtonState() 函数会:
发送中状态:
- 禁用发送按钮(
disabled = true) - 禁用输入框(
disabled = true) - 按钮半透明显示(
opacity: 0.5) - 输入框半透明显示(
opacity: 0.7) - 鼠标指针变为禁止样式
空闲状态:
- 启用发送按钮和输入框
- 恢复正常透明度
- 恢复正常鼠标指针
document.getElementById('sendBtn').addEventListener('click', async () => {
if (isSending) {
console.log('请等待上一条消息处理完成');
return;
}
// ... 处理逻辑
});document.getElementById('chatInput').addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
if (!isSending) {
document.getElementById('sendBtn').click();
}
}
});❌ 用户可以连续点击发送按钮 ❌ 可以在处理中继续输入并发送 ❌ 可能导致重复请求和混乱的对话顺序 ❌ 没有明显的视觉反馈
✅ 发送按钮在处理中自动禁用 ✅ 输入框在处理中变为只读状态 ✅ 清晰的视觉反馈(半透明、禁止光标) ✅ 控制台日志提示用户等待 ✅ 防止重复请求 ✅ 确保对话顺序正确
用户输入文本
↓
点击发送/按回车
↓
检查 isSending
↓
├─ true → 忽略操作(控制台提示)
↓
└─ false → 继续处理
↓
设置 isSending = true
↓
禁用按钮和输入框
↓
显示 typing indicator
↓
发送 API 请求
↓
等待响应
↓
显示 AI 回复
↓
刷新日历事件
↓
设置 isSending = false(finally 块)
↓
启用按钮和输入框
操作:输入"明天开会",快速点击发送按钮3次
预期:只发送一次,后续点击被忽略
结果:✅ 只有第一次点击生效
操作:输入"查看今天日程",按回车后立即再按回车
预期:第二次回车被忽略
结果:✅ 第二次按键无效
操作:发送消息后,在处理期间尝试输入新内容
预期:输入框禁用,无法输入
结果:✅ 输入框变为只读状态
操作:发送导致错误的请求
预期:即使出错,状态也会恢复
结果:✅ finally 块确保状态恢复
- ✅ 适用于鼠标点击
- ✅ 适用于键盘操作(Enter键)
- ✅ 支持移动端触摸
- ✅ 所有现代浏览器
| 状态 | 发送按钮 | 输入框 | 光标 |
|---|---|---|---|
| 空闲 | 正常显示 | 正常显示 | pointer |
| 发送中 | 半透明(0.5) | 半透明(0.7) | not-allowed |
| 禁用 | disabled | disabled | - |
- 使用 finally 块:确保无论成功或失败都会恢复状态
- 双重检查:在事件监听器和处理函数中都检查状态
- 视觉反馈:通过样式变化告知用户当前状态
- 日志记录:控制台输出帮助调试
- 防止重复提交导致的服务器压力
- 避免竞态条件
- 确保请求按顺序处理
- 保护用户体验
- 添加发送队列,排队处理多个请求
- 显示倒计时或进度条
- 添加取消按钮
- 请求超时后自动恢复状态
- 添加重试机制