Gitee千Star优质项目解析: ng-form-element低开引擎解析

gitee,star,ng,form,element · 浏览次数 : 3

小编点评

## Project Open Source Analysis **Summary:** This document provides an analysis of a low-code project with open-source components and contributions. **Key Points:** * The project contains various components, some closed-source and some open-source. * Open-source components include the core engine and some UI components. * The project's source code is entirely open-source, showcasing transparency and encouraging collaboration. * This project serves as a valuable example of genuine open-source, free to use and modify. **Components:** The project features the following components: * Input field with dynamic label * Radio buttons with dynamic values * Button with custom size and styling * Rating component with validation rules **Benefits of Open-Source Contribution:** * **Transparency:** Open-source code allows for scrutiny and contributions. * **Collaboration:** Open-source licenses encourage community engagement and knowledge sharing. * **Flexibility:** Developers can customize and extend the components to meet their specific needs. * **Accessibility:** Open-source projects are often more readily available for integration into other projects. **Conclusion:** The project analyzed demonstrates the power and importance of open-source software development. By contributing to an open-source project, developers can gain valuable insights, build a strong community, and make a meaningful impact. **Additional Notes:** * The project's configuration specifies label position, width, and other properties. * The rules section defines validation for the rating component. * The project's open-source nature allows for further exploration and potential enhancements.

正文

好家伙,

在写项目的时候,我发现自己的平台的组件写的实在是太难看了,于是想去gitee上偷点东西,于是我们本期的受害者出现了

gitee项目地址

https://gitee.com/jjxliu306/ng-form-elementplus-sample.git

组件库以及引擎完全开源,非常牛逼的项目,非常牛逼的作者

 

项目名:ng-form-element

整体的布局,组件样式,编辑器模板,组件拖动时的过渡动画,都写的非常漂亮,(比我写的好看多了)

于是我决定,扒一下他的内裤,学习(抄袭)一下

 

0.项目解析

官方文档:NG-FORM

于是我们快速定位到引擎部分

 

1.开始分析

我们知道,低开的引擎做的事无非是

  把数据

  变成视图

 

我们先来找数据

数据部分

//packages/index.vue

  data() {
    return {
      selectItem: {},
      arrow: false,
      i18nkey: getUUID(),
      formTemplate: this.template || {
              list: [
              ],
              config: {
                labelPosition: 'left',
                labelWidth: 100,
                size: 'mini',
                outputHidden: true, //  是否输出隐藏字段的值 默认打开,所有字段都输出
                hideRequiredMark: false,
                syncLabelRequired: false,
                labelSuffix: '' , // 标签后缀
                customStyle: ''
              }
            },
    }
  },
  props: {
    template: {
      type: Object,
      default: () => {
        return {
          list: [],
          config: {
            labelPosition: 'top',
            labelWidth: 80,
            size: 'mini',
            outputHidden: true, //  是否输出隐藏字段的值 默认打开,所有字段都输出
            hideRequiredMark: false,
            syncLabelRequired: false,
            labelSuffix: '' , // 标签后缀
            customStyle: ''
          }
        }
      }
    },

 

然后来找视图部分

//packages/index.vue

<ContainerPanel
              :formTemplate="formTemplate"
              @handleSelectItem="handleSelectItem"
              :selectItem="selectItem"
              :arrow="arrow"
          >
          </ContainerPanel>

import ContainerPanel from './panel-container/index.vue'
  1. :formTemplate:传模板数据的

  2. @handleSelectItem:这是一个事件处理器,当组件内的 handleSelectItem 方法被触发时,会执行传入的回调函数。handleSelectItem 是组件内部定义的事件处理函数名。

  3. :selectItem:这是绑定的数据属性,用于传递一个"被选中的数据"

  4. :arrow:暂时没看出来干嘛的

 

//packages/panel-container/index.vue
<el-form  
          :label-width="formTemplate.config.labelWidth + 'px'" 
          class="ng-form"
          :label-position="formTemplate.config.labelPosition"
          :hide-required-asterisk="formTemplate.config.hideRequiredMark" 
          :label-suffix="formTemplate.config.labelSuffix"
          ref="form" 
          :style="formTemplate.config.customStyle" 
          :size="formTemplate.config.size"
        >
        <el-row :gutter="20" class="row"> 
                <draggable  
                    tag="div"
                    class="draggable-box"
                    v-bind="{
                      group: 'form-draggable',
                      ghostClass: 'moving',
                      animation: 180,
                      handle: '.drag-move'
                    }"
                    :force-fallback="true"
                    v-model="formTemplate.list" 
                    @add="dragEnd($event, formTemplate.list)" 
                      >
                    <transition-group tag="div" name="list" class="items-main"> 
                        <Node  
                                :class="{'drag-move' : record.drag_ == undefined || record.drag_  }"
                                v-for="record in formTemplate.list"
                            :key="record.key"
                            :record="record"
                            :isDrag="true"
                            :config="formTemplate.config"
                            :selectItem="selectItem"
                            @handleSelectItem="handleSelectItem"
                            @handleCopy="handleCopy(record)"
                            @handleDetele="handleDetele(record)"
                            >  
                        
                        </Node> 
                    </transition-group>
                </draggable> 
         
        </el-row> 
    </el-form> 
