优化: 界面交互完善与设置体验

- 审批卡尊重滚动意图,等长切换回底,排队超时按钮修活
- 带图编辑提示不静默丢,删除目标回写,切换文案消歧
- 会话键盘可达+搜索态重命名,筛选计数口径对齐
- 子任务提示正名,看板加载态
- 主题跟随系统,表单脏态拦截,密钥显隐留空提示
This commit is contained in:
lxy
2026-10-03 16:28:38 +08:00
parent 31f7a9570b
commit 3e3c6f4d0f
22 changed files with 294 additions and 12 deletions
+38 -1
View File
@@ -65,6 +65,11 @@ const emit = defineEmits<{
* 调 store.sendMessage 触发 AI 装流式。仅 fetch_url 工具会发此事件。
*/
'install-obscura': []
/**
* 审批卡到达但用户不在底部附近(上滚读历史中):不强拉滚动,
* 由父(MessageList)出「有新审批」轻提示,尊重用户阅读位置。
*/
'pending-out-of-view': []
}>()
// 根元素 ref(scrollToFirstPending 查 DOM 用)
@@ -306,6 +311,28 @@ let _unlistenPending: UnlistenFn | null = null
// 论证 workflow 回归中优:防卸载竞态——await listen 解析前组件已卸载(HMR/快速切会话)时,
// onBeforeUnmount 跑到 _unlistenPending 仍 null 跳过 unlisten → 孤儿监听器泄漏。disposed 标志兜底。
let _disposed = false
/** 距滚动容器底部 <150px 视为"在底部附近",审批卡到达才跟随滚动(宽于 useMessageScroll 的 80px 在底判定) */
const PENDING_FOLLOW_PX = 150
/** 轻提示去重阈值:审批卡短时间连发多条时 5s 内只提示一次 */
const PENDING_HINT_INTERVAL_MS = 5000
// 模块级(跨实例共享):ai-pending-arrived 广播所有 ToolCardList 实例都会收到,去重须超组件实例
let _lastPendingHintTs = 0
/** 滚动容器(.ai-messages,MessageList 持有)是否在底部附近 */
function isContainerNearBottom(el: Element): boolean {
const he = el as HTMLElement
return he.scrollHeight - he.scrollTop - he.clientHeight < PENDING_FOLLOW_PX
}
/** 用户不在底部时的轻提示:不拉滚,经父转发 toast(5s 去重防连发刷屏) */
function notifyPendingOutOfView(): void {
const now = Date.now()
if (now - _lastPendingHintTs < PENDING_HINT_INTERVAL_MS) return
_lastPendingHintTs = now
emit('pending-out-of-view')
}
onMounted(async () => {
const fn = await listen('ai-pending-arrived', () => {
// BUG-260624-01 残留优化:本实例无 pending 卡时短路返回,避免大列表 N 个 ToolCardList 实例
@@ -313,7 +340,17 @@ onMounted(async () => {
// 内 querySelector 返 null no-op 天然过滤(功能正确),此处显式 props 过滤把无 pending 实例挡在
// 进入函数前。含 pending 的实例再走 nextTick + scrollToFirstPending(展开折叠组 + scroll)。
if (!props.toolCalls.some(tc => tc.status === 'pending_approval')) return
nextTick(() => scrollToFirstPending())
nextTick(() => {
// UX 走查-对话域 B1:尊重用户滚动意图。nextTick 等审批卡渲染完再测距底距离:
// 在底部附近(含本就贴底)才跟随滚到审批卡;用户上滚读历史时不强拉,改轻提示。
// 容器找不到(极端:分离窗口 DOM 层级变化)按可滚处理,保留原展开+滚动兜底。
const container = rootEl.value?.closest('.ai-messages')
if (!container || isContainerNearBottom(container)) {
scrollToFirstPending()
} else {
notifyPendingOutOfView()
}
})
})
// 解析时若已卸载(HMR/极速 mount/unmount),立即 unlisten 刚拿到的句柄,防孤儿监听器。
if (_disposed) { fn(); return }