最近项目一直用vue3+element plus,发现el-dialog里form表单 el-form使用 resetFields() 并没有想象中会重置弹窗的表单项和表单验证状态。经过好长一段时间之后,突然明白了为什么。
首先,父组件引用子组件,子组件为弹窗el-dialog 下面是父组件示例
<template>
<div class="menu-management-container">
<vab-card shadow="hover">
<vab-query-form>
<vab-query-form-left-panel :span="24">
<el-form :inline="true" :model="state.form">
<el-space wrap>
<el-form-item>
<el-button type="primary" @click="handleEdit()">新增</el-button>
</el-form-item>
</el-space>
</el-form>
</vab-query-form-left-panel>
</vab-query-form>
<el-table
v-loading="state.listLoading"
border
:data="state.dataList"
row-key="id"
>
<el-table-column
align="center"
label="标题"
prop="title"
show-overflow-tooltip
/>
<el-table-column
align="center"
label="排序"
prop="order"
show-overflow-tooltip
/>
<el-table-column
align="center"
class-name="table-actions"
fixed="right"
label="操作"
>
<template #default="{ row }">
<el-button
size="small"
text
type="primary"
@click="handleEdit(row)"
>
编辑
</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty class="vab-data-empty" description="暂无数据" />
</template>
</el-table>
</vab-card>
<el-pagination
v-model:currentPage="pagination.page"
background
:layout="pagination.layout"
:page-size="pagination.pageSize"
:total="pagination.total"
@current-change="pagination.handleCurrentChange"
@size-change="pagination.handleSizeChange"
/>
<CategoryEdit ref="CategoryEditRef" @fetch-data="fetchData" />
</div>
</template>
<script setup>
import { getMaterialCategoryList } from '@/api/videoThemeManagement'
// import { Search, Edit } from '@element-plus/icons-vue'
// const $baseConfirm = inject('$baseConfirm')
// const $baseMessage = inject('$baseMessage')
const CategoryEdit = defineAsyncComponent(() =>
import('./components/categoryEdit.vue')
)
const CategoryEditRef = ref()
const state = reactive({
dataList: [],
listLoading: false,
})
const fetchData = async () => {
state.listLoading = true
const {
code,
data: { data, total },
} = await getMaterialCategoryList({
page: pagination.page,
per_page: pagination.pageSize,
})
if (code == 200) {
state.dataList = data
pagination.total = total
}
state.listLoading = false
}
const handleEdit = (row) => {
CategoryEditRef.value.showEdit(row)
}
//分页模块
const pagination = reactive({
page: 1,
pageSize: 10,
layout: 'total, sizes, prev, pager, next, jumper',
total: 0,
//改变每页显示条数
handleSizeChange: (val) => {
pagination.pageSize = val
pagination.page = 1
fetchData()
},
//改变当前页
handleCurrentChange: (val) => {
pagination.page = val
fetchData()
},
})
// watch(
// () => pagination.page,
// (val) => {
// console.log('pagination.page', val)
// },
// { deep: true, immediate: true }
// )
onMounted(() => {
fetchData()
})
</script>
<style lang="scss" scoped></style>
下面是子组件示例:
<template>
<el-dialog
v-model="state.dialogFormVisible"
:close-on-click-modal="false"
:title="state.title"
width="60%"
@close="close"
>
<el-form
ref="formRef"
label-width="140px"
:model="state.form"
:rules="state.rules"
>
<el-form-item label="标题" prop="title">
<el-input v-model="state.form.title" placeholder="请输入标题" />
</el-form-item>
<el-form-item label="排序" prop="order">
<el-input-number v-model="state.form.order" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="close">取 消</el-button>
<el-button :loading="state.loading" type="primary" @click="save">
确 定
</el-button>
</template>
</el-dialog>
</template>
<script setup>
import {
addMaterialCategory,
editMaterialCategory,
} from '@/api/videoThemeManagement'
const $baseMessage = inject('$baseMessage')
const emit = defineEmits(['fetch-data'])
const formRef = ref()
const state = reactive({
loading: false,
form: {
id: '',
title: '',
order: 0,
},
rules: {
title: [
{ required: true, message: '请输入标题', trigger: 'blur' },
{ min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' },
],
},
title: '新增素材',
dialogFormVisible: false,
})
const showEdit = async (row) => {
console.log('row', row)
if (row) {
state.isEdit = true
state.title = '编辑素材'
nextTick(() => {
state.form = Object.assign(state.form, row)
})
} else {
state.isEdit = false
state.title = '新增素材'
}
state.dialogFormVisible = true
}
const close = () => {
formRef.value.resetFields()
state.dialogFormVisible = false
}
const save = () => {
formRef.value.validate(async (valid) => {
if (valid) {
if (state.isEdit) {
state.loading = true
const { data } = await editMaterialCategory(state.form)
state.loading = false
$baseMessage(data, 'success', 'vab-hey-message-success')
emit('fetch-data')
close()
} else {
state.loading = true
const { data } = await addMaterialCategory(state.form)
state.loading = false
$baseMessage(data, 'success', 'vab-hey-message-success')
emit('fetch-data')
close()
}
}
})
}
// watch(
// () => pagination.page,
// (val) => {
// console.log('pagination.page', val)
// },
// { deep: true, immediate: true }
// )
onMounted(() => {})
defineExpose({ showEdit })
</script>
<style lang="scss" scoped></style>
如果我们的子组件里的showEdit 是这样子写的话
const showEdit = async (row) => {
console.log('row', row)
if (row) {
state.isEdit = true
state.title = '编辑素材'
state.form = Object.assign(state.form, row)
} else {
state.isEdit = false
state.title = '新增素材'
}
state.dialogFormVisible = true
}
我们会发现,表单项初始值会是你第一次打开时的数据
这样的原因是因为,我的代码是写在setup语法糖里,并且未执行showEdit时,el-dialog里的实例都为生成,el-form自然没有生成,所以模板确实给el-form绑定了state.form,然而el-form实例未挂载,也就没有初始值。当执行完showEdit后,state.dialogFormVisible = true,此时state.form已经被改变,el-dialog显示,el-form实例挂载,el-form初始值就变成了已经被改变的state.form的值。
知道了原因 那么我们可以用最简单的方式解决
使用nextTick。确保组件实例渲染完成之后,再进行表单项数据的改变。
const showEdit = async (row) => {
console.log('row', row)
if (row) {
state.isEdit = true
state.title = '编辑素材'
nextTick(() => {
state.form = Object.assign(state.form, row)
})
} else {
state.isEdit = false
state.title = '新增素材'
}
state.dialogFormVisible = true
}
又或者
const showEdit = async (row) => {
console.log('row', row)
state.dialogFormVisible = true
if (row) {
state.isEdit = true
state.title = '编辑素材'
state.form = Object.assign(state.form, row)
} else {
state.isEdit = false
state.title = '新增素材'
}
}