优化: 会话状态键控与提示补全

- 队列清空/发送失败补提示,错误接入分类文案
- 审批超时气泡按会话归属,流守卫按会话清理
- 会话操作失败就近反馈,拖拽非法流转提示原因
This commit is contained in:
lxy
2026-10-03 16:27:39 +08:00
parent 8710a09be2
commit 31f7a9570b
6 changed files with 118 additions and 26 deletions
+21 -4
View File
@@ -24,7 +24,16 @@ import { t } from '@/i18n/i18n-helpers'
import { resetStreamWatchdog, clearStreamWatchdog, clearResumeAttempts } from './useAiStream'
import { setStreaming } from './streamingGuard'
import { startListener } from './useAiEvents'
import { nextMsgId, startApprovalTimer, clearApprovalTimer, clearAllApprovalTimers, resolveAiLang, convStates, setConvCurrentText, setConvStreaming, flushCurrentText } from './aiShared'
import { nextMsgId, startApprovalTimer, clearApprovalTimer, clearAllApprovalTimers, resolveAiLang, convStates, setConvCurrentText, setConvStreaming, flushCurrentText, notifyQueueCleared, classifySendError, type SendErrorKind } from './aiShared'
/// UX-P2-A5:drainQueue 失败气泡的错误分类文案映射(分类逻辑 aiShared.classifySendError,
/// 键复用 ChatInput 发送失败 toast 同一批 aiChat.sendErr* 文案,原始错误经 { msg } 细节保留)
const QUEUED_SEND_ERR_KEY: Record<SendErrorKind, string> = {
rate_limit: 'aiChat.sendErrRateLimit',
quota: 'aiChat.sendErrQuota',
network: 'aiChat.sendErrNetwork',
other: 'aiChat.toastSendFail',
}
/// 待发送队列上限(超过抛错提示用户)
const QUEUE_LIMIT = 10
@@ -163,9 +172,12 @@ async function regenerate() {
setStreaming(true, { convId: state.activeConversationId, reason: 'regenerate' })
state.currentText = ''
// 只清当前会话的排队消息,不误伤其他会话
// 只清当前会话的排队消息,不误伤其他会话;
// UX-P2-A2:清队从静默改 toast 提示条数(排队消息无声消失原无任何反馈)
const regenConvId = state.activeConversationId
const droppedQueued = state.queue.filter(q => q.conversationId === regenConvId).length
state.queue = state.queue.filter(q => q.conversationId !== regenConvId)
notifyQueueCleared(droppedQueued)
// 会话态走 per-conv 键(与 doSend 一致),不再落 legacy 单 timer fallback
resetStreamWatchdog(regenConvId || undefined)
@@ -236,9 +248,12 @@ async function editMessage(newMessage: string) {
setStreaming(true, { convId: state.activeConversationId, reason: 'editMessage' })
state.currentText = ''
// 只清当前会话的排队消息,不误伤其他会话
// 只清当前会话的排队消息,不误伤其他会话;
// UX-P2-A2:清队从静默改 toast 提示条数(排队消息无声消失原无任何反馈)
const editConvId = state.activeConversationId
const droppedQueued = state.queue.filter(q => q.conversationId === editConvId).length
state.queue = state.queue.filter(q => q.conversationId !== editConvId)
notifyQueueCleared(droppedQueued)
// 会话态走 per-conv 键(与 doSend 一致),不再落 legacy 单 timer fallback
resetStreamWatchdog(editConvId || undefined)
@@ -301,10 +316,12 @@ export function drainQueue(convId?: string | null) {
// 仅目标会话为当前视图才推错误气泡(后台会话续发失败不污染当前视图;错误消息留在队列
// 供切回后编辑/重试/取消)
if (targetId === state.activeConversationId) {
// UX-P2-A5:失败气泡接入 classifySendError 分类文案(对齐 ChatInput 发送失败 toast 四类),
// 替代原 raw 错误直出的 ai.queuedSendFailed;原始错误串经 { msg } 细节保留
state.messages.push({
id: `err-${nextMsgId()}`,
role: 'assistant',
content: t('ai.queuedSendFailed', { error: errMsg }),
content: t(QUEUED_SEND_ERR_KEY[classifySendError(errMsg)], { msg: errMsg }),
isError: true,
timestamp: Date.now(),
} as AiMessage)