import Item from '../items/index.vue'

 

  1. <transition-group> 是 Vue.js 的内置过渡组件,用于给列表添加过渡效果。

  2. el-form的作用我们后面说
//packages/form-design/items/index.vue
<template>    
  <ItemNode 
    v-if="isLayout"
    :record="record"
    :disabled="disabled" 
    :preview="preview"
    :isDragPanel="isDragPanel"
    :prop-prepend="propPrepend"
    :selectItem="selectItem" 
    :style="{'display': recordVisible ? '' : 'none'}"
    :models="models" 
    @handleSelectItem="handleSelectItem" 
    >
      <!-- 递归传递插槽!!! -->
      <template v-for="slot in Object.keys($slots)"  :slot="slot">
        <slot :name="slot" :record="record"/>
      </template>
    </ItemNode> 
  <el-form-item 
    v-else
    :label="label" 
    :style="{'display': recordVisible ? '' : 'none'}"
    :rules="recordRules"
    :prop="recordProps"
    :key="record.key"
    :required="recordRequired" 
    :id="record.model" 
    :name="record.model"
    :label-width="labelWidth"
    >       
    <ItemNode 
      :record="record"
      :disabled="disabled" 
      :preview="preview"
      :isDragPanel="isDragPanel"
      :selectItem="selectItem" 
      :prop-prepend="propPrepend"
      :models="models" 
      @handleSelectItem="handleSelectItem"
      >
        <!-- 递归传递插槽!!! -->
        <template v-for="slot in Object.keys($slots)"  :slot="slot">
          <slot :name="slot" :record="record"/>
        </template>
      </ItemNode> 
  </el-form-item>  
</template>

import ItemNode from './node.vue'

 

  1.v-if="isLayout":是否为预览模式

 

//packages/form-design/items/node.vue

<template>

  <component
        :record="record"
        :style="{
          margin: record.margin && record.margin.length > 0 ? record.margin.join('px ') + 'px' : '0px',
          borderRadius: (record.itemBorderRadius ? record.itemBorderRadius : 0) + 'px',
          backgroundColor: record.backgroundColor ? record.backgroundColor  : '',

        }"
        :disabled="disabled"
        :preview="preview"
        :isDragPanel="isDragPanel"
        :selectItem="selectItem"
        :prop-prepend="propPrepend"
        :models.sync="models"
        @handleSelectItem="handleSelectItem"
        @handleFocus="handleFocus"
        @handleBlur="handleBlur"
        :is="customComponent">
      <!-- 递归传递插槽!!! -->
      <template v-for="slot in Object.keys($slots)"  :slot="slot">
        <slot :name="slot" :record="record"/>
      </template>  
  </component>
</template>

 

ok终于到了最后一层

最终的关键就是这么行代码

  • :is="customComponent":动态绑定组件名称,根据 customComponent 的值来渲染不同的组件。
