格子表单GRID-FORM | 嵌套子表单与自定义脚本交互

news2024/11/17 7:20:08

格子表单/GRID-FORM已在Github 开源,如能帮到您麻烦给个星🤝

GRID-FORM 系列文章

  • 基于 VUE3 可视化低代码表单设计器
  • 嵌套表单与自定义脚本交互

新版本功能 🎉

不觉间,GRID-FORM 已经开源一年(2023年1月29日首次提交),初始版本功能较为简单,能用但很死板。后来陆续进行小版本迭代,增加诸如数据联动右键菜单等,可是作为常用且必要的嵌套(子表单)按钮功能一直没有实现。于是就有了今年的第一个0.1.1版本:

  • 支持嵌套容器(子表单)
  • 支持自定义脚本交互
  • 新增 Element Plus 渲染器,完善 Vant4 渲染器
  • 新增组件:按钮、图片、静态表格

目前具备的模块与组件如下图(带边框为新增功能)所示:

运行时截图

表单渲染效果

从左到右分别是 NaiveUI、ElementPlus、Vant4对于同一表单的渲染效果

可视化设计器

子表单(嵌套)

所谓子表单,可以理解为大背包里面的小包,底下可以添加子字段,同时支持录入多条数据;常见应用于录入字段格式固定、条数不定的数据清单。

按照 GRID-FORM 的设计,初始表单为一个顶层容器,能够定义标签样式(如位置、对齐方式)、格子列数、尺寸大小等布局属性,还可以嵌套子容器(如上图中的外层容器子容器1子容器2),每个容器均能定义其布局属性,理论上支持无限嵌套(递归渲染)。
渲染流程

嵌套类型

子表单能够设置如下类型:

类型说明
仅布局只作为布局上的分组,字段均为同级
单个嵌入一个对象到父字段
多行嵌入多个格式固定的对象(数组)到父字段

下面我用一个实际例子说明,比如要录入一则学生信息,字段包含:

字段名说明
姓名、年龄、籍贯仅布局三个同级基本信息
专业信息单个数据:名称、学院、学年
教育经历多行数据:开始日期、结束日期、学校

最终得到的表单数据:

{
    姓名 : "张三",
    年龄 : 19,
    籍贯 : "广西",
    专业 : {
        名称 : "水利水电工程",
        学院 : "土木建筑工程学院",
        学年 : 4
    },
    教育经历 : [
        { 
            开始日期 : "2011.09",
            结束日期 : "2020.06",
            学校 : "XX市第一小学"
        },
        { 
            开始日期 : "2020.09",
            结束日期 : "2023.06",
            学校 : "XX市第一高级中学"
        }
    ]
}

效果演示

核心代码

<template>
    <template v-if="isMultiple">
        <table class="gf-render-table">
            <tr v-for="(rowData, rowIndex) in formData" :class="{striped:rowIndex%2==1}">
                <td width="40" class="c">
                    <n-popconfirm :negative-text="null" @positive-click="formData.splice(rowIndex, 1)">
                        <template #trigger>
                            <n-button size="small" type="primary" tertiary circle>{{rowIndex+1}}</n-button>
                        </template>
                        删除第{{rowIndex+1}}行数据?
                    </n-popconfirm>
                </td>
                <td>
                    <n-grid :x-gap="gridGap" :y-gap="gridGap" :cols="form.grid" :style="{width: form.width, margin:'0px auto' }">
                        <template v-for="(item, index) in form.items" :key="index">
                            <n-form-item-gi v-if="item._hide!=true" :span="item._col" :show-feedback="false" :show-label="!(item._hideLabel === true || !form.labelShow)"
                                :label-placement="form.labelPlacement" :label-align="form.labelAlign" :label-width="form.labelWidth">
                                <template #label>
                                    {{item._text}}<span v-if="item._required" style="color: red;"> *</span>
                                </template>

                                <component v-if="item._container && item.items" :is="buildComponent(item, renders, false)">
                                    <render-container :gridGap="gridGap" :renders="renders" :form="item" :formData="childForm(item)" :labelPlacement="item.labelPlacement" :labelAlign="item.labelAlign" />
                                </component>
                                <component v-else-if="item._widget=='DATE'" v-model:formatted-value="rowData[item._uuid]" :is="buildComponent(item, renders, false)" />
                                <component v-else v-model:value="rowData[item._uuid]" :is="buildComponent(item, renders, false)" />
                            </n-form-item-gi>
                        </template>
                    </n-grid>
                </td>
            </tr>
        </table>
        <div style="margin-top: 10px; text-align: center;">
            <n-button size="small" :disabled="!canAdd" circle @click.stop="onAddRow">+</n-button>
        </div>
    </template>
    <template v-else>
        <n-grid :x-gap="gridGap" :y-gap="gridGap" :cols="form.grid" :style="{width: form.width, margin:'0px auto' }">
        <template v-for="(item, index) in form.items" :key="index">
            <n-form-item-gi v-if="item._hide!=true" :span="item._col" :show-feedback="false" :show-label="!(item._hideLabel === true || !form.labelShow)"
                :label-placement="form.labelPlacement" :label-align="form.labelAlign" :label-width="form.labelWidth">
                <template #label>
                    {{item._text}}<span v-if="item._required" style="color: red;"> *</span>
                </template>

                <component v-if="item._container && item.items" :is="buildComponent(item, renders, false)">
                    <render-container :gridGap="gridGap" :renders="renders" :form="item" :formData="childForm(item)" :labelPlacement="item.labelPlacement" :labelAlign="item.labelAlign" />
                </component>
                <component v-else-if="item._widget=='DATE'" v-model:formatted-value="formData[item._uuid]" :is="buildComponent(item, renders, false)" />
                <component v-else v-model:value="formData[item._uuid]" :is="buildComponent(item, renders, false)" />
            </n-form-item-gi>
        </template>
    </n-grid>
    </template>
