ListForm 弹窗表单组件
组件介绍
ListForm是一个通用的弹窗表单组件,支持动态加载指定目录下的创建(create)和编辑(update)组件,实现弹窗的显示/隐藏控制和父子组件间的数据传输。组件内部通过事件总线和props实现灵活的数据通信。
核心功能
- 支持通过路由路径或手动指定路径动态加载Vue组件
- 弹窗显示/隐藏状态双向绑定管理
- 父子组件间的数据传递和事件通信
- 支持创建/编辑两种模式,也可扩展其他模式
- 操作结果反馈和自动/手动刷新页面数据
- 表单数据重置和组件清理机制
基本用法
引入组件
<template>
<div class="app-container">
<!-- 其他页面内容 -->
<list-form
:params="formObj.params"
:row="formObj.row"
:type="formObj.type"
:title="formObj.title"
:dialogWidth="formObj.dialogWidth"
:top="formObj.top"
@update:dialogVisible="listFormClose"
@success="handleSuccess"
/>
</div>
</template>
<script>
// 无需显式引入,组件已全局注册
export default {
// ...
}
</script>
基本配置
data() {
return {
formObj: {
type: null, // 'create' 或 'update'
title: '客户列表', // 弹窗标题
dialogVisible: false, // 弹窗显示状态
row: {}, // 用于编辑时传递当前行数据
params: {}, // 传递给子组件的参数
dialogWidth: '80%', // 弹窗宽度
top: '8vh' // 弹窗距离顶部的距离
}
}
},
methods: {
// 打开创建弹窗
handleCreate() {
this.formObj = {
type: 'create',
title: '新增客户',
dialogVisible: true,
params: { /* 初始参数 */ }
};
this.$formEvent.$emit('form-event', true);
},
// 打开编辑弹窗
editRow(row) {
this.formObj = {
type: 'update',
title: '编辑',
dialogVisible: true,
row: row,
params: { /* 其他参数 */ }
};
this.$formEvent.$emit('form-event', true);
},
// 弹窗关闭回调
listFormClose(visible) {
this.formObj.dialogVisible = visible;
this.getList(); // 关闭弹窗后刷新列表
},
// 操作成功回调
handleSuccess(data) {
this.formObj.dialogVisible = false;
this.getList(); // 刷新数据列表
}
}
属性说明
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | String | 'create' | 弹窗类型,可选值:'create'或'update'等 |
| title | String | '创建' | 弹窗标题 |
| dialogVisible | Boolean | false | 控制弹窗显示/隐藏,支持双向绑定 |
| row | Object | {} | 用于编辑时传递当前行数据,会自动赋值给dialog.formData |
| params | Object | {} | 传递给子组件的额外参数 |
| dialogWidth | String | '80%' | 弹窗宽度 |
| top | String | '8vh' | 弹窗距离顶部的距离 |
事件说明
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:dialogVisible | visible: Boolean | 弹窗显示状态变化时触发,用于双向绑定 |
| success | data: Object | 表单提交成功后触发,传递表单数据 |
| handleSuccess | data: Object | 同success事件,表单提交成功后触发 |
| cancel | - | 表单取消操作时触发 |
| handleCancel | - | 同cancel事件,表单取消操作时触发 |
| formClose | visible: Boolean | 表单关闭时触发,传递关闭状态 |
弹窗控制流程
显示弹窗
- 父组件设置
formObj.type为'create'或'update' - 设置
formObj.dialogVisible为true - 可选:设置
formObj.row传递编辑数据 - 调用
this.$formEvent.$emit('form-event', true)触发弹窗显示 - 组件自动加载对应类型的表单组件
隐藏弹窗
- 点击弹窗关闭按钮
- 表单提交成功后自动关闭
- 点击取消按钮
- 父组件修改
formObj.dialogVisible为false - 关闭时会触发
formClose事件并刷新列表
// 组件内部关闭逻辑
handleClose() {
this.dialog.visible = false;
this.currentComponent = null; // 重置组件引用
this.$emit("update:dialogVisible", false); // 同步状态到父组件
this.$emit("formClose", false); // 通知父组件关闭
this.resetForm(); // 重置表单数据
}
// 重置表单
handleDialogCancel() {
this.dialog.visible = false;
console.log('表单取消');
this.$emit("cancel");
this.$emit("handleCancel");
this.$emit("formClose", false);
}
数据传输
父传子
通过
row属性传递编辑数据:// 父组件 data() { return { formObj: { // ... row: { id: 123, name: '测试数据' } } } }通过
params属性传递额外参数:// 父组件 data() { return { formObj: { // ... params: { category: 'customer', status: 'active' } } } }子组件接收数据:
// 子组件(动态加载的create.vue/update.vue) export default { props: { formData: { type: Object, default: () => ({}) }, // 来自父组件的row params: { type: Object, default: () => ({}) } // 来自父组件的params }, // ... }
子传父
通过事件机制将数据从子组件传递回父组件:
// 子组件
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
// 提交表单数据
this.$emit('handleSuccess', this.formData);
}
});
}
handleCancel() {
this.$emit('handleCancel');
}
// 父组件
<list-form
@success="handleSuccess"
@handleSuccess="handleSuccess"
@cancel="handleCancel"
@handleCancel="handleCancel"
@update:dialogVisible="listFormClose"
/>
methods: {
handleSuccess(data) {
console.log('操作成功', data);
this.getList(); // 刷新列表
},
handleCancel() {
console.log('操作取消');
},
listFormClose(visible) {
this.formObj.dialogVisible = visible;
}
}
动态组件加载
ListForm会根据当前路由路径自动加载对应目录下的组件:
// 动态加载组件核心代码
async loadComponent(type) {
try {
// 支持两种加载方式:自动路径和手动指定路径
if (this.params.componentPath) {
// 手动指定路径加载
const component = require(`@/views/${this.params.componentPath}/${type}.vue`).default;
this.currentComponent = component;
console.log("手动指定路径组件加载成功:", `@/views/${this.params.componentPath}/${type}.vue`);
} else {
// 获取当前路由对应的组件路径
const currentPath = this.$route.path;
// 提取目录路径
const dirPath = currentPath.substring(0, currentPath.lastIndexOf("/"));
// 动态导入组件 - 支持两种路径格式
let component;
try {
// 尝试使用项目别名格式
component = require(`@projectName/views${dirPath}/${type}.vue`).default;
console.log("自动路径组件加载成功(项目别名):", `@projectName/views${dirPath}/${type}.vue`);
} catch (e) {
// 失败则使用常规格式
component = require(`@/views${dirPath}/${type}.vue`).default;
console.log("自动路径组件加载成功(常规路径):", `@/views${dirPath}/${type}.vue`);
}
this.currentComponent = component;
}
} catch (error) {
console.error("组件加载失败:", error);
this.$message.error(`加载${type === "create" ? "创建" : "编辑"}组件失败`);
}
}
完整示例
父组件中使用ListForm (完整示例)
<template>
<div class="app-container">
<!-- 公共操作区 -->
<div class="common-header">
<el-button type="primary" @click="handleCreate">新增</el-button>
</div>
<!-- 检索区域 -->
<div class="search-form">
<el-input v-model="searchForm.keyword" placeholder="请输入关键词" style="width: 200px;"></el-input>
<el-button type="primary" @click="getList">搜索</el-button>
</div>
<!-- 数据列表 -->
<el-table :data="dataList" style="width: 100%">
<el-table-column prop="name" label="名称"></el-table-column>
<el-table-column prop="code" label="编码"></el-table-column>
<el-table-column label="操作" width="150">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="editRow(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- ListForm组件 -->
<list-form
:params="formObj.params"
:row="formObj.row"
:type="formObj.type"
:title="formObj.title"
:dialogVisible="formObj.dialogVisible"
:dialogWidth="formObj.dialogWidth"
:top="formObj.top"
@update:dialogVisible="listFormClose"
@success="handleSuccess"
@handleSuccess="handleSuccess"
@cancel="handleCancel"
@handleCancel="handleCancel"
@formClose="formClose"
/>
</div>
</template>
<script>
// 无需显式引入,组件已全局注册
export default {
data() {
return {
dataList: [],
searchForm: {
keyword: ''
},
formObj: {
type: null,
title: '',
dialogVisible: false,
row: {},
params: {},
dialogWidth: '80%',
top: '8vh'
}
};
},
created() {
this.getList();
},
methods: {
// 获取数据列表
getList() {
// 实际项目中这里会调用API获取数据
this.dataList = [
{ id: 1, name: '测试数据1', code: 'TEST001' },
{ id: 2, name: '测试数据2', code: 'TEST002' }
];
},
// 打开创建弹窗
handleCreate() {
this.formObj = {
type: 'create',
title: '新增',
dialogVisible: true,
params: {
// 可选:手动指定组件路径
// componentPath: 'path/to/custom/component'
}
};
this.$formEvent.$emit('form-event', true);
},
// 打开编辑弹窗
editRow(row) {
this.formObj = {
type: 'update',
title: '编辑',
dialogVisible: true,
row: { ...row }, // 深拷贝避免直接修改表格数据
params: {
// 额外参数
action: 'edit'
}
};
this.$formEvent.$emit('form-event', true);
},
// 处理弹窗关闭
listFormClose(visible) {
this.formObj.dialogVisible = visible;
},
// 处理操作成功
handleSuccess(data) {
this.formObj.dialogVisible = false;
this.$message.success('操作成功');
this.getList(); // 刷新数据列表
},
// 处理取消操作
handleCancel() {
console.log('操作取消');
},
// 表单关闭事件
formClose(visible) {
this.formObj.dialogVisible = visible;
this.getList(); // 关闭弹窗后刷新列表
}
}
};
</script>
动态加载的create.vue示例
<template>
<el-form ref="form" :model="formData" label-width="100px">
<!-- 表单内容 -->
<el-form-item label="客户名称" prop="name">
<el-input v-model="formData.name"></el-input>
</el-form-item>
<!-- 其他表单项 -->
<div slot="footer" class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</div>
</el-form>
</template>
<script>
export default {
props: {
formData: { type: Object, default: () => ({}) },
params: { type: Object, default: () => ({}) }
},
methods: {
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
// 提交表单数据
this.$emit('handleSuccess');
}
});
},
handleCancel() {
this.$emit('cancel');
}
}
};
</script>
注意事项
- 确保在使用ListForm的目录下存在对应的create.vue和update.vue组件,或通过params.componentPath指定正确路径
- 动态加载组件支持自动路径和手动指定路径两种方式
- 组件加载失败时会有错误提示,可查看控制台获取详细错误信息
- 表单提交成功后,会自动触发success和handleSuccess事件
- 弹窗关闭时会重置组件引用和表单数据,避免内存泄漏
- 使用事件总线时,确保在组件销毁时移除事件监听器
高级用法
手动指定组件路径
当需要加载非当前路由目录下的组件时,可以通过params.componentPath手动指定:
this.formObj = {
type: 'create',
title: '创建',
dialogVisible: true,
params: {
componentPath: 'projects/scrm/views/common'
}
}
表单验证与反馈
子组件中可以进行表单验证,并将结果通过事件传递给父组件:
// 子组件
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
// 模拟API请求
setTimeout(() => {
this.$emit('handleSuccess', this.formData);
this.$message.success('提交成功');
}, 1000);
} else {
this.$message.error('请填写必填项');
}
});
}
自定义弹窗样式
通过dialogWidth和top属性自定义弹窗样式:
<list-form
:params="formObj.params"
:type="formObj.type"
:title="formObj.title"
:dialogVisible="formObj.dialogVisible"
:dialogWidth="'60%'"
:top="'10vh'"
@update:dialogVisible="listFormClose"
@success="handleSuccess"
/>
多表单状态管理
在复杂场景下,可以管理多个表单状态:
data() {
return {
forms: {
basic: {
type: 'create',
title: '基本信息',
dialogVisible: false,
params: {}
},
advanced: {
type: 'update',
title: '高级设置',
dialogVisible: false,
params: {}
}
}
};
}
