开源表单设计器vue-form-design自动化校验实现原理

未命名.gif

表单校验可以改善用户体验和减轻服务器的压力, 而动态配置表单校验能极大的提高动态表单的扩展性、灵活性, 满足多样性、差异化需求

目标

👌,首先我们简要说下要实现的目标功能:

  • 具有基础的表单验证功能
  • 提供一些内置验证规则
  • 提供对外开放的能力

这样就能直接通过配置的形式无代码来表单校验

背景

开源库 vue-form-design基于 Vue3 的可视化表单设计器,拖拽式操作让你快速构建一个表单, 让表单开发简单而高效。

低代码表单设计器, 可以通过拖拽的形式生成 n 种不同类型的表单, 这样的表单如何校验数据的准确性?

就需要用到 element-plus 提供的表单校验相关规则, 并且该规则是一个对象, 那我们是不是可以配置 json 来进行校验?

在开发表单设计器初期, 确实是通过 json 配置的形式进行表单校验, 但是这种方法有几大缺陷

  • 局限于 element-plus 提供出来的校验方式, 如必填, 数字校验, 长度校验等
  • 保存动态表单配置时会转成 json 字符串, 如果使用 element-ui 自定义校验函数时,会转换成空 (就算能使用函数的 toString 方法转成字符串, 但校验函数的代码提示不好, 不能自定义参数等)
  • 入门门槛高, 需了解 element-plus 校验方式, 需要一定的基础
  • 流程复杂, 不能通过下拉框枚举出常见校验直接使用
  • 动态表单, 顾名思义, 可以通过表单配置的形式来校验输入的准确性, 但 json 配置的方式不支持

从以上几大缺陷可知, 使用 json 校验方式的扩展性, 可维护性, 便捷性, 灵活性都没有, 所以才需要重新设计动态表单的校验方式

技术

组件库使用的是element-plus, 所以相关表单校验的设计要符合element-plus提供的规则

如以下规则:

{
  "rules": {
    "name": [
      { "required": true, "message": "请输入活动名称", "trigger": "blur" },
      { "min": 3, "max": 5, "message": "长度在 3 到 5 个字符", "trigger": "blur" }
    ],
    "region": [{ "required": true, "message": "请选择活动区域", "trigger": "change" }]
  }
}

自定义校验函数规则:

var validatePass = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("请输入密码"));
  } else {
    if (this.ruleForm.checkPass !== "") {
      this.$refs.ruleForm.validateField("checkPass");
    }
    callback();
  }
};

为了提升扩展性, 可以通过编写自定义校验函数的形式进行校验, 涉及到代码开发, 所以需要一个可提示、可编写注释的开源编辑器codemirror

CodeMirror 是基于 js 的源代码编辑器组件,它支持 javascript 等多种高级语言,tampermonkey 内置的代码编辑器就是基于它。它的按键组合方式兼容 vim,emacs 等,调用者还可自定义’自动完成’的列表窗口,自由度极高,相当成熟。

使用

import { basicSetup } from "codemirror";
import VueCodemirror from "vue-codemirror";
import { javascript } from "@codemirror/lang-javascript";

// 全局注册
app.use(VueCodemirror, {
  autofocus: true,
  disabled: false,
  indentWithTab: true,
  tabSize: 2,
  placeholder: "Code goes here...",
  extensions: [basicSetup, javascript()],
});

以上是可以编写 javascript 相关代码, 如果我们要编写 css, json 呢? 正好在该库也使用到

import { json } from "@codemirror/lang-json";
export default {
  setup() {
    const extensions = [json()];
    return {
      extensions,
    };
  },
};

template

<codemirror v-model="code" placeholder="Code goes here..." mode="text/json" :style="{ height: '400px' }" :extensions="extensions" :autofocus="true" :indent-with-tab="true" :tab-size="2" />

通过以上代码, 就可以在项目中使用 codemirror 来编写 javascript 代码

架构设计

通过以上两种技术, 我相信大家都知道 element-plus 两种校验方式

