1、新建工具文件 utils/messageBox.js

// utils/messageBox.js
import {
	ElMessageBox
} from 'element-plus'

export function confirmCountDown(title, message, waitSecond = 5, options = {}) {
	const totalSec = Math.max(1, Number(waitSecond) || 5)
	let timer = null
	let count = totalSec
	let observer = null

	// 销毁清理
	const destroy = () => {
		timer && clearInterval(timer)
		timer = null
		observer && observer.disconnect()
		observer = null
	}

	const boxOpts = {
		cancelButtonText: '取消',
		closeOnClickModal: false,
		beforeClose: (_action, _ins, done) => {
			destroy()
			done()
		},
		...options
	}

	return new Promise((resolve, reject) => {
		const boxPromise = ElMessageBox.confirm(message, title, boxOpts)

		boxPromise
			.then(() => {
				destroy()
				resolve(true)
			})
			.catch(() => {
				destroy()
				reject(false)
			})

		observer = new MutationObserver(() => {
			const confirmBtn = document.querySelector('.el-message-box__btns .el-button--primary')
			if (!confirmBtn) return
			observer.disconnect()

			setTimeout(() => {
				// 封装统一设置禁用状态方法
				const setDisabled = (status) => {
					confirmBtn.disabled = status
					if (status) {
						confirmBtn.classList.add('is-disabled')
					} else {
						confirmBtn.classList.remove('is-disabled')
					}
				}

				// 初始禁用
				setDisabled(true)
				confirmBtn.innerText = `确认(${count}s)`

				timer = setInterval(() => {
					count--
					if (count > 0) {
						setDisabled(true)
						confirmBtn.innerText = `确认(${count}s)`
					} else {
						setDisabled(false)
						confirmBtn.innerText = '确认'
						destroy()
					}
				}, 1000)
			}, 50)
		})

		observer.observe(document.body, {
			childList: true,
			subtree: true
		})
	})
}

2、页面中使用(Vue3 setup)

<template>
  <el-button type="danger" @click="handleDelete">删除数据</el-button>
</template>

<script setup>
import { confirmCountDown } from '@/utils/messageBox'
import { ElMessage } from 'element-plus'

const handleDelete = async () => {
  try {
    // 第二个参数倒计时6秒,第三个可传原生配置
    await confirmCountDown('危险操作', '删除后数据无法恢复,请谨慎操作', 6, {
      type: 'error', // 弹窗图标类型
      closeOnPressEscape: false // 禁止ESC关闭
    })
    // 点击确认且倒计时结束才能走到这里
    ElMessage.success('删除成功')
  } catch {
    ElMessage.info('已取消删除')
  }
}
</script>

3、全局挂载(可选,main.js)

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import { confirmCountDown } from '@/utils/messageBox'

const app = createApp(App)
app.use(ElementPlus)
// 全局挂载
app.config.globalProperties.$confirmCountDown = confirmCountDown
app.mount('#app')

全局调用写法:

const { proxy } = getCurrentInstance()
proxy.$confirmCountDown('提示', '确定提交?', 3)

关键优化细节说明

  1. 用 requestAnimationFrame 替代 setTimeout (0) 更精准等待 DOM 渲染,比宏任务时序更稳定,低帧率页面也不会获取不到按钮
  2. 全场景定时器销毁 beforeClose、then 确认、catch 取消三大分支全部执行 clearTimer,彻底杜绝内存泄漏、多定时器叠加
  3. 数值容错 传入 0、负数、字符串数字都会自动修正为最少 1 秒倒计时
  4. try-catch 包裹 DOM 操作 弹窗销毁瞬间还在执行更新逻辑不会抛红报错
  5. 配置透传 原生 ElMessageBox 所有参数(type、center、input、closeOnPressEscape 等)全部支持自定义传入
  6. Promise 标准格式 then 代表确认,catch 代表所有关闭行为,符合业务常规写法
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