fix: 修复 agents 页面侧边栏切回后自动关闭

This commit is contained in:
Wenjie Zhang 2026-04-20 23:55:51 +08:00
parent ccf0d98eb6
commit cd47227888
2 changed files with 88 additions and 37 deletions

View File

@ -57,6 +57,7 @@
- 调整对话配置入口与侧边栏头尾交互:输入区配置按钮改为轻量 dropdown 触发器默认保持透明、hover 才出现背景;下拉中直接提供配置切换、新建配置,以及查看/编辑当前配置的入口。配置侧边栏则移除头部配置切换和新建入口,改为仅显示当前配置名称、可点击星标和关闭图标,并将删除操作下沉到底部保存按钮右侧。
- 为历史线程补充前端本地配置变更提示:当已有历史消息的对话中切换 Agent、切换配置或编辑任意配置项时在聊天消息流中插入一条非持久化的信息提示提醒继续在当前线程运行可能影响最终效果建议新建对话提示仅保留在前端当前线程状态中不写入后端历史消息。后续补齐了 3 个关键边界:提示插入时主动滚动到底部确保可见、历史消息尚未加载完成时先挂起提示并在加载完成后落地、流式回复过程中切换配置时将提示锚定在当时完整对话流之后,避免把正在生成的回复错误归因到新配置。
- 调整 Worker run 模式下的消息首屏反馈:前端发送消息时先乐观渲染用户消息,再将前端生成的 `request_id` 透传给 `/api/chat/runs` 与服务端 `init` 对账;“正在生成回复”动画改为仅在收到 `init` 确认后展示,修复 worker 轮询延迟导致的先转圈、后出现用户消息的问题。后续又补齐了线程切换场景:后端持久化 user message 时同步写入 `request_id`,前端在合并历史消息与 ongoing 消息时按 `request_id` 去掉重复的首条 human message避免切回运行中的线程后出现连续两个 user message。
- 修复 agents 页对话侧边栏在 `keep-alive` 路由切换后的误关闭问题:`AgentChatComponent` 的宽度监听现在会在页面 deactivated 时断开、activated 时重连,并忽略隐藏态的 `0` 宽度,避免从其他页面切回 `/agent` 时把用户持久化的侧边栏打开状态错误覆盖为关闭。
---

View File

@ -281,7 +281,18 @@
</template>
<script setup>
import { ref, reactive, onMounted, watch, nextTick, computed, onUnmounted, h } from 'vue'
import {
ref,
reactive,
onMounted,
watch,
nextTick,
computed,
onUnmounted,
onActivated,
onDeactivated,
h
} from 'vue'
import { message } from 'ant-design-vue'
import AgentInputArea from '@/components/AgentInputArea.vue'
import AgentMessageComponent from '@/components/AgentMessageComponent.vue'
@ -902,6 +913,69 @@ const isSidebarFloating = ref(false)
let chatMainResizeObserver = null
// ResizeObserver
let isResizeObserverReady = false
let resizeObserverReadyTimer = null
const armResizeObserver = () => {
if (resizeObserverReadyTimer) {
clearTimeout(resizeObserverReadyTimer)
}
isResizeObserverReady = false
// keep-alive
resizeObserverReadyTimer = setTimeout(() => {
isResizeObserverReady = true
}, 50)
}
const stopChatMainResizeObserver = () => {
if (resizeObserverReadyTimer) {
clearTimeout(resizeObserverReadyTimer)
resizeObserverReadyTimer = null
}
isResizeObserverReady = false
if (chatMainResizeObserver) {
chatMainResizeObserver.disconnect()
chatMainResizeObserver = null
}
}
const startChatMainResizeObserver = () => {
if (!window.ResizeObserver || !chatMainRef.value || chatMainResizeObserver) {
return
}
localUIState.chatMainWidth = chatMainRef.value.clientWidth || window.innerWidth
chatMainResizeObserver = new ResizeObserver((entries) => {
// layout
if (!isResizeObserverReady) return
for (const entry of entries) {
const width = entry.contentRect.width
if (!width) continue
localUIState.chatMainWidth = width
const isTakingSpace = chatUIStore.isSidebarOpen && !isSidebarFloating.value
if (isTakingSpace) {
if (width < 600) {
isSidebarFloating.value = true
chatUIStore.isSidebarOpen = false
localStorage.setItem('chat_sidebar_open', 'false')
}
} else {
if (width >= 880) {
isSidebarFloating.value = false
} else {
isSidebarFloating.value = true
}
}
}
})
chatMainResizeObserver.observe(chatMainRef.value)
armResizeObserver()
}
onMounted(() => {
nextTick(() => {
@ -910,50 +984,26 @@ onMounted(() => {
chatMainContainer.addEventListener('scroll', scrollController.handleScroll, { passive: true })
}
if (window.ResizeObserver && chatMainRef.value) {
localUIState.chatMainWidth = chatMainRef.value.clientWidth || window.innerWidth
chatMainResizeObserver = new ResizeObserver((entries) => {
// layout
if (!isResizeObserverReady) return
for (const entry of entries) {
const width = entry.contentRect.width
localUIState.chatMainWidth = width
const isTakingSpace = chatUIStore.isSidebarOpen && !isSidebarFloating.value
if (isTakingSpace) {
if (width < 600) {
isSidebarFloating.value = true
chatUIStore.isSidebarOpen = false
localStorage.setItem('chat_sidebar_open', 'false')
}
} else {
if (width >= 880) {
isSidebarFloating.value = false
} else {
isSidebarFloating.value = true
}
}
}
})
chatMainResizeObserver.observe(chatMainRef.value)
}
// 50ms ResizeObserver layout
setTimeout(() => {
isResizeObserverReady = true
}, 50)
startChatMainResizeObserver()
})
setTimeout(() => {
localUIState.isInitialRender = false
}, 300)
})
onActivated(() => {
nextTick(() => {
startChatMainResizeObserver()
})
})
onDeactivated(() => {
stopChatMainResizeObserver()
})
onUnmounted(() => {
scrollController.cleanup()
if (chatMainResizeObserver) {
chatMainResizeObserver.disconnect()
}
stopChatMainResizeObserver()
if (sendCooldownTimer) {
clearTimeout(sendCooldownTimer)
sendCooldownTimer = null