那如何解决几大缺陷?

我是如何做的? 以下三种方式进行解决

枚举校验

把常见的、已知的校验规则枚举出来, 直接通过文件引用的方式遍历出校验列表进行选择(最好是维护到后台里面, 通过接口获取, 这样方便增删改查)

如:

// 数字校验规则
const validateNumber = `(rule, value, callback) => {
  console.log(rule);
  
  if (value === "" || value == null) {
    callback(new Error("请输入"));
  } else if (!/^[0-9]*$/.test(value)) {
    callback(new Error("必须为数字"));
  }
  callback();
}`;
// 数字校验规则(小数点保留两位)
const validateNumberD2 = `(rule, value, callback) => {
    if (value === "" || value == null) {
      callback(new Error("请输入"));
    } else if (!/^([1-9]+[\d]*(.[0-9]{1,2})?)$/.test(value)) {
      callback(new Error("必须为数字,且小数点最多两位"));
    }
    callback();
  }`;
const ruleList = [
  {
    label: "数字校验规则",
    validator: validateNumber,
  },
  {
    label: "数字校验规则(小数点保留两位)",
    validator: validateNumberD2,
  },
];
export default ruleList;

这样就可以通过下拉框的形式选择需要使用的校验方式

表单自定义配置

把 element-plus 校验规则进行总结, 归纳出所有组合类型, 通过表单配置的形式选择

企业微信截图_5f558adb-7b07-4ef1-bcd5-63d6e1e072cb.png

这样就可以把校验规则进行可视化配置, 直观配置需要的校验规则, 学习成本低

同时校验规则表单列表使用到了动态表单组件(上图), 即 vue-form-design 暴露出来的两个组件其一(渲染表单组件和配置表单组件)

自定义校验函数

上文可知, 自定义校验函数使用到了 CodeMirror, 这样的话就可以编写代码

企业微信截图_0eaa5d09-a226-4cd1-bac7-163c661f9180.png

上图可知, 包括两个配置

  • 函数触发事件配置, blur 还是 change
  • 校验函数配置

该配置只写函数体, 如果整个函数可配, 可能导致参数使用错误, 并且把参数位置写死, 并和 element-plus 保持一致,
这样后期好处理, 降低配置错误率.并且把每个参数的作用列出来, 方便开发. 同时在 element-plus 固有的参数基础上, 新增了第四个参数, 表示当前表单配置数据,
方便基于其他表单数据作为校验判断的基础

使用场景如:

企业微信截图_1e2b9915-b9d1-49d9-8772-a9e091446935.png

假设有两个表单如图, 一个是开关表单, 一个是校验表单

文本表单要求开关表单配置为 true 才校验通过, 否则不通过

则配置自定义函数

企业微信截图_d74be027-1e9d-468d-8767-c3e0adecf0ad.png
这样就使用到了第四个参数, 即拿到当前表单列表页数据{ceshi: true, verify: ""}, 进行校验

通过以上方法, 极大的提高了表单校验的扩展性, 灵活性

使用 codemirror 返回的函数体为一个字符串, 这种数据结构就能方便存储和进行各种处理, 如下

{
  "rule": [
    {
      "type": "func",
      "title": "自定义函数规则",
      "value": {
        "trigger": "blur",
        "func": "if(mainData.ceshi){callback()}else{\n  callback(new Error(\"请变更为false\"));\n}\n"
      }
    }
  ]
}

通过以上三种方法就能解决使用 json 配置的所有缺陷

下面的 json 是 element-plus 的校验规则例子, 大家发现了什么规则? 是不是字段的校验规则的数据结构是数组, 代表可配置多个校验规则

{
  "rules": {
    "name": [
      { "required": true, "message": "请输入活动名称", "trigger": "blur" },
      { "min": 3, "max": 5, "message": "长度在 3 到 5 个字符", "trigger": "blur" }
    ]
  }
}

所以为了和 element-plus 保持一致, 我们可以使用以上三个方法动态遍历设置校验规则

