Vben2.0+Vue3+TS+简单动态表单生成

939 阅读3分钟

这是根据最近的一个动态表单需求所实现的一个动态表单功能,基于技术栈VUE3+VBEN+TS。

需求点:

  1. 创建一个表单,包含表单标题,表单内容(附件上传+复杂单选),状态等等...
  2. 根据表单内容创建填写表单,并可以重复编辑及查看填写的内容详情... 打算出一个系列,专门写写功能开发+框架中遇到的坑以及其他小知识点。

开发前期准备

代码是基于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文档的写法有以下的基础实现效果

image.png

代码如下(后续均使用组合式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 });
};

完成后我们看看页面显示的效果:

image.png

OK 啦! 一个包含简单的动态表单项就完成了!

功能列表:

  1. 表单项动态添加
  2. 表单项动态删除
  3. 表单项必填校验
  4. 表单提交结果汇集成表格数据
  5. 根据动态表单提交表单内容
  6. 编辑已提交的表单内容
  7. ...