Files
DevFlow/src/composables/useConfirm.ts
绝尘 2ddd2940cc 修复: CR-31 useConfirm 并发覆盖 resolve+ToolCardList 对称清 expandedTools
①useConfirm confirmDialog 进入时先 resolve?.(false) 打断前一个(连点删除/confirm 交错时前一个 Promise 永挂,resolve 被覆盖静默中断+内存泄漏),视为取消;②ToolCardList collapseInactive 对称过滤 expandedTools(原只清 expandedCards,expandedTools 残留致已完成卡片折叠后内容展开态)。cron 巡检捕获走查第8轮 CR-31,vue-tsc 0
2026-06-15 09:17:21 +08:00

54 lines
2.0 KiB
TypeScript

//! 统一确认弹层状态机 — 抽自 Projects/ProjectDetail/Ideas/Settings 四处重复
//!
//! 背景:原生 window.confirm 在 Tauri webview 带 localhost:port 来源信息无法去除,
//! 改为自建弹层。四视图各自重复一份 {visible,msg,resolve} + Promise 机制,此处收敛。
//!
//! 分工:本 composable 只管「状态 + Promise」,不渲染 UI ——
//! - Projects/ProjectDetail/Ideas 复用 ../components/ConfirmDialog.vue
//! - Settings 用内联模板(沿用其既有 toast/confirm 同框布局)
//! 各视图绑定 confirmState 的 visible/msg 并把按钮点击回 answerConfirm 即可。
//!
//! i18n:composable 在 setup 内调用,文案由调用方用各自 useI18n().t 翻译后传入 msg,
//! 这里无需 i18n 依赖。dangerLabel 由 ConfirmDialog 组件侧兜底为 common.delete。
import { ref } from 'vue'
export interface ConfirmState {
visible: boolean
msg: string
resolve: null | ((v: boolean) => void)
}
/** 弹出确认对话框,返回 Promise;resolve(true) 表示用户确认,resolve(false) 表示取消 */
export type ConfirmFn = (msg: string) => Promise<boolean>
/** 关闭弹层并回传结果 */
export type AnswerFn = (ok: boolean) => void
export function useConfirm() {
const confirmState = ref<ConfirmState>({
visible: false,
msg: '',
resolve: null,
})
function confirmDialog(msg: string): Promise<boolean> {
return new Promise(resolve => {
// CR-260615-31:并发覆盖前一个 confirmDialog(连点删除/confirm 交错)时,
// 前一个 Promise 永挂(resolve 被覆盖)→ 先 resolve?.(false) 打断前一个视为取消
confirmState.value.resolve?.(false)
confirmState.value.msg = msg
confirmState.value.visible = true
confirmState.value.resolve = resolve
})
}
function answerConfirm(ok: boolean) {
confirmState.value.visible = false
confirmState.value.resolve?.(ok)
confirmState.value.resolve = null
}
return { confirmState, confirmDialog, answerConfirm }
}