修复: aichat 切换/新建对话缺陷(队列串会话/回切丢消息/审批误终态化/删除复活/切换链路)
This commit is contained in:
+29
-20
@@ -66,18 +66,18 @@
|
||||
path 挂起归一进 ToolCard 内联审批(单真相源:状态层合);关(false,兜底回退)时挂载
|
||||
DirAuthDialog 独立弹窗老链路。两路共存,appSettings 随时回退无需改代码。 -->
|
||||
<DirAuthDialog v-if="!unifiedApproval" @toast="(p) => showToast(p.msg, p.type)" />
|
||||
<!-- 待发送队列(生成中排队的消息,完成后自动续发) -->
|
||||
<div v-if="store.state.queue.length > 0" class="ai-queue" :class="{ 'ai-queue--timeout': queueTimedOut }">
|
||||
<!-- 待发送队列(生成中排队的消息,完成后自动续发;仅显示当前活跃会话的条目) -->
|
||||
<div v-if="activeQueue.length > 0" class="ai-queue" :class="{ 'ai-queue--timeout': queueTimedOut }">
|
||||
<div class="ai-queue-head">
|
||||
<span class="ai-queue-title">{{ $t('aiChat.queueTitle', { n: store.state.queue.length }) }}<span v-if="queueWaitSeconds >= 0" class="ai-queue-timer">({{ queueWaitSeconds }}s)</span></span>
|
||||
<span class="ai-queue-title">{{ $t('aiChat.queueTitle', { n: activeQueue.length }) }}<span v-if="queueWaitSeconds >= 0" class="ai-queue-timer">({{ queueWaitSeconds }}s)</span></span>
|
||||
<div class="ai-queue-actions">
|
||||
<button v-if="queueTimedOut" class="ai-queue-force" @click="handleForceSend">{{ $t('ai.forceSendBtn') }}</button>
|
||||
<button class="ai-queue-clear" @click="store.clearQueue">{{ $t('aiChat.clearQueue') }}</button>
|
||||
<button class="ai-queue-clear" @click="store.clearQueue(store.state.activeConversationId)">{{ $t('aiChat.clearQueue') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-queue-list">
|
||||
<div v-for="(item, idx) in store.state.queue" :key="idx" class="ai-queue-item">
|
||||
<!-- UX-260616-06 决策只编 text:编辑态切 inline input,回车/失焦写回,ESC 取消 -->
|
||||
<div v-for="(item, idx) in activeQueue" :key="idx" class="ai-queue-item">
|
||||
<!-- 编辑态只编 text:inline input,回车/失焦写回,ESC 取消 -->
|
||||
<template v-if="editingQueueIdx === idx">
|
||||
<span v-if="item.skill" class="ai-queue-skill">/{{ item.skill }}</span>
|
||||
<input
|
||||
@@ -94,9 +94,9 @@
|
||||
<template v-else>
|
||||
<span v-if="item.skill" class="ai-queue-skill">/{{ item.skill }}</span>
|
||||
<span class="ai-queue-text">{{ item.text }}</span>
|
||||
<!-- UX-260616-06:编辑按钮 -->
|
||||
<!-- 编辑按钮 -->
|
||||
<button class="ai-queue-btn" @click="startEditQueued(idx)" :title="$t('aiChat.queueEdit')">{{ $t('aiChat.queueEdit') }}</button>
|
||||
<!-- UX-260616-07 决策 a:立即发送(打断当前+发本条) -->
|
||||
<!-- 立即发送(打断当前+发本条) -->
|
||||
<button class="ai-queue-btn" @click="handleSendQueuedNow(idx)" :title="$t('aiChat.queueSendNow')">{{ $t('aiChat.queueSendNow') }}</button>
|
||||
<button class="ai-queue-x" @click="store.cancelQueued(idx)" :title="$t('common.cancel')">×</button>
|
||||
</template>
|
||||
@@ -385,33 +385,40 @@ function onBeforeUnloadGuard(e: BeforeUnloadEvent) {
|
||||
// 第二批抽离: handleKeydown/autoResize/handleSend 已迁移至 ChatInput.vue
|
||||
// (ChatInput 经 emit('sent')/emit('cancel-edit')/emit('error') 与父交互)
|
||||
|
||||
// ── B-260616-02 L2 发送韧性:排队超时检测 + 强制发送 ──
|
||||
// ── L2 发送韧性:排队超时检测 + 强制发送 ──
|
||||
|
||||
/** 队首排队是否已超 30s */
|
||||
const queueTimedOut = computed(() => store.isQueueTimedOut())
|
||||
/** 当前活跃会话的待发送队列(按 activeConversationId 过滤)。
|
||||
* 队列是全局数组,多会话并发可各自排队;旧实现渲染全部会话的 queue,切/新建后会显示
|
||||
* 旧会话排队消息。此处仅显当前活跃会话条目,相关操作(编辑/删除/立即发送/清空/超时)均按此收敛。 */
|
||||
const activeQueue = computed(() =>
|
||||
store.state.queue.filter(q => q.conversationId === store.state.activeConversationId),
|
||||
)
|
||||
|
||||
/** 队首排队是否已超 30s(仅看当前活跃会话队列) */
|
||||
const queueTimedOut = computed(() => store.isQueueTimedOut(store.state.activeConversationId))
|
||||
|
||||
/** 队首已排队秒数(用于 UI 展示倒计时;无队列时 -1) */
|
||||
const queueWaitSeconds = computed(() => {
|
||||
const first = store.state.queue[0]
|
||||
const first = activeQueue.value[0]
|
||||
if (!first) return -1
|
||||
return Math.floor((Date.now() - first.enqueuedAt) / 1000)
|
||||
})
|
||||
|
||||
/** 强制发送:委托 tryForceSend 唯一负责弹 confirm + shift 队列 + force_send */
|
||||
/** 强制发送:委托 tryForceSend 唯一负责弹 confirm + 移除队列 + force_send(作用于当前活跃会话) */
|
||||
async function handleForceSend() {
|
||||
await store.tryForceSend(confirmDialog)
|
||||
await store.tryForceSend(confirmDialog, store.state.activeConversationId)
|
||||
}
|
||||
|
||||
// ── UX-260616-06 队列消息编辑(决策只编 text)+ UX-260616-07 立即发送(决策 a 插队)──
|
||||
// ── 队列消息编辑(只编 text)+ 立即发送(插队)──
|
||||
|
||||
/** 当前正在编辑的队列项 index;null=非编辑态 */
|
||||
const editingQueueIdx = ref<number | null>(null)
|
||||
/** 编辑态输入框文本(回车写回 / ESC 取消还原) */
|
||||
const editingQueueText = ref('')
|
||||
|
||||
/** 进入编辑态:记录 index + 快照当前 text */
|
||||
/** 进入编辑态:记录 index + 快照当前 text(activeQueue 视图内下标) */
|
||||
function startEditQueued(idx: number) {
|
||||
const item = store.state.queue[idx]
|
||||
const item = activeQueue.value[idx]
|
||||
if (!item) return
|
||||
editingQueueIdx.value = idx
|
||||
editingQueueText.value = item.text
|
||||
@@ -431,7 +438,7 @@ function saveEditQueued() {
|
||||
editingQueueText.value = ''
|
||||
}
|
||||
|
||||
/** UX-260616-07 立即发送:委托 store.sendQueuedNow(splice+stop+sendMessage) */
|
||||
/** 立即发送:委托 store.sendQueuedNow(移除+stop+sendMessage) */
|
||||
async function handleSendQueuedNow(idx: number) {
|
||||
await store.sendQueuedNow(idx)
|
||||
}
|
||||
@@ -620,7 +627,8 @@ async function initContextEventListeners(): Promise<void> {
|
||||
// 后端 emit ai_context_cleared 后会重置 session.messages,前端刷对话拉最新状态。
|
||||
// 守卫:事件到达前用户可能切走,activeConversationId 变 null,跳过刷新避免空指针。
|
||||
if (store.state.activeConversationId) {
|
||||
void store.switchConversation(store.state.activeConversationId)
|
||||
// force=true:同会话刷新(同会话点击短路对程序化刷新放行,清空后需重拉最新上下文)
|
||||
void store.switchConversation(store.state.activeConversationId, true)
|
||||
}
|
||||
},
|
||||
// 治 Task#1:仅手动压缩触发(useAiContext 的 AiManualCompressed case 调此回调);
|
||||
@@ -629,7 +637,8 @@ async function initContextEventListeners(): Promise<void> {
|
||||
showToast(t('aiChat.compressSuccess'), 'info')
|
||||
// 刷新对话(历史消息落库标 compressed + system 摘要消息回填到视图)
|
||||
if (store.state.activeConversationId) {
|
||||
void store.switchConversation(store.state.activeConversationId)
|
||||
// force=true:同会话刷新(压缩后需重拉最新消息视图)
|
||||
void store.switchConversation(store.state.activeConversationId, true)
|
||||
}
|
||||
},
|
||||
onError: (_convId, message) => {
|
||||
|
||||
Reference in New Issue
Block a user