</template>
<script setup>
    import { ref, computed } from 'vue'

    import { ContainerProps, ContainerMixin } from '@grid-form/common/render.mixin'
    import { buildComponent } from '@grid-form/common'

    const props = defineProps(ContainerProps)

    const { isMultiple, canAdd, childForm, onAddRow } = ContainerMixin(props)
</script>

脚本交互

  1. 增加支持交互(单击、双击)的组件:按钮、图片
  2. 优化运行时函数:表单项数组对象增加$(致敬 JQuery😄)方法,便于快速按 ID/编号 查找内容
//找到编号为 name 的表单项(返回首个匹配值),并禁用
items.$("name").disabled = true
//找到编号为'name'、_text为'专业名称'的表单项(返回首个匹配值),并禁用
items.$({_uuid:"name", "_text":"专业名称"}).disabled = true

结语

因个人能力有限,此工具在设计、实现上存在诸多不足,仅作学习交流🙂。

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

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

相关文章

蓝桥杯---加法变乘法

我们都知道:123 ….. 491225&#xff0c;现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如&#xff1a;123 ... 10*1112 ... 27*2829 ... 492015 就是符合要求的答案. 请你寻找另外一个可能的答案,并把位置靠前的那个乘号左边的数字提交(对于示例,就是 提交10)…

inode生命周期

1.添加inode到inode cache链表 当inode的引用计数器i_count为0后&#xff0c;会调用iput_final去释放 static void iput_final(struct inode *inode) {struct super_block *sb inode->i_sb;const struct super_operations *op inode->i_sb->s_op;unsigned long sta…

07.领域驱动设计:了解3种常见微服务架构模型的对比和分析

目录 1、概述 2、整洁架构 3、六边形架构 4、三种微服务架构模型的对比和分析 5、从三种架构模型看中台和微服务设计 5.1 中台建设要聚焦领域模型 5.2 微服务要有合理的架构分层 5.2.1 项目级微服务 5.2.2 企业级中台微服务 5.3 应用和资源的解耦与适配 6、总结 1、概…

云计算底层技术、磁盘技术揭秘虚拟化管理、公有云概述

查看本机是否具备虚拟化支持 硬件辅助虚拟化 处理器里打开 虚拟化Inter VT-x/EPT 或AMD-V 构建虚拟化平台工具软件包 yum 与 dnf Yum和DNF都是用于管理Linux系统中的软件包的工具&#xff0c;但它们在许多方面存在一些差异。以下是一些可能的区别&#xff1a; 依赖解…

Qt|QPushButton控件讲解

前提 按钮分为了四种状态&#xff1a;常态、聚焦、按下、禁用 前一段时间更新了MFC框架下CButton的自绘。因为MFC框架下的按钮限制性很高&#xff0c;所以只能由自绘实现各种风格&#xff0c;但是QT框架完美的解决了这个问题&#xff0c;我们只需要了解如何调用&#xff0c;就…

Doris简介及单机部署(超详细)

文章目录 一、Doris简介1、Doris介绍2、Doris架构 二、Doris单机部署&#xff08;Centos7.9&#xff09;1、下载Doris2、准备环境3、安装部署3.1 创建存储目录3.2 配置 FE3.3 启动 FE3.4 查看 FE 运行状态3.5 配置 BE3.6 启动 BE3.7 添加 BE 节点到集群3.8 查看 BE 运行状态3.9…

elementui中的tree自定义图标

需求&#xff1a;实现如下样式的树形列表 自定义树的图标以及点击时&#xff0c;可以根据子级的关闭&#xff0c;切换图标 <el-tree :data"treeList" :props"defaultProps"><template #default"{ node, data }"><span class&quo…

C++PythonC# 三语言OpenCV从零开发(7):图像的阈值

文章目录 相关链接前言阈值阈值使用代码PythonCCsharpcsharp代码问题 总结 相关链接 C&Python&Csharp in OpenCV 专栏 【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程&#xff08;附带课程课件资料课件笔记&#xff09; OpenCV一个窗口同时显示多张图片 …

