需求说明
- 弹窗中内嵌一个 form 表单
- 原始代码
<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
<el-dialog v-model="dialogVisible" title="弹窗" width="500" :before-close="handelDialogPop('cancel')">
...
- 问题出现了,一进入页面时,直接抛错。报错信息可看出,before-close 被触发了
问题解决
- 文档中表明,before-close 传参,done 说明会执行。
- 重新为 before-close 封装一个函数
完整代码
<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>