背景:放置用户误操作,对删除按钮的confirm弹框中,误触回车键,导致删除。
条件:element-plus组件
先来一段视频:
废话不多说:代码如下
this.$confirm(tip, this.$t('public.delete'), {
confirmButtonText: this.$t('public.delete'),
cancelButtonText: this.$t('public.cancel'),
type: 'warning',
closeOnClickModal: false,
confirmButtonClass: 'confirm-btn',
beforeClose: (action, instance, done) => {
// 确认按钮按下时
if (action === 'confirm') {
const confirmButton = document.querySelector('.confirm-btn')
// 监听确认按钮的点击事件
const onClickHandler = event => {
event = event || window.event
// enter键入detail为0,不进入done,阻止关闭;点击确认detail为1,进入done,进入then
if (event.detail !== 0) {
done()
}
}
// 先移除之前可能存在的点击事件处理函数
confirmButton.removeEventListener('click', onClickHandler)
// 再为确认按钮添加新的点击事件处理函数
confirmButton.addEventListener('click', onClickHandler)
onClickHandler()
} else {
// 其他情况,相当于取消,进入catch
done()
}
}
}).then(() => {
this.deleteEmailTemplate(res.id)
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('public.canceledDelete')
})
})
}
官网:MessageBox 消息弹框 | Element Plus
在beforeClose处赋值一个回调,有三个参数action操作类型 instance实例 done,调用done则关闭否则反之
然后用confirmButtonClass定义类名,查询到当前dom节点,并且监听按钮的点击事件,通过event.detail的值可以判断出是手动点击确认还是按下enter触发的,当然如果存在input select等可输入类型的表单按下回车键,可以手动在这边过滤一下;随后移除上一次关闭弹框之后的监听,随后为按钮新增监听,手动触发一次即可。
拓展:全局$confirm如何都实现呢?总不能每个$confirm都去加一遍吧。
vue2小伙伴可以使用Vue.prototype.$confirm 绑定到Vue实例
vue3小伙伴呢可以使用app.config.globalProperties全局挂载属性利用插件把confirm封装‘enter不触发确认操作’的逻辑
// confirm-plugin.js 自定义插件
// custom-confirm-plugin.js
import { ElMessageBox } from 'element-plus'
const ConfirmPlugin = {
install(app) {
// 添加全局方法 $confirm
app.config.globalProperties.$confirm = function (message, title, userOptions = {}) {
// 合并默认配置和用户自定义选项
const defaultOptions = {
confirmButtonClass: 'confirm-btn',
beforeClose: (action, instance, originalDone) => {
if (action === 'confirm') {
const confirmButton = document.querySelector('.confirm-btn')
const onClickHandler = event => {
event = event || window.event
const isInputLikeElement = target => {
const inputTypes = ['textarea', 'input', 'select']
return inputTypes.includes(target.tagName.toLowerCase())
}
if (event.detail !== 0 && !isInputLikeElement(event.target)) {
originalDone() // 点击确认或非回车键时正常关闭
}
}
confirmButton.removeEventListener('click', onClickHandler)
confirmButton.addEventListener('click', onClickHandler)
onClickHandler()
} else {
originalDone()
}
}
}
const finalOptions = { ...defaultOptions, ...userOptions }
// 返回原生的$confirm方法调用,但使用的是合并后的选项
return ElMessageBox.confirm(message, title, finalOptions)
}
}
}
export default ConfirmPlugin
// main.js 注册这个插件
import { createApp} from 'vue'
import App from './App'
import ConfirmPlugin from '@/mixin/confirmPlugin'
...
const app = createApp(App)
app.use(ConfirmPlugin)
...
// 普通页面正常使用$confirm
this.$confirm(tip, this.$t('public.delete'), {
confirmButtonText: this.$t('public.delete'),
cancelButtonText: this.$t('public.cancel'),
type: 'warning',
closeOnClickModal: false,
}).then(() => {
this.deleteEmailTemplate(res.id)
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('public.canceledDelete')
})
})
}
不过vue3不推荐使用gobalProperties,尤其是组合式api不会被所有组件自动注入,所以推荐project/inject 方式将方法注入,然后创建全局mixin再引入上面透传的方法,随后进行全局绑定$confirm
话不多说:
// confirmService.js
// confirmService.js
import { ElMessageBox } from 'element-plus'
const ConfirmService = {
confirm(message, title, userOptions = {}) {
const defaultOptions = {
confirmButtonClass: 'confirm-btn',
beforeClose: (action, instance, originalDone) => {
if (action === 'confirm') {
const confirmButton = document.querySelector(`.${defaultOptions.confirmButtonClass}`)
const onClickHandler = event => {
event = event || window.event
const isInputLikeElement = target => {
const inputTypes = ['textarea', 'input', 'select']
return inputTypes.includes(target.tagName.toLowerCase())
}
if (event.detail !== 0 && !isInputLikeElement(event.target)) {
originalDone() // 点击确认或非回车键时正常关闭
}
}
confirmButton.removeEventListener('click', onClickHandler)
confirmButton.addEventListener('click', onClickHandler)
onClickHandler()
} else {
originalDone()
}
}
}
const finalOptions = { ...defaultOptions, ...userOptions }
return ElMessageBox.confirm(message, title, finalOptions)
}
}
export default ConfirmService
// mixin.js
// globalConfirmMixin.js
import { inject } from 'vue'
const globalConfirmMixin = {
data() {
return {
confirmService: null
}
},
mounted() {
this.confirmService = inject('confirmService')
},
methods: {
$confirm(message, title, options = {}) {
return this.confirmService.confirm(message, title, options)
}
}
}
export default globalConfirmMixin
// main.js
import { createApp, provide } from 'vue'
import ConfirmService from '@/mixin/confirmService'
import GlobalConfirmMixin from '@/mixin/globalConfirmMixin'
app.provide('confirmService', ConfirmService)
app.mixin(GlobalConfirmMixin)
/ 普通页面正常使用$confirm
this.$confirm(tip, this.$t('public.delete'), {
confirmButtonText: this.$t('public.delete'),
cancelButtonText: this.$t('public.cancel'),
type: 'warning',
closeOnClickModal: false,
}).then(() => {
this.deleteEmailTemplate(res.id)
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('public.canceledDelete')
})
})
}
大功告成!