bug 记录 - 使用 el-dialog 的 before-close 的坑

需求说明

  • 弹窗中内嵌一个 form 表单
  • 原始代码
html 复制代码
<script setup lang="ts">
import { reactive, ref } from "vue"
import type { FormRules } from 'element-plus'
const ruleFormRef = ref()
interface RuleForm {
    name: string
    region: number | null
}
const ruleForm = reactive<RuleForm>({
    name: '',
    region: null,
})
const rules = reactive<FormRules<RuleForm>>({
    name: [
        { required: true, message: '请填写姓名', trigger: 'blur' },
    ],
    region: [
        { required: true, message: '请选择区域编码', trigger: 'change' },
    ]
})
const dialogVisible = ref(false);
function handelDialogPop(type: string) {
    if (type == "open") {
        dialogVisible.value = true; // 开启弹窗
    } else if (type == "submit") {
        ruleFormRef.value.validate((valid: any, fields: any) => {
            if (valid) {
                console.log('提交成功')
                // 关闭弹窗
                ruleFormRef.value.resetFields();
                dialogVisible.value = false;
            } else {
                console.log('error submit!', fields)
            }
        })
    } else if (type == "cancel") {
        // 关闭弹窗
        ruleFormRef.value.resetFields();
        dialogVisible.value = false
    }
}
</script>
<template>
    <div class="dataBox">
        <el-button type="primary" @click="handelDialogPop('open')">开启弹窗</el-button>
        <el-dialog v-model="dialogVisible" title="弹窗" width="500">
            <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto">
                <el-form-item label="姓名" prop="name">
                    <el-input v-model="ruleForm.name" />
                </el-form-item>
                <el-form-item label="区域" prop="region">
                    <el-select v-model="ruleForm.region" placeholder="区域编码">
                        <el-option label="第一区" :value="1" />
                        <el-option label="第二区" :value="2" />
                    </el-select>
                </el-form-item>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" plain @click="handelDialogPop('cancel')">取消</el-button>
                    <el-button type="primary" @click="handelDialogPop('submit')">提交</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
  • 点击取消按钮,或者提交通过校验时,先执行了 form 表单的 resetFields 事件。此代码会将表单的值重置为初始值,并且移除所有校验。这样可以保证下次点开弹窗时,不会保留上次操作 form 的痕迹。
  • 为了省事儿,我把所有对 dialog 的操作都写进了 handelDialogPop 函数里,传入 type 来区分是什么事件(这里就是出问题的关键)

发现问题

  • 手动点击关闭按钮,或者点击 model 空白区域时,弹窗被关闭。未执行 form 表单的 resetFields 方法,所以再次打开时,弹窗保留了上次操作的痕迹(form 表单未重置)

  • 因此需要监听 dialog 的关闭事件,官方提供了一个方法,before-close

html 复制代码
<el-dialog v-model="dialogVisible" title="弹窗" width="500" :before-close="handelDialogPop('cancel')">
...
  • 问题出现了,一进入页面时,直接抛错。报错信息可看出,before-close 被触发了

问题解决

  • 文档中表明,before-close 传参,done 说明会执行。
  • 重新为 before-close 封装一个函数

完整代码

html 复制代码
<script setup lang="ts">
import { reactive, ref } from "vue"
import type { FormRules } from 'element-plus'
const ruleFormRef = ref()
interface RuleForm {
    name: string
    region: number | null
}
const ruleForm = reactive<RuleForm>({
    name: '',
    region: null,
})
const rules = reactive<FormRules<RuleForm>>({
    name: [
        { required: true, message: '请填写姓名', trigger: 'blur' },
    ],
    region: [
        { required: true, message: '请选择区域编码', trigger: 'change' },
    ]
})
const dialogVisible = ref(false);
function beforeCloseDialog() {
    console.log("执行了 beforeCloseDialog");

    ruleFormRef.value.resetFields();
    dialogVisible.value = false
}
function handelDialogPop(type: string) {
    if (type == "open") {
        dialogVisible.value = true
    } else if (type == "submit") {
        ruleFormRef.value.validate((valid: any, fields: any) => {
            if (valid) {
                console.log('提交成功')
                beforeCloseDialog();
            } else {
                console.log('error submit!', fields)
            }
        })
    } else if (type == "cancel") {
        // 关闭弹窗
        beforeCloseDialog()
    }
}
</script>
<template>
    <div class="dataBox">
        <el-button type="primary" @click="handelDialogPop('open')">开启弹窗</el-button>
        <el-dialog v-model="dialogVisible" title="弹窗" width="500" :before-close="beforeCloseDialog">
            <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto">
                <el-form-item label="姓名" prop="name">
                    <el-input v-model="ruleForm.name" />
                </el-form-item>
                <el-form-item label="区域" prop="region">
                    <el-select v-model="ruleForm.region" placeholder="区域编码">
                        <el-option label="第一区" :value="1" />
                        <el-option label="第二区" :value="2" />
                    </el-select>
                </el-form-item>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" plain @click="handelDialogPop('cancel')">取消</el-button>
                    <el-button type="primary" @click="handelDialogPop('submit')">提交</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
相关推荐
俊昭喜喜里35 分钟前
理解全局跨域CORS配置
前端
香芋芋圆1 小时前
iD-editor 详解|OSM官方在线编辑器,基于D3.js的轻量地图编辑器
开发语言·javascript·编辑器
晗夜未央3 小时前
任务队列反复失败,先分清重试、阻塞和结果待核对
开发语言·javascript
锋行天下3 小时前
DeepSeek写Langgraph就是个大傻逼,一本正经胡说八道
前端·人工智能·后端
jktantan4 小时前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript
计算机魔术师4 小时前
Anthropic官方插件被「寄生」,背后是同一类攻击又来了
前端
我命由我123454 小时前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
key_3_feng5 小时前
评估与对齐:Agent 自我进化的护栏
前端·数据库·agent
程序员大阳5 小时前
副队长JavaScript教程(65)-详解JSON:轻量级数据交换格式
javascript·json·序列化·反序列化·stringify·parse
LaughingZhu5 小时前
Product Hunt 热榜 2026-10-30:Empacer、Scape、Floot in ChatGPT
java·前端·数据库·人工智能·chatgpt·notion