Vue3+element-plus复杂表单分组处理

一、为什么表单要分组处理?

  • 方便表单字段的复用:例如,你的表单有十个字段会在很多的表单都会用到,那么表单则需要进行分组进行表单复用;
  • 实现不同角色的表单权限控制:例如一个表单有60个字段,角色A拥有表单前30个的权限,角色B拥有其它30个字段的权限,正常想法,可能会在表单的页面直接获取角色的权限,通过v-if来控制不同字段的权限,这样的话,权限逻辑和表单逻辑就会堆砌在一起,简单一点的表单可能没有太大影响,如果表单逻辑很复杂或者表单字段过多,就会导致代码的臃肿,后期难以维护;若表单进行了分组处理,可以增加一个业务组件来处理权限的逻辑,表单的校验等逻辑则在分组表单中完成。

二、表单分组处理实现思路

  1. 表单字段按业务逻辑进行划分成不同的组,一个组代表一个表单子组件;
  2. 表单子组件实现校验字段的方法,如果有需要重置表单字段需求,则增加表单重置方法;
  3. 增加的校验方法(校验成功则返回子表单所有字段,否则返回null)和表单重置方法,在子组件onMounted钩子中将两个方法传递给业务组件(父组件),业务组件维护两个数组分别存储子表单组件的校验方法和重置表单方法;
  4. 用户点击提交表单或者重置表单时,触发对应维护的数组即可;

三、具体实现代码

3.1 将表单分为两组为例,目录结构如下:
在这里插入图片描述
在这里插入图片描述

效果图如下:

请添加图片描述

3.2 ComplexForm.vue业务组件代码如下:

<script setup lang="ts">
import FormItemA from "@/views/component/complex-form/FormItemA.vue";
import FormItemB from "@/views/component/complex-form/FormItemB.vue";
import { reactive, provide, ref, onMounted } from "vue";

defineOptions({
  name: "ComplexForm"
});

const formData = ref({});
// 向子组件注入表单的初始化的值或者回填的值,一般是编辑表单的时候需要传递
provide("defaultFormData", formData);

const getFormData = () => {
  formData.value = {
    name: "Hello",
    region: "shanghai",
    count: "5",
    date1: "",
    date2: "",
    delivery: false,
    type: [],
    resource: "",
    desc: "hello world"
  };
};
onMounted(() => {});
// 将子组件表单的参数添加到formData中汇总
const addParamsToFormData = (params: any) => {
  Object.assign(formData.value, params);
};
const formEventList = reactive([]);
// 存储子组件的表单验证方法,在提交时统一调用
const addEventToFormEventList = (event: Function) => {
  formEventList.push(event);
};
const submitHandler = () => {
  let successFlag = true;
  console.log("formEventList=", formEventList);
  formEventList.forEach((func, index, list) => {
    // 执行子组件的方法(表单验证+触发add-params添加参数)
    func()
      .then(res => {
        if (!res) {
          // 如果表单存在一个不满足,则设置标识为false,后续根据这个标识来确定是否可以提交表单
          successFlag = false;
        } else {
          // 表单验证通过后,添加参数到formData
          addParamsToFormData(res);
        }
        // 执行最后一个表单验证并且通过后,提交表单处理
        if (index === list.length - 1 && successFlag) {
          console.log("表单验证通过,提交表单");
        }
      })
      .catch(err => {
        console.log("返回错误", err);
      });
  });
};

const formResetEvent = reactive([]);
//  存储子组件的表单重置方法,在重置时统一调用
const addFormResetEvent = func => {
  formResetEvent.push(func);
};
const resetForm = () => {
  formResetEvent.forEach(func => {
    func();
  });
};
</script>

<template>
  <div>{{ formData }}</div>
  <FormItemA
    @add-submit-event="addEventToFormEventList"
    @add-reset-event="addFormResetEvent"
  />
  <FormItemB
    @add-submit-event="addEventToFormEventList"
    @add-reset-event="addFormResetEvent"
  />
  <el-button type="primary" @click="submitHandler">表单提交</el-button>
  <el-button @click="resetForm">重置表单</el-button>
  <el-button @click="getFormData">模拟接口请求表单回填数据</el-button>