运用

企业微信截图_85ec9275-fffc-4e38-9b32-63669c634aa8.png

如图, 在 vue-form-design 中可动态配置校验方式(自定义枚举、自定义校验函数、高级模式), 并按对应校验方式配置校验规则

一个表单最后生成的数据结构如下:

[
  {
    "ControlType": "Text",
    "nameCn": "文本框",
    "id": "QGR69RscPmjnFqHW7JeMB",
    "layout": false,
    "data": {
      "fieldName": "Text_N-A6Ft1FUd8NJyrWN3LL2",
      "label": "标签名称",
      "tip": "",
      "placeholder": "",
      "showRule": "{}",
      "required": false,
      "rule": [
        {
          "type": "enum",
          "title": "自定义枚举",
          "value": "(rule, value, callback) => {\n    if (value === \"\" || value == null) {\n      callback(new Error(\"请输入\"));\n    } else if (!/^1(?:3d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8d|9d)d{8}$/.test(value)) {\n      callback(new Error(\"请输入正确的值\"));\n    }\n    callback();\n  }"
        },
        {
          "type": "func",
          "title": "自定义函数规则",
          "value": {
            "trigger": "blur",
            "func": "callback()"
          }
        },
        {
          "type": "high",
          "title": "高级模式",
          "value": {
            "message": "必填",
            "trigger": "change",
            "required": true,
            "ruleType": "1"
          }
        }
      ],
      "default": "",
      "csslist": []
    }
  }
]

最后渲染表单组件时再处理每个表单的校验规则, 拼接成 element-plus 要求的数据接口即可.

如上数据结构,最后转换为如下 校验规则 json:

{
  "rules": {
    "Text_N-A6Ft1FUd8NJyrWN3LL2": [
      // ...
    ]
  }
}

校验实现

上文可知, 三种校验方式(自定义枚举、自定义校验函数、高级模式), 目的是转换为 element-plus 要求的规则

只要我们把配置的规则进行转换就能实现校验

function getFormListRules(rules: any[]) {
  const result: any[] = [];
  if (Array.isArray(rules) && rules && rules.length > 0) {
    rules.forEach((item) => {
      if (item.type == "enum") {
        // 自定义枚举 上文可知我们枚举出来的是函数字符串, 使用eval执行返回函数
        const func = eval(`(${item.value})`);
        result.push({
          validator: func,
          trigger: "blur",
        });
      } else if (item.type == "func") {
        // 自定义函数校验 配置的是函数体 所以我们需要自己拼接出函数, 第四个参数需要通过使用默认参数的形式进行传参(利用到了闭包), 否则element-plus底层获取不到当前表单数据
        const mainData = props.formResult;
        const func = eval(`((rule, value, callback, mainData = mainData) => {${item.value.func}})`);
        result.push({
          validator: func,
          trigger: "blur",
        });
      } else if (item.type == "high") {
        //  高级模式 是通过表单配置出element-plus支持的数据, 所以不做特殊处理, 因为数字校验min, max使用表单配置校验不成功, 所有兜底使用了函数校验
        if (item.value.ruleType == 5) {
          result.push({
            validator: eval(item.value.validor),
            trigger: item.value.trigger,
          });
          return;
        }
        result.push(item.value);
      }
    });
  }
  return result;
}
const rules = ref({});
function getRules(item) {
  let rule = [];
  if (item.data.required) {
    rule.push({
      required: true,
      message: "请输入" + item.data.label,
      trigger: "blur",
    });
  }
  if (typeof item.data.rule == "string") {
    rule = rule.concat(proxy.$Flex.tryParseJson(item.data.rule));
  } else {
    rule = rule.concat(getFormListRules(item.data.rule));
  }
  // 特殊的jsoneditor表单要单独处理
  if (item.data.json) {
    rule.push(...proxy.$Flex.getJsonValidate());
  }
  rules.value[item.data.fieldName] = rule;
}

template 上使用