C++提取ICO图标(PE文件资源提取)

最近需要写一个提取EXE或者DLL图标资源的功能, 网上找了很久, 要么功能不好用, 最后结果如下: 1.很多是加载为HICON句柄后转换为图片保存, 全损画质..., 2.后来找了个还能用的, 详见 https://github.com/TortoiseGit/TortoiseGit/blob/master/src/Utils/IconExtractor.cpp …

【Axure教程0基础入门】04交互动效基础

04交互动效基础 1.Axure交互事件的基本概念 &#xff08;1&#xff09;交互动效Interaction 原型图中&#xff0c;原件与页面的动态效果&#xff08;dynamic behaviors&#xff09;。 &#xff08;2&#xff09;交互动效的构成 目标&#xff08;target&#xff09;&#xff1a;…

Windows系统本地安装Wnmp服务并结合内网穿透公网远程访问

目录 前言 1.Wnmp下载安装 2.Wnmp设置 3.安装cpolar内网穿透 3.1 注册账号 3.2 下载cpolar客户端 3.3 登录cpolar web ui管理界面 3.4 创建公网地址 4.固定公网地址访问 结语 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊Windows…

我用Rust开发Rocketmq name server

我是蚂蚁背大象(Apache EventMesh PMC&Committer)&#xff0c;文章对你有帮助给Rocketmq-rust star,关注我GitHub:mxsm&#xff0c;文章有不正确的地方请您斧正,创建ISSUE提交PR~谢谢! Emal:mxsmapache.com 1. Rocketmq-rust namesrv概述 经过一个多月的开发&#xff0c;终…

【Docker】docker安装jenkins

一、执行命令 下载jenkins镜像 #下载jenkins 镜像 docker pull jenkins/jenkins:latest-jdk8 启动jenkins容器 #启动jenkins 容器 #挂载 如果不挂载 每次启动jenkins的配置、插件、用户等信息都没有了 #jenkins_home 包含jenkins配置、插件、用户等信息。 要指定必须配置用…

StarRocks-3.1.0 单节点部署

1. 相关环境准备 FE&#xff1a; /opt/starrocks BE&#xff1a; /opt/starrocks 安装包下载 wget https://releases.starrocks.io/starrocks/StarRocks-3.1.0.tar.gz解压缩 tar -zxvf StarRocks-3.1.0.tar.gz 安装jdk (v2.5 及以上版本建议安装 JDK 11&#xff0c;我们使用…

260:vue+openlayers 通过webgl方式加载矢量图层

第260个 点击查看专栏目录 本示例介绍如何在vue+openlayers中通过webgl方式加载矢量图层。在做这个示例的时候,采用vite的方式而非webpack的方式。这里的基础设置需要改变一下。 ol的版本7.5.2或者更高。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果 文…

探索数字经济:从基础到前沿的奇妙旅程

新一轮技术革命方兴未艾&#xff0c;特别是以人工智能、大数据、物联网等为代表的数字技术革命&#xff0c;催生了一系列新技术、新产业、新模式&#xff0c;深刻改变着世界经济面貌。数字经济已成为重组全球要素资源、重塑全球经济结构、改变全球竞争格局的关键力量。预估到20…

ICLR 2024 Oral|微软推出多模态数学测试基准MathVista,挑战Google Gemini

微软等机构提出了首个以视觉场景为核心的数学推理基准 — MathVista。他们提供了一份长达 116 页的详尽报告&#xff0c;其中深入剖析了包括 GPT-4V 在内的 12 个大型模型的性能。令人瞩目的是&#xff0c;MathVista 已被 Google Gemini 采纳为其多模态数学测试的标准之一。此项…

Docker 安装与基本操作

目录 一、Docker 概述 1、Docker 简述 2、Docker 的优势 3、Docker与虚拟机的区别 4、Docker 的核心概念 1&#xff09;镜像 2&#xff09;容器 3&#xff09;仓库 二、Docker 安装 1、命令&#xff1a; 2、实操&#xff1a; 三、Docker 镜像操作 1、命令&#xff1…

Unity中使用Ultraleap的InteractionButton组件

本节在上一节基础上进行&#xff0c;上一小结参考如下&#xff1a; Unity中创建Ultraleap 3Di交互项目 本节工程文件如下&#xff1a; Unity中使用Ultraleap的InteractionButton组件 本节结构有所更改&#xff0c;主要是参考官方示例结构进行重新调整&#xff0c;和上一小节相…

torch与cuda\cudnn和torchvision的对应

以上图片来源于这篇博客 于是&#xff0c;我需要手动下载0.9.0torchvision 直接在网站https://pypi.tuna.tsinghua.edu.cn/simple/后面加上torchvision&#xff0c;就不用ctrlF搜torchvision了&#xff0c;即进入下面这个网站&#xff0c;找到对应版本的包下载安装即可 https…