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

news2024/12/22 18:28:55

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

  • 方便表单字段的复用:例如,你的表单有十个字段会在很多的表单都会用到,那么表单则需要进行分组进行表单复用;
  • 实现不同角色的表单权限控制:例如一个表单有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>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 NAND FLASH知识点

1.NAND FLASH的简介 NAND FLASH 的概念是由东芝公司在 1989 年率先提出&#xff0c;它内部采用非线性宏单元模式&#xff0c;为固态大容量内存的实现提供了廉价有效的解决方案。 NAND FLASH 存储器具有容量较大&#xff0c;改写速度快等优点&#xff0c;适用于大量数据的存储&…

【MySQL】事务、锁

目录 事务案例场景模拟实现转账&#xff1a;从张三的账户转账500元到李四的账户SQL示例异常 什么是事务事务的特性&#xff0c;简称ACID 属性实现原理redo logundo log MySQL 中一条 SQL 更新语句的执行过程( InnoDB 存储引擎)事务的提交流程隔离性并发事务产生的问题事务隔离级…

434G数据失窃!亚信安全发布《勒索家族和勒索事件监控报告》

最新态势快速感知 最新一周全球共监测到勒索事件90起&#xff0c;与上周相比数量有所增加。 lockbit3.0仍然是影响最严重的勒索家族&#xff1b;alphv和cactus恶意家族也是两个活动频繁的恶意家族&#xff0c;需要注意防范。 Change Healthcare - Optum - UnitedHealth遭受了…

【Python】外网远程登录访问jupyter notebook+pycharm使用ipython

第一步&#xff1a;创建python虚拟环境 conda create -n py3610 python3.6.10第二步&#xff1a;安装ipython pip install ipython pip install ipython notebook第三步&#xff1a;创建 IPython Notebook 服务器配置文件 # 进入python交互shell&#xff0c;设置密码 >&…

SpringCloud(19)之Skywalking应用上篇

一、Skywalking概述 随着互联网架构的扩张&#xff0c;分布式系统变得日趋复杂&#xff0c;越来越多的组件开始走向分布式化&#xff0c;如微服务、消 息收发、分布式数据库、分布式缓存、分布式对象存储、跨域调用&#xff0c;这些组件共同构成了繁杂的分布式网络。 思考以下…

Leaflet 加载高德地图

前言 在前面的文章中&#xff0c;我们学习了如何使用 Leaflet 创建一个基本的地图。在本文中&#xff0c;我们将学习如何在 Leaflet 中加载高德地图&#xff0c;并结合实际应用构建地图点击事件。 一、介绍 高德地图是一款由高德软件提供的数字地图服务&#xff0c;在国内使用…

在国内如何申请US,visa卡?

随着跨境与AI的发展大家对美国虚拟卡的需求也越来越多&#xff0c;比如说亚马逊、ebay、Etsy、ChatGPTPLUS、midjourney、POE等等软件以及海淘的需要&#xff0c;所以我们需要用到美国虚拟卡的场景就越来越多 如何获得一张US 虚拟信用卡&#xff1f; 方法很简单&#xff0c;点…

React-子传父

1.概念 说明&#xff1a;React中子组件向父组件传递数据通常涉及回调函数和状态提升等方法。 2.代码实现 2.1绑定事件 说明&#xff1a;父组件绑定自定义事件 <Son onGetSonMsg{getMsg}></Son> 2.2接受事件 说明&#xff1a;子组件接受父组件的自定义事件名称…

【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &#x1f680; 本…

【Pytorch入门】常见Transforms/ __call__方法

在Python中&#xff0c;__call__方法是一个特殊方法&#xff0c;用于使对象可以像函数一样被调用。当一个对象实现了__call__方法时&#xff0c;可以直接使用括号运算符将对象作为函数调用。 通过实现__call__方法&#xff0c;可以为对象提供函数式的行为&#xff0c;使其更加…