</template>

<style scoped lang="scss"></style>

3.2 FormItemA.vue 子组件代码如下:

<script lang="ts" setup>
import {
  reactive,
  ref,
  defineEmits,
  onMounted,
  inject,
  watch,
  nextTick
} from "vue";
import type { FormInstance, FormRules } from "element-plus";

interface RuleForm {
  name: string;
  region: string;
  count: string;
}

interface Emits {
  (e: "add-submit-event", event: Function): void;
  (e: "add-reset-event", event: Function): void;
}
const emits = defineEmits<Emits>();

// 接受注入的默认表单数据(表单回填)
const defaultFormData = inject("defaultFormData");

onMounted(() => {
  // 将当前表单验证方法传递给父组件维护的数组,父组件点击提交时,统一遍历数组进行表单验证
  emits("add-submit-event", submitForm);
  emits("add-reset-event", resetForm);
});

const setDefaultFormData = (ruleForm, sourceForm) => {
  console.log("666666--sourceForm", sourceForm);
  for (const key in ruleForm) {
    if (Object.prototype.hasOwnProperty.call(sourceForm, key)) {
      ruleForm[key] = JSON.parse(JSON.stringify(sourceForm[key]));
    }
  }
};

watch(
  () => defaultFormData.value,
  () => {
    console.log("watch监听");
    // 回填表单数据时,需要加nextTick,否则ruleFormRef.value.resetFields()初始化表单时,会初始化为赋值后的表单数据(无法达到真正初始化表单为空值)
    nextTick(() => {
      setDefaultFormData(ruleForm, defaultFormData.value);
    });
  },
  {
    immediate: true
  }
);

const formSize = ref("default");
const ruleFormRef = ref<FormInstance>();
const ruleForm = reactive<RuleForm>({
  name: "Hello",
  region: "",
  count: ""
});

