这是根据最近的一个动态表单需求所实现的一个动态表单功能,基于技术栈VUE3+VBEN+TS。
需求点:
- 创建一个表单,包含表单标题,表单内容(附件上传+复杂单选),状态等等...
- 根据表单内容创建填写表单,并可以重复编辑及查看填写的内容详情... 打算出一个系列,专门写写功能开发+框架中遇到的坑以及其他小知识点。
开发前期准备
代码是基于Vben2.0+Ruoyi分离版结合的基础后台管理模板项目开发,Vben框架封装的比较深,除了个别api文档不好找以外,我用起来还是比较舒服的。
Vben2.0(文档地址:doc.vvbin.cn/ )
Ruoyi(文档地址:doc.ruoyi.vip/ruoyi-vue/ )
开发环境是vscode+主流Vue3开发的一些插件,包含:
- Vue - Official
- Prettier - Code formatter
- ESLint
- UnoCSS
- JavaScript (ES6) code snippets
- 其他
创建动态表单
利用官方BasicForm文档的写法有以下的基础实现效果
代码如下(后续均使用组合式API):
<template>
<div class="bg-white p-4">
<BasicForm @register="register" @submit="handleSubmit" />
</div>
</template>
<script lang="ts" setup>
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
import { useMessage } from '@/hooks/web/useMessage';
// 表单配置
const schemas: FormSchema[] = [
{
field: 'formTitle',
component: 'Input',
label: '本次表单标题',
},
{
field: 'formCon',
component: 'Input',
label: '表单主题内容',
},
{
field: 'status',
component: 'Switch',
label: '状态',
},
];
// 表单实例
const [register] = useForm({
labelWidth: 120,
schemas,
baseColProps: {
span: 13,
},
actionColOptions: {
span: 24,
},
});
// 提交表单
const { createMessage } = useMessage();
const handleSubmit = (values: any) => {
createMessage.success('click search,values:' + JSON.stringify(values));
};
</script>
下面开始表演,从表单主体内容项开始动手
{ field: 'formCon', component: 'Input' , label: '动态内容', }, 使用input组件肯定不对,所以要使用vue组件中常用的插槽写法slot,改为
{
field: 'diyFormItems',
label: '动态内容',
slot: 'diyFormItemsSlot',
},
对应html部分也要修改为:
<BasicForm @register="register" @submit="handleSubmit">
<template #diyFormItemsSlot="{ model }">
<!-- 自定义插槽 model 为表单绑定的值-->
</template>
</BasicForm>
中间的自定义插槽及要写我们的业务内容了,根据已知需求,要自定义表单项名称,即选项的标题是用户自己输入的,且支持增加和删除,实现如下:
<template #diyFormItemsSlot="{ model }">
<!-- 自定义插槽 model 为表单绑定的值-->
<div v-for="(item, i) in model.diyFormItems" :key="i" class="flex items-center mb-5 mt-8">
<FormItem class="w-30% mb-0" :label="'项' + (i + 1)" :name="'accName'">
<a-input v-model:value="item.accName" :maxlength="50" />
</FormItem>
</div>
</template>
给动态内容一个默认值(默认一条记录):
{
field: 'diyFormItems',
label: '动态内容',
slot: 'diyFormItemsSlot',
defaultValue: [
{
itemName: '',
itemVal: '',
itemOther: '',
remarkInfo: {
remarkName: '',
remarkFile: [],
remarkDesc: '',
withItemName: '',
},
},
],
},
这里考虑到后续串通业务逻辑,所以在初始创建的时候就把所有结构给创建好,这里定义一个type类型:
type DiyItem = {
itemName: string; // 表单项名称
itemVal: string; // 表单项的值
itemOther: string; // 表单项值的其他
remarkInfo: {
// 表单项备注信息
remarkName: string; // 备注名称
remarkFile: Array<string>; // 备注附件
remarkDesc: string; // 备注描述
withItemName: string; // 备注关联的表单项名称
};
};
这样就实现一个动态的表单项,内容是自定义插槽里自己定义的,接下来再完善一下新增和删除的逻辑,在每一个表单项名称后面加上按钮和icon:
<a-button
@click="addItem()"
class="ml-2"
size="small"
:icon-size="12"
ghost
shape="circle"
color="success"
>
<PlusOutlined />
</a-button>
<a-button
v-if="model.attachmentItems.length !== 1"
@click="delItem(i, 'attachmentItems')"
class="ml-2"
size="small"
:icon-size="12"
ghost
shape="circle"
color="error"
>
<MinusOutlined />
</a-button>
然后是按钮的逻辑部分:
// 增加
const addItem = () => {
const addItem: DiyItem = {
itemName: '',
itemVal: '',
itemOther: '',
remarkInfo: {
remarkName: '',
remarkFile: [],
remarkDesc: '',
withItemName: '',
},
};
const newData = [...getFieldsValue().acceptanceItems, addItem];
setFieldsValue({ acceptanceItems: newData });
};
// 删除
const delItem = (i: number, field: string) => {
const d_data = [...getFieldsValue()[field]];
d_data.splice(i, 1);
setFieldsValue({ [field]: d_data });
};
完成后我们看看页面显示的效果:
OK 啦! 一个包含简单的动态表单项就完成了!
功能列表:
表单项动态添加表单项动态删除- 表单项必填校验
- 表单提交结果汇集成表格数据
- 根据动态表单提交表单内容
- 编辑已提交的表单内容
- ...