超全Chat GPT论文修改指令

文献综述指令润色修改指令论文选题指令论文大指令研究理论指令论文致谢指令参考文献指令论文润色整体逻辑论文整体优化提问指令 1&#xff0e;文献综述指令 请你帮我写一份关于&#xff08;研究主题&#xff09;的文献综述。我的论文选题方向是 XXXX &#xff0c;我已经找到了…

Vue.js 修饰符:精准控制组件行为

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

IDEA中Maven无法下载jar包问题解决

在项目中经常会遇到jar包无法下载的问题&#xff0c;可以根据以下几种方法进行排查。 1. 排查网络连接 网络连接失败&#xff0c;会导致远程访问Maven仓库失败&#xff0c;所以应确保网络连接正常。 2. 排查Maven的配置 Maven配置文件&#xff08;settings.xml&#xff09;…

《数字图像处理(MATLAB版)》相关算法代码及其分析(3)

目录 1 对边界进行子采样 1.1 输入参数检查 1.2 处理重复坐标 1.3 计算边界最大范围 1.4 确定网格线数量 1.5 构建网格位置向量 1.6 计算曼哈顿距离 1.7 整理输出结果 1.8 返回结果 2 改变图像的存储类别 2.1 函数输入 2.2 数据类型转换 2.3 错误处理 2.4 返回结…

LabVIEW高温摩擦磨损测试系统

LabVIEW高温摩擦磨损测试系统 介绍了一个基于LabVIEW的高温摩擦磨损测试系统的软件开发项目。该系统实现高温条件下材料摩擦磨损特性的自动化测试&#xff0c;通过精确控制和数据采集&#xff0c;为材料性能研究提供重要数据支持。 项目背景 随着材料科学的发展&#xff0c;…

视觉Transformers中的位置嵌入 - 研究与应用指南

视觉 Transformer 中位置嵌入背后的数学和代码简介。 自从 2017 年推出《Attention is All You Need》以来&#xff0c;Transformer 已成为自然语言处理 (NLP) 领域最先进的技术。 2021 年&#xff0c;An Image is Worth 16x16 Words 成功地将 Transformer 应用于计算机视觉任务…

小迪安全31WEB 攻防-通用漏洞文件上传js 验证mimeuser.ini语言特性

#知识点&#xff1a; 1、文件上传-前端验证 2、文件上传-黑白名单 3、文件上传-user.ini 妙用 4、文件上传-PHP 语言特性 #详细点&#xff1a; 检测层面&#xff1a;前端&#xff0c;后端等 2、检测内容&#xff1a;文件头&#xff0c;完整性&#xff0c;二次渲染…

docker快照备份回滚

1. 安装系统 1.1 vm安装Ubuntu 参考:https://blog.csdn.net/u010308917/article/details/125157774 1.2 其他操作 添加自定义物理卷 –待补充– 1.2.1 查询可用物理卷 fdisk -l 输出如下 Disk /dev/loop0: 73.9 MiB, 77492224 bytes, 151352 sectors Units: sectors of …

Vue 项目重复点击菜单刷新当前页面

需求&#xff1a;“在当前页面点击当前页面对应的菜单时&#xff0c;也能刷新页面。” 由于 Vue 项目的路由机制是路由不变的情况下&#xff0c;对应的组件是不重新渲染的。所以重复点击菜单不会改变路由&#xff0c;然后页面就无法刷新了。 方案一 在vue项目中&#xff0c;…

英特尔/ARM/国产化EMS储能控制器解决方案

新型储能是建设新型电⼒系统、推动能源绿⾊低碳转型的重要装备基础和关键⽀撑技术&#xff0c;是实现碳达峰、碳中和⽬标的重要⽀撑。说到储能&#xff0c;大众首先想到的就是电池&#xff0c;其好坏关系到能量转换效率、系统寿命和安全等重要方面&#xff0c;但储能要想作为一…