Files
DevFlow/src/composables/useTimerOwnership.ts
T
lxy c480627ba6 优化: 批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 错误气泡
2026-08-05 22:10:50 +08:00

61 lines
2.1 KiB
TypeScript

//! 每实例独立的定时器所有权 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 }
}