customComponent() {

      // 判断是否自定义组件
      if(this.customComponents && this.customComponents.length > 0) {
        const cs = this.customComponents.filter(t=> t.type == this.record.type)

        if(cs && cs.length > 0) {
          return cs[0].component
        }
      }

      const selectItemType = this.record.type
            // 将数组映射成json
      if(this.items && this.items.length > 0) {
            for(let i = 0 ; i < this.items.length ; i++) {
              const itemList = this.items[i]

              if(itemList.list && itemList.list.length > 0) {
                const fs = itemList.list.filter(t=>t.type == selectItemType)
                if(fs && fs.length > 0) {
                  return fs[0].component
                }
              }

            }
      }
      return null
    },

 

 

2.数据格式

在这个项目上随便做的一个表格并导出数据

 

{
    "list": [
        {
            "type": "input",
            "options": {
                "defaultValue": "",
                "type": "text",
                "prepend": "",
                "append": "",
                "placeholder": "请输入",
                "maxLength": 0,
                "clearable": false,
                "hidden": false,
                "disabled": false
            },
            "label": "输入框",
            "labelWidth": -1,
            "width": "100%",
            "span": 24,
            "model": "input_17156872001522",
            "key": "input_17156872001522",
            "rules": [
                {
                    "required": false,
                    "message": "必填项",
                    "trigger": [
                        "blur"
                    ]
                }
            ],
            "dynamicLabel": false
        },
        {
            "type": "radio",
            "options": {
                "defaultValue": "",
                "placeholder": "请输入",
                "dynamic": 0,
                "options": [
                    {
                        "value": "1",
                        "label": "选项1"
                    },
                    {
                        "value": "2",
                        "label": "选项2"
                    }
                ],
                "methodType": "get",
                "dynamicPostData": "",
                "remoteFunc": "",
                "dataPath": "",
                "remoteValue": "",
                "remoteLabel": "",
                "dictType": "",
                "disableItemScript": "",
                "hidden": false,
                "disabled": false,
                "linkage": false,
                "linkData": []
            },
            "label": "单选框",
            "labelWidth": -1,
            "width": "100%",
            "span": 24,
            "model": "radio_17156872321432",
            "key": "radio_17156872321432",
            "rules": [
                {
                    "required": false,
                    "message": "必填项",
                    "trigger": [
                        "blur"
                    ]
                }
            ],
            "dynamicLabel": false
        },
        {
            "type": "button",
            "event_": false,
            "listen_": false,
            "options": {
                "size": "mini",
                "type": "primary",
                "align": "left",
                "control": "",
                "eventName": "",
                "script": "",
                "plain": false,
                "circle": false,
                "round": false,
                "disabled": false
            },
            "label": "按钮",
            "labelWidth": 0,
            "width": "100%",
            "span": 24,
            "model": "button_17156901763582",
            "key": "button_17156901763582",
            "dynamicLabel": false
        },
        {
            "type": "rate",
            "options": {
                "max": 5,
                "defaultValue": 0,
                "allowHalf": false,
                "hidden": false,
                "disabled": false
            },
            "label": "评分",
            "labelWidth": -1,
            "width": "100%",
            "span": 24,
            "model": "rate_17156901773022",
            "key": "rate_17156901773022",
            "rules": [
                {
                    "required": false,
                    "message": "必填项",
                    "trigger": [
                        "blur"
                    ]
                }
            ],
            "dynamicLabel": false
        }
    ],
    "config": {
        "labelPosition": "top",
        "labelWidth": 80,
        "size": "mini",
        "outputHidden": true,
        "hideRequiredMark": false,
        "syncLabelRequired": false,
        "labelSuffix": "",
        "customStyle": ""
    }
}
  1. type:表示该组件的类型,该对象的类型为 "rate",用于评分。
  2. options:表示该组件的选项,包括:
  3. label:表示该组件的标签文本,值为 "评分"。
  4. labelWidth:表示该组件的标签宽度,值为 -1,表示使用系统默认值。
  5. width:表示该组件的宽度,值为 "100%"。
  6. span:表示该组件所占的栅格数,值为 24。
  7. model:表示该组件的 v-model 绑定值的变量名,值为 "rate\_17156901773022"。
  8. key:表示该组件的唯一标识,值为 "rate\_17156901773022"。
  9. rules:表示该组件的校验规则,包括:
  10. dynamicLabel:表示该组件的标签是否动态显示,值为 false。

 

 3.总结

