优化: 批A前端(timer所有权+会话族+审批conv-scoped+FilePreview+失败反馈)
A1-B1 timer所有权:新建 useTimerOwnership composable(每实例独立ref+onUnmounted清理)+ useToast _timer 下沉 + Ideas debounce 补清理(治跨实例串扰) A1-B2 FilePreview:reqSeq 双计数器守卫(loadFile/loadDiff 最新seq才写,防乱序覆盖)+ mermaid securityLevel strict + filePath 比对(防跨文件SVG注入) A1-B3 会话族:load_more 滚顶加载接线(switch透传has_more/earliest_seq+prepend去重+scrollTop恢复)+ switch失败保留视图+报错(仅对话不存在才create-new)+ new/delete失败反馈(withConvOp helper收敛)+ delete清ai_messages孤儿 A1-B5前端契约:isToolFailure 三处加 success===false 判定(useToolCard/ToolResultBody/ToolCard,git只读失败不再绿框) A2-B10 审批conv-scoped:pendingApprovals 补conversationId + cleanup 按convId filter + 移除全局清(AiError不再误清其他conv)+ :676 dir auth filter方向修 usage打标前端:is_estimated 字段+事件透传+MessageList『估算』角标(详情面板说明) A2-B9前端:clearChat try/catch 错误气泡
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
//! 每实例独立的定时器所有权 composable。
|
||||
//!
|
||||
//! 背景:模块级/组件级 let timer 单例共享(如 useToast 原模块级 _timer)会跨实例串扰 ——
|
||||
//! 组件 B 的 showToast/onUnmounted 清掉组件 A 的 timer,致 A 的 toast 永不隐藏;
|
||||
//! 组件卸载后 timer 仍可能触发(向单例 store 写入,setState-after-unmount)。
|
||||
//!
|
||||
//! 分工:本 composable 为每个调用实例持有独立的 timeout/interval 注册表,
|
||||
//! onUnmounted 自动全清,互不影响。setOwnTimeout 触发后自动从注册表移除(一次性)。
|
||||
//! 适用:toast 自动隐藏 / keyword 防抖 / FilePreview 等后续 debounce 场景。
|
||||
//!
|
||||
//! 注意:仅在组件 setup 内调用(内部依赖 onUnmounted 生命周期钩子)。
|
||||
|
||||
import { onUnmounted } from 'vue'
|
||||
|
||||
export function useTimerOwnership() {
|
||||
const timeouts = new Set<ReturnType<typeof setTimeout>>()
|
||||
const intervals = new Set<ReturnType<typeof setInterval>>()
|
||||
|
||||
/** 注册一次性 timeout;触发后自动从注册表移除。返回可交给 clearOwn 的 id。 */
|
||||
function setOwnTimeout(fn: () => void, ms: number) {
|
||||
const id = setTimeout(() => {
|
||||
timeouts.delete(id)
|
||||
fn()
|
||||
}, ms)
|
||||
timeouts.add(id)
|
||||
return id
|
||||
}
|
||||
|
||||
/** 注册重复 interval;onUnmounted 自动清理。返回可交给 clearOwnInterval 的 id。 */
|
||||
function setOwnInterval(fn: () => void, ms: number) {
|
||||
const id = setInterval(fn, ms)
|
||||
intervals.add(id)
|
||||
return id
|
||||
}
|
||||
|
||||
/** 清理指定 timeout(id 为 null/undefined 时 no-op)。 */
|
||||
function clearOwn(id?: ReturnType<typeof setTimeout> | null) {
|
||||
if (id != null) {
|
||||
clearTimeout(id)
|
||||
timeouts.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
/** 清理指定 interval(id 为 null/undefined 时 no-op)。 */
|
||||
function clearOwnInterval(id?: ReturnType<typeof setInterval> | null) {
|
||||
if (id != null) {
|
||||
clearInterval(id)
|
||||
intervals.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
timeouts.forEach(clearTimeout)
|
||||
intervals.forEach(clearInterval)
|
||||
timeouts.clear()
|
||||
intervals.clear()
|
||||
})
|
||||
|
||||
return { setOwnTimeout, setOwnInterval, clearOwn, clearOwnInterval }
|
||||
}
|
||||
Reference in New Issue
Block a user