<el-form ref="ruleForm" :model="formResult" :rules="rules"> </el-form>

最后就能转换出 element-plus 用到校验数据格式

总结

三种校验方案都是基于 element-plus 提供的规则, 合理利用和发掘, 对其进行封装设计, 能在程序上极大提升配置效率, 降低门槛

自定义函数校验还是有一定的缺陷, 如自定义代码有缺陷或者抛错, 外层未进行兜底等, 后期会进行解决

github 地址

预览

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

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

相关文章

精读《useRef 与 createRef 的区别》

1 引言 useRef 是常用的 API&#xff0c;但还有一个 createRef 的 API&#xff0c;你知道他们的区别吗&#xff1f;通过 React.useRef and React.createRef: The Difference 这篇文章&#xff0c;你可以了解到何时该使用它们。 2 概述 其实原文就阐述了这样一个事实&#xf…

ICT产品供应链安全现状分析与对策建议

本文尝试分析ICT产品供应链的安全背景、政策法规&#xff0c;并给出具体针对性的建议供ICT产品供应链的供需双方参考。 当今网络安全威胁和攻击的形式正在发生深刻的变化&#xff0c;已经从传统的网络安全领域逐渐扩展到供应链安全方向。ICT&#xff08;信息通信技术&#xff…

[Qt学习笔记]Qt实现鼠标点击或移动时改变鼠标的样式以及自定义鼠标样式

1、鼠标样式介绍以及对应函数 在Qt中大概有20种左右的内置鼠标样式&#xff0c;一般使用setCursor(Qt::CursorShape shape)来进行设置&#xff0c;一般常用的有标准箭头、手型&#xff0c;双箭头等等形状&#xff0c;对于不同的操作系统下&#xff0c;鼠标的样式显示会略有差别…

2.Labview字符串与路径精讲(上) — 理论篇

本章讲解labview中的字符串和路径及其使用方法&#xff0c;从前面板字符串属性到后面板字符串函数应用做出详细概述&#xff0c;通过本文的学习希望大家了解到字符串在labview编程中的重要地位。 本系列文章为labview 从基础到强化到精通的学习文章&#xff0c;大家可以随时点进…

《Adaptive Adversarial Patch Attack on Face Recognition Models》论文分享(侵删)

原文地址&#xff1a;Adaptive Adversarial Patch Attack on Face Recognition Models | IEEE Conference Publication | IEEE Xplore author{Bei Yan and Jie Zhang and Zheng Yuan and Shiguang Shan}, title{Adaptive Adversarial Patch Attack on Face Recognition Models…

springcloud-Eureka注册中心

如果你要理解这个技术博客博客专栏 请先学习以下基本的知识&#xff1a; 什么是微服务什么是服务拆分什么是springcloud Springcloud为微服务开发提供了一个比较泛用和全面的解决框架&#xff0c;springcloud继承了spring一直以来的风格——不重复造轮子&#xff0c;里面很多的…

c++ 指针大小

C的一个指针占内存几个字节&#xff1f; 结论&#xff1a; 取决于是64位编译模式还是32位编译模式&#xff08;注意&#xff0c;和机器位数没有直接关系&#xff09; 在64位编译模式下&#xff0c;指针的占用内存大小是8字节在32位编译模式下&#xff0c;指针占用内存大小是4字…

JavaScript 使用 Promise 实现 sleep 休眠

以下为代码实现&#xff0c;该代码实现了每隔1秒打印一次当前时间&#xff0c;总共打印5次的功能 for(let i 1; i < 5; i){console.log(new Date().toString())await new Promise(resolve>setTimeout(resolve,1000)) }实现休眠的核心代码为: await new Promise(resolv…

回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测

回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SAO-LSTM雪消融…

使用el-cascader组件写下拉级联多选并且具有全选功能

样式 说明&#xff1a; 级联选择器中加上全选的按钮&#xff0c; 并且保证数据响应式。 思路 因为是有全选的功能&#xff0c;所以不能直接使用el-cascader组件&#xff0c; 而是选择使用el-select组件&#xff0c; 在此组件内部使用el-cascader-panel级联面板全选按钮也是…