在翻了许许多多的低开项目后,发现,

巨大多数的低开项目要么引擎核心闭源,要么物料组件库闭源

而这个项目,所有的东西都开源了,真真正正的开源,真的牛bi

非常值得自学的一个低开项目

 

与Gitee千Star优质项目解析: ng-form-element低开引擎解析相似的内容:

Gitee千Star优质项目解析: ng-form-element低开引擎解析

好家伙, 在写项目的时候,我发现自己的平台的组件写的实在是太难看了,于是想去gitee上偷点东西,于是我们本期的受害者出现了 gitee项目地址 https://gitee.com/jjxliu306/ng-form-elementplus-sample.git 组件库以及引擎完全开源,非常牛逼的项

openEuler technical-certification

https://gitee.com/meitingli/technical-certification/ 介绍 存放openEuler技术测评相关的文档,包括技术测评标准、流程、指导性文档等 技术测评概述 欧拉技术测评:基于欧拉操作系统,含开源版本和商业版本,对软件伙伴产品及解决方案,完成的多样性算

[转帖]通过Shell脚本自动监控JAVA进程中线程cpu使用率

https://gitee.com/jialy/auto-monitor-java-process/tree/master 本文主要介绍在 show-busy-java-threads.sh 脚本的功能基础上,通过 process-cpu-monitor.sh 脚本实现Linux平台上Java进程或

[转帖]openEuler官方容器镜像仓

https://gitee.com/openeuler/openeuler-docker-images 介绍 这里存放着由openEuler官方提供的容器镜像,包含openEuler基础镜像、应用镜像。 openEuler基础镜像 openEuler的基础镜像由社区官方发布,目前发布在openEul

[转帖]理解 BBR 拥塞控制算法--理论篇

https://switch-router.gitee.io/blog/bbr1/ 简介 BBR (Bottleneck Bandwidth and Round-trip propagation time)是 Google 在 2016 年发布的一套拥塞控制算法。它尤其适合在存在一定丢包率的弱网环境

[转帖]深入理解同步机制---内核自旋锁

https://switch-router.gitee.io/blog/spinlock/ 进程(线程)间的同步机制是面试时的常见问题,所以准备用一个系列来好好整理下用户态与内核态的各种同步机制。本文就以内核空间的一种基础同步机制—自旋锁开始好了 自旋锁是什么 自旋锁就是一个二状态的原子(atomi

[转帖]TCP timestamp 选项那点事

https://switch-router.gitee.io/blog/tcp-timestamp/ TCP 最早在 RFC1323 [] 中引入了 timestamp 选项, 并在后来的 RFC7323 中进行了更新。引入 timestamp 最初有两个目的:1.更精确地估算报文往返时间(roun

[转帖]Flannel 环境搭建与分析

https://switch-router.gitee.io/blog/flanenl/ 介绍 Flannel是CoreOS团队针对Kubernates设计的跨主机容器网络解决方案, 它可以使集群中不同节点上运行的docker容器都具有全集群唯一的虚拟IP地址。 举个例子,在一个由3台主机节点组成系

[转帖]Flannel 环境搭建与分析

https://switch-router.gitee.io/blog/flanenl/ 介绍 Flannel是CoreOS团队针对Kubernates设计的跨主机容器网络解决方案, 它可以使集群中不同节点上运行的docker容器都具有全集群唯一的虚拟IP地址。 举个例子,在一个由3台主机节点组成系

Springboot简单功能示例-1 实现基本WEB服务

博主尝试通过gitee的发行版,使用Springboot为基础框架,逐步整合JWT、JPA、VUE等常用功能项目。 其中博主还将尝试统一异常处理、自定义加密认证、代码自动生成等功能