const rules = reactive<FormRules<RuleForm>>({
  name: [
    { required: true, message: "Please input Activity name", trigger: "blur" },
    { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" }
  ],
  region: [
    {
      required: true,
      message: "Please select Activity zone",
      trigger: "change"
    }
  ],
  count: [
    {
      required: true,
      message: "Please select Activity count",
      trigger: "change"
    }
  ]
});

// 单个表单提交,校验通过则返回表单的字段,校验失败则返回null
const submitForm = () => {
  return new Promise((resolve, reject) => {
    if (!ruleFormRef.value) return resolve(null);
    ruleFormRef.value.validate((valid, fields) => {
      if (valid) {
        console.log("formItemA---submit!");
        resolve(ruleForm);
      } else {
        resolve(null);
      }
    });
  });
};

const resetForm = () => {
  if (!ruleFormRef.value) return;
  ruleFormRef.value.resetFields();
};

const options = Array.from({ length: 10000 }).map((_, idx) => ({
  value: `${idx + 1}`,
  label: `${idx + 1}`
}));
</script>

<template>
  <el-form
    ref="ruleFormRef"
    :model="ruleForm"
    :rules="rules"
    label-width="120px"
    class="demo-ruleForm"
    :size="formSize"
    status-icon
  >
    <el-form-item label="Activity name" prop="name">
      <el-input v-model="ruleForm.name" />
    </el-form-item>
    <el-form-item label="Activity zone" prop="region">
      <el-select v-model="ruleForm.region" placeholder="Activity zone">
        <el-option label="Zone one" value="shanghai" />
        <el-option label="Zone two" value="beijing" />
      </el-select>
    </el-form-item>
    <el-form-item label="Activity count" prop="count">
      <el-select-v2
        v-model="ruleForm.count"
        placeholder="Activity count"
        :options="options"
      />
    </el-form-item>
  </el-form>
</template>

3.3 FormItemB.vue 子组件代码如下

<script lang="ts" setup>
import {
  reactive,
  ref,
  defineEmits,
  onMounted,
  watch,
  nextTick,
  inject
} from "vue";
import type { FormInstance, FormRules } from "element-plus";

interface RuleForm {
  date1: string;
  date2: string;
  delivery: boolean;
  type: string[];
  resource: string;
  desc: string;
}

interface Emits {
  (e: "add-submit-event", event: Function): void;
  (e: "add-reset-event", event: Function): void;
}
const emits = defineEmits<Emits>();

// 接受注入的默认表单数据(表单回填)
const defaultFormData = inject("defaultFormData");

onMounted(() => {
  // 将当前表单验证方法传递给父组件维护的数组,父组件点击提交时,统一遍历数组进行表单验证
  emits("add-submit-event", submitForm);
  emits("add-reset-event", resetForm);
});

const setDefaultFormData = (ruleForm, sourceForm) => {
  console.log("666666--sourceForm", sourceForm);
  for (const key in ruleForm) {
    if (Object.prototype.hasOwnProperty.call(sourceForm, key)) {
      ruleForm[key] = JSON.parse(JSON.stringify(sourceForm[key]));
    }
  }
};

watch(
  () => defaultFormData.value,
  () => {
    console.log("watch监听");
    // 回填表单数据时,需要加nextTick,否则ruleFormRef.value.resetFields()初始化表单时,会初始化为赋值后的表单数据(无法达到真正初始化表单为空值)
    nextTick(() => {
      setDefaultFormData(ruleForm, defaultFormData.value);
    });
  },
  {
    immediate: true
  }
);

const formSize = ref("default");
const ruleFormRef = ref<FormInstance>();
const ruleForm = reactive<RuleForm>({
  date1: "",
  date2: "",
  delivery: false,
  type: [],
  resource: "",
  desc: ""
});

const rules = reactive<FormRules<RuleForm>>({
  date1: [
    {
      type: "date",
      required: true,
      message: "Please pick a date",
      trigger: "change"
    }
  ],
  date2: [
    {
      type: "date",
      required: true,
      message: "Please pick a time",
      trigger: "change"
    }
  ],
  type: [
    {
      type: "array",
      required: true,
      message: "Please select at least one activity type",
      trigger: "change"
    }
  ],
  resource: [
    {
      required: true,
      message: "Please select activity resource",
      trigger: "change"
    }
  ],
  desc: [
    { required: true, message: "Please input activity form", trigger: "blur" }
  ]
});

const submitForm = () => {
  return new Promise((resolve, reject) => {
    if (!ruleFormRef.value) return resolve(null);
    ruleFormRef.value.validate((valid, fields) => {
      if (valid) {
        console.log("formItemB---submit!");
        resolve(ruleForm);
      } else {
        // console.log("error submit!", fields);
        resolve(null);
      }
    });
  });
};

const resetForm = () => {
  if (!ruleFormRef.value) return;
  ruleFormRef.value.resetFields();
};
</script>

<template>
  <el-form
    ref="ruleFormRef"
    :model="ruleForm"
    :rules="rules"
    label-width="120px"
    class="demo-ruleForm"
    :size="formSize"
    status-icon
  >
    <el-form-item label="Activity time" required>
      <el-col :span="11">
        <el-form-item prop="date1">
          <el-date-picker
            v-model="ruleForm.date1"
            type="date"
            label="Pick a date"
            placeholder="Pick a date"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
      <el-col class="text-center" :span="2">
        <span class="text-gray-500">-</span>
      </el-col>
      <el-col :span="11">
        <el-form-item prop="date2">
          <el-time-picker
            v-model="ruleForm.date2"
            label="Pick a time"
            placeholder="Pick a time"
            style="width: 100%"
          />
        </el-form-item>
      </el-col>
    </el-form-item>
    <el-form-item label="Instant delivery" prop="delivery">
      <el-switch v-model="ruleForm.delivery" />
    </el-form-item>
    <el-form-item label="Activity type" prop="type">
      <el-checkbox-group v-model="ruleForm.type">
        <el-checkbox label="Online activities" name="type" />
        <el-checkbox label="Promotion activities" name="type" />
        <el-checkbox label="Offline activities" name="type" />
        <el-checkbox label="Simple brand exposure" name="type" />
      </el-checkbox-group>
    </el-form-item>
    <el-form-item label="Resources" prop="resource">
      <el-radio-group v-model="ruleForm.resource">
        <el-radio label="Sponsorship" />
        <el-radio label="Venue" />
      </el-radio-group>
    </el-form-item>
    <el-form-item label="Activity form" prop="desc">
      <el-input v-model="ruleForm.desc" type="textarea" />
    </el-form-item>
  </el-form>
</template>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/441152.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3DEXPERIENCE Works八大核心优势分析

云技术正在加速普及&#xff0c;助力各行各业数字化转型。根据IDC 2023年12月发布的报告&#xff0c;2023年全球云计算市场规模达到3329亿美元&#xff0c;同比增长19.4%。其中&#xff0c;公有云市场规模达到2587亿美元&#xff0c;同比增长21.5%;私有云市场规模达到742亿美元…

倒计时最后1天!AutoMQ x 阿里云云原生创新论坛精彩预告

3月9日&#xff08;本周六&#xff09;“AutoMQ x 阿里云云原生创新论坛”就要与大家见面了&#xff0c;让我们一起来看看本次论坛都有哪些精彩的议题&#xff01;文末附有参会交通指南和直播预约链接。 精彩剧透 01 AutoMQ&#xff1a;加速云原生创新&#xff0c;助力大数据…

Java集合面试题(day 02)

&#x1f4d1;前言 本文主要是【JAVA】——Java集合面试题的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&am…

支小蜜校园防欺凌系统听到声音之后会自动识别吗

在校园安全领域&#xff0c;特别是在预防和应对欺凌问题上&#xff0c;校园防欺凌系统作为新兴的技术应用&#xff0c;正在受到越来越多的关注和探索。那么当这样的系统听到声音之后&#xff0c;它是否能够自动识别并作出相应反应呢&#xff1f;本文将围绕这一问题展开探讨。 …

protobufjs使用教程,支持proto文件打包成typescript或javascript脚本

官方链接&#xff1a;https://docs.cocos.com/creator/manual/zh/scripting/modules/example.html 第一步&#xff0c;安装nodejs。&#xff08;自行安装&#xff09; 安装教程可参考 https://www.runoob.com/nodejs/nodejs-install-setup.html 第二步&#xff0c;创建cocos…

雷赛控制卡正负限位的信号灯不亮问题处理

雷赛控制卡正负限位的信号灯不亮问题处理 正负限位IO映射&#xff1a;这个映射要和轴号对映。 回零设置中的IO映射也是一样的设置。 如下图&#xff1a;3轴的IO映射都选3。1轴的IO映射都选1

c++的STL(2)-- vector容器

目录 1. 默认构造 代码: 相关知识点: 2. 有参构造函数 以及 使用{}初始化对象 代码: 相关知识点: 3. vector容器在尾部添加和删除元素 代码: 使用push_back()和pop_back()进行尾部元素的添加和删除 相关知识点: 代码: 使用emplace_back在尾部添…

机器学习——神经网络压缩

神经网络压缩 需要部署&#xff0c;设备内存和计算能力有限&#xff0c;需要进行模型压缩&#xff0c;在设备上运行的好处是低延迟&#xff0c;隐私性。 目录 不考虑硬件问题&#xff0c;只考虑通过软件算法优化。 修剪网络 参数过多或者没有用的参数&#xff0c;可以将其剪…

MRI基础--k空间

k空间定义 k空间是表示 MR 图像中空间频率的数字数组。 k空间物理意义 k 空间的单元通常显示在主轴 kx 和 ky 的矩形网格上。 k 空间的 kx 和 ky 轴对应于图像的水平 (x) 和垂直 (y) 轴。然而,k 轴表示 x 和 y 方向上的空间频率而不是位置。 k 空间中的各个点 (kx,ky) 与图像…

R语言 | 复数 相关函数

问题 大家好&#xff0c;我有一个问题&#xff0c;我看到一个函数如下&#xff1a; L2_distance <- function(A, B){rowA <- apply(A*A, 1, sum)matrixA <- matrix(rep(rowA, eachlength(rowA)), nrowlength(rowA), byrowT)rowB <- apply(B*B, 1, sum)matrixB &l…

【教程】Kotlin语言学习笔记(四)——方法(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【Kotlin语言学习】系列文章 第一章 《认识Kotlin》 第二章 《数据类型》 第三章 《数据容器》 第四章 《方法》 文章目录 【…

小白跟做江科大51单片机之AD/DA

1.看原理图找接口 2.看时序图编写读取数据代码 XPT2046.c代码 #include <REGX52.H> //引脚定义 sbit XPY2046_DINP3^4; sbit XPY2046_CSP3^5; sbit XPY2046_DCLKP3^6; sbit XPY2046_DOUTP3^7; unsigned int XPT2046_ReadAD(unsigned char Command) { unsigned char …

多多关键字API php java Python

多多关键字API接口广泛应用于商家进行市场分析、竞品分析、关键词优化等场景。商家可以通过分析关键词数据&#xff0c;了解用户需求&#xff0c;制定针对性的营销策略&#xff0c;提高产品的曝光率和转化率。 多多-item_seach-通过关键字搜索商品列表 公共参数 获取key和秘钥…

开发知识点-C++之win32与NT内核

win32 Windows MFC编程 常用API汇总EnumWindows()函数UpdateData()函数static与 单例 设计模式函数原型:BOOL WINAPI SetConsoleTitle(__in LPCTSTR lpConsoleTitle);HWND 是一个基本类型 表示窗口句柄FindWindow函数SendMessage函数 将指定的消息发送到一个或多个窗口PostMes…

百度地图城市点位数据下载并转换

概述 在浏览百度地图开放平台的时候&#xff0c;发现有个资源下载页面&#xff0c;里面有个城市中心点位和百度地图行政区划adcode映射表数据&#xff0c;这是一个经常使用到的数据&#xff0c;本文实现将这个数据转换为geojson&#xff0c;并借助QGIS转换为经纬度坐标或火星坐…

手写简易操作系统(一)--环境配置

本专栏是我新开设的一个学术专栏&#xff0c;旨在全面介绍手写操作系统的相关内容。其中包括实模式向保护模式的过渡、锁机制、信号量操作、内存分配、硬盘驱动、文件系统、简单shell和管道等操作系统核心知识。该专栏旨在为有意开发自己操作系统的研究人员提供指导与帮助。作为…

代码随想录刷题笔记-Day31

1. 分发饼干 455. 分发饼干https://leetcode.cn/problems/assign-cookies/ 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口…

园区内部无线语音通信的解决方案

在一些园区、办公环境和厂矿场所&#xff0c;内部无线语音通信功能的需求日益凸显&#xff0c;尤其对于人员流动和移动办公的场景。这种需求着重强调了无线通信的便捷性和内部部署环境的适应性。 传统的内部通信系统中&#xff0c;有线通信能力占据主导地位&#xff0c;如集团…

【Latex】图片排版归纳(单张、两张图片并排、三张图片并排)

文章目录 单张图片两张并排三张并排 单张图片 \begin{figure}[htb]\centering\caption{PWM原理图}\includegraphics[width0.45\textwidth]{images/PWM.png}%可修改0.45为其他比例&#xff0c;调整大小 \end{figure}两张并排 实现效果如下 \begin{figure}[htb]\begin{minipag…

Qt初识 - 编写Hello World的两种方式 | 对象树

目录 一、通过图形化方式&#xff0c;在界面上创建出一个控件 二、通过代码方式&#xff0c;创建Hello World 三、Qt 内存泄漏问题 (一) 对象树 一、通过图形化方式&#xff0c;在界面上创建出一个控件 创建项目后&#xff0c;打开双击forms文件夹中的ui文件&#xff0c;可…