【机器学习】科学库使用第2篇:机器学习概述,学习目标【附代码文档】

机器学习&#xff08;科学计算库&#xff09;完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;机器学习&#xff08;常用科学计算库的使用&#xff09;基础定位、目标&#xff0c;机器学习概述定位,目标,学习目标,学习目标。机器学习概述&#xff0c;1.3 人…

idea-不同项目使用不同maven版本

背景 idea一直使用的是maven3.6. 新接的项目要求maven要3.9&#xff0c;所以down下来以后maven的dependencies一直加载失败。 报错信息为 Full classname legend:CustomModelValidator: "org.jetbrains.idea.maven.server.embedder.CustomModelValidator" DefaultM…

RabbitMQ--03--SpringAMQP(SpringBoot集成RabbitMQ)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 SpringAMQP1.SpringBoot 的支持https://spring.io/projects/spring-amqp 2.RabbitTemplate3.RabbitListener&#xff08;终极监听方案&#xff09;4.RabbitConfig--…

用pdf2docx将PDF转换成word文档

pdf2docx是一个Python模块&#xff0c;可以将PDF文件转换为docx格式的Word文档。 pdf2docx模块基于Python的pdfminer和python-docx库开发&#xff0c;可以在Windows、Linux和Mac系统上运行。它可以从PDF文件中提取文本和图片&#xff0c;并将其转换成可编辑的Word文档&#xf…

Photoshop 2024让图像处理更智能、更高效@

Photoshop 2024是一款功能强大的图像处理软件&#xff0c;广泛应用于创意设计和图像处理领域。它提供了丰富的绘画和编辑工具&#xff0c;包括画笔、铅笔、颜色替换、混合器画笔等&#xff0c;使用户能够轻松进行图片编辑、合成、校色、抠图等操作&#xff0c;实现各种视觉效果…

CAPL如何实现TCP Packet的option字段

在TCP协议中,主机可以根据自身的需要决定TCP通信时是否携带option字段,来扩展TCP功能。option字段属于TCP首部的扩展部分,且是可选项,TCP根据首部中的offset字段值确定TCP报文是否携带option字段。 TCP首部固定的部分有20个字节,如果没有扩展部分(option字段),20个字节…

万界星空科技铜杆加工行业生产管理MES系统

传统的铜管加工方法有&#xff1a; &#xff08;1&#xff09;铜管挤压加工技术&#xff08;2&#xff09;铜管上引连铸法&#xff08;3&#xff09;铜管(有缝)焊接生产技术&#xff08;4&#xff09;铸轧法生产精密铜管铸轧法 生产精密铜管是一种全新的生产工艺&#xff0c;…

像uniapp image标签一样对图片进行缩放和裁剪

像uniapp image标签一样对图片进行缩放和裁剪 0 前言提示1 实现1.1 不保持纵横比缩放图片&#xff0c;使图片的宽高完全拉伸至填满 image 元素1.2 保持纵横比缩放图片&#xff0c;使图片的长边能完全显示出来。也就是说&#xff0c;可以完整地将图片显示出来。1.3 保持纵横比缩…

git如何在某个commitId的状态提交到一个分支

有些时候&#xff0c;我们在使用子仓库&#xff0c;或者其他情况&#xff0c;会有一个状态是当前的git仓库是在一个commitId上&#xff0c;而没有在一个分支上&#xff1a; 这时如果想要把基于这个commitId创建一个分支&#xff0c;可以使用下面这个命令&#xff1a; git push…

ubuntu20.04搭建rtmp视频服务

1.安装软件 sudo apt-get install ffmpeg sudo apt-get install nginx sudo apt-get install libnginx-mod-rtmp 2.nginx配置 修改/etc/nginx/nginx.conf文件&#xff0c;在末尾添加&#xff1a; rtmp {server {listen 1935;application live {live on;}} } 3.视频测试 本…