form-create 低代码开发

低代码开发

前言

我的前端页面比较简单,多为表单类的小工具,所以研究了一下开源的表单设计组件。

例如:

form-create,他的官方示例,支持生成json和导入json。
考虑到集成到项目后,也可以使用官方demo进行解析json,因此采用form-create。
在这里插入图片描述

form-create使用

安装和引入

文档:https://www.form-create.com/v2/guide/
根据文档进行安装和引入

npm i @form-create/element-ui

在main.js里引入

import Vue from 'vue'

import formCreate from '@form-create/iview'
Vue.use(formCreate)

低代码之事件注入

本次实践的低代码,写好一个框架,通过json配置实现表单的生成、提交等操作,期间涉及到的change、click事件也通过执行自定义脚本完成。

低代码时,最需要解决的是如何执行脚本,脚本格式是什么。

经过一些非开源平台的体验,查看他们请求的响应结果,小有思路后付诸实践,目前有两个方案,但pass了一个。
下面小小的记录一下吧。

第一种,也是被pass掉的一种:

  • 由于对开源组件的不熟悉,想不修改他们生成的json,新建一个对象记录自定义的方法;
  • 方法格式:分为方法名、方法入参和方法体,通过new Function(x,x,x)来创建;
  • 创建完不知道放哪,浅浅的挂载到了vue上;(此操作影响巨大)
  • 发现自定义的方法,没有办法和组件对应起来,还得处理重名问题;
  • 到此,另寻出路

第二种,利用事件注入:

  • 事件注入,利用inject对象传入自定义数据;将自定义的脚本放在这里,就可以通过eval执行,解决了change和click等事件;
  • 事件注入要自定义事件前缀,可以统一一下

设计方案

在了解官方文档后,我的设计为:

事件名说明组件返回参数自定义脚本入参
submit表单提交事件formData, fapiformData
gs-changechange事件inject, valformData,val,inject
submit事件详细解释
  1. 组件事件submit,开源组件给的返回值有(formData, fapi);
  2. 固定方法onSubmit,思考用户可能需要的数据,目前给脚本的入参为(formData);
  3. 内部使用eval 执行用户的自定义脚本;
  4. 脚本位置 options.submitEvent,默认值为(formData) => {};

submit事件的json示例:

{
  "formData": {},
  "rule": [],
  "options": {
    "form": {
      "labelPosition": "left",
      "size": "mini",
      "labelWidth": "125px",
      "hideRequiredAsterisk": false,
      "showMessage": true,
      "inlineMessage": false
    },
    "submitBtn": true,
    "resetBtn": false,
    "submitEvent": "(formData) => {\n // 在此编写代码 \n alert(JSON.stringify(formData)) \n}"
  }
}

gs-change事件详细解释
  1. 自定义事件gs-change,开源组件给的返回值有(inject, val);
  2. 固定方法onChange,思考用户可能需要的数据,目前给脚本的入参为(formData, val, inject);
  3. 内部使用eval 执行用户的自定义脚本;
  4. 脚本位置 inject[0],默认值为(formData) => {};

gs-change事件的json示例:

"emitPrefix": "gs",
"emit": [
  {
    "name": "change",
    "inject": [
    	"(formData) => {\n // 在此编写代码}"
   
    ]
  }
]

change注入实践

vue示例

<template>
  <div class="app-container">
    <form-create
      v-model="egData.formData"
      :value.sync="egData.formData"
      :rule="egData.rule"
      :option="egData.options"
      @submit="onSubmit"
      @gs-change="onChange"
      @gs-blur="onBlur"
    />
  </div>
</template>

<script>
import axios from 'axios'
export default {
  data() {
    return {
      egData: {
        // 表单默认值,也作为双向绑定的表单数据
        formData: {},
        rule: [],
        options: {}
      }
    }
  },
  created() {
    this.getConfig()
  },
  methods: {
    getConfig() {
      axios.get('/json/form-create/eg.json').then(res => {
        this.egData = res.data
      })
    },
    onSubmit(formData, fapi) {
      console.log(fapi)
      // eslint-disable-next-line no-eval
      const func = eval(fapi.options.submitEvent)
      func(formData)
    },
    onChange(inject, val) {
      console.log(inject)
      // 具体脚本通过inject指定,格式为 "inject": ["(data, formData)=>{// 自定义}"]
      // eslint-disable-next-line no-eval
      const func = eval(inject.inject[0])
      func(inject.$f.form, inject.self.value, inject)
    },
    onBlur(inject) {
      console.log(inject)
      // eslint-disable-next-line no-eval
      const func = eval(inject.inject[0])
      func(inject.$f.form, inject.self.value, inject)
    }
  }
}
</script>

<style scoped>

</style>

eg.json全部数据:

{
  "formData": {},
  "rule": [
    {
      "type": "FcRow",
      "children": [
        {
          "type": "col",
          "props": {
            "span": 24,
            "offset": 0,
            "push": 0,
            "pull": 0
          },
          "children": [
            {
              "type": "input",
              "field": "f1",
              "title": "change事件",
              "info": "",
              "_fc_drag_tag": "input",
              "hidden": false,
              "display": true,
              "props": {
                "type": "text",
                "maxlength": 20,
                "minlength": 1,
                "showWordLimit": true,
                "clearable": true
              },
              "$required": "测试必填项",
              "validate": [
                {
                  "trigger": "change",
                  "mode": "required",
                  "message": "",
                  "required": true
                }
              ],
              "emitPrefix": "gs",
              "emit": [
                {
                  "name": "change",
                  "inject": [
                    "(formData, data) => {\n // 在此编写代码\n alert('change已输入:' + data)\n}"
                  ]
                }
              ]
            }
          ],
          "_fc_drag_tag": "col",
          "hidden": false,
          "display": true
        }
      ],
      "_fc_drag_tag": "row",
      "hidden": false,
      "display": true
    }
  ],
  "options": {
    "form": {
      "labelPosition": "left",
      "size": "mini",
      "labelWidth": "125px",
      "hideRequiredAsterisk": false,
      "showMessage": true,
      "inlineMessage": false
    },
    "submitBtn": true,
    "resetBtn": false,
    "submitEvent": "(formData) => {\n // 在此编写代码 \n alert(JSON.stringify(formData)) \n}"
  }
}

form-create-designer使用

安装和引入

文档:https://designer.form-create.com/guide/
根据文档进行安装和引入

npm i @form-create/designer

在main.js里引入

import FcDesigner from '@form-create/designer'
Vue.use(FcDesigner)

使用

<fc-designer ref="designer"/>

官方提供的API,常用到的有getRule、getOption、setRule、setOption,基本这四个就可以实现基础的表单;
当然,有更高级的需求,可以去查看文档。
在这里插入图片描述

// 拖拽好后生成json
this.$refs.designer.getRule()
this.$refs.designer.getOption()
// 根据json生成表单
this.$refs.designer.setRule()
this.$refs.designer.setOption()

绑定事件

本次主要想解决表单组件在线绑定事件,使用过一些可体验的低代码平台,最理想的体验方式是把绑定事件放在组件配置里,但是看完一圈API并没有发现可以修改。
后来便试了一下自定义按钮,插槽也并没有返回数据,无法做到组件和事件的一一对应了。
在这里插入图片描述
没事,坚信一定可以解决,便打印了this.$refs.designer,发现在选中组件后,输出结果里面有一个_self对象,包含了当前选中组件的rule。
这意味着,组件和事件可以一一对应上了,那后续的事件注入便没有难度了。
在这里插入图片描述

效果图

由于自定义了事件,组件内置的预览功能便无法满足了,自行开发一个预览。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

代码

<gen-func :active-rule="activeRule" @success="setEvent" />

setEvent(obj) {
      // 用户手动输入事件脚本后,回传到最终数据里
      var rule = this.$refs.designer.getRule()
      var activeRule = this.$refs.designer._self.activeRule
      // 遍历找到当前选中的表单组件,赋值
      var firstElement = rule[0]
      if (firstElement.type === 'FcRow') {
        var cols = firstElement.children
        cols.forEach(col => {
          var formItem = col.children
          if (formItem.field === activeRule.field) {
            formItem.emitPrefix = 'gs'
            formItem.emit = []
            formItem.emit.push(obj)
          }
        })
      } else {
        rule.forEach(formItem => {
          if (formItem.field === activeRule.field) {
            formItem.emitPrefix = 'gs'
            formItem.emit = []
            formItem.emit.push(obj)
          }
        })
      }

      this.$refs.designer.setRule(rule)
      this.msgSuccess('添加成功')
    }

genFunc.vue,代码编辑器用的vue-codemirror(ps:据说最新版仅支持vue3上,vue2可以下载4.x版本:
npm i vue-codemirror@4.x --save)

<template>
  <div class="app-container">
    <el-form ref="form" class="form" :model="formData" label-position="left" label-width="68px">
      <el-form-item label="事件">
        <el-select v-model="formData.name" style="width: 100%">
          <el-option
            v-for="item in eventList"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
      </el-form-item>
    </el-form>
    <codemirror
      ref="myCodeMirror"
      v-model="code"
      :options="cmOptions"
    />
    <div class="el-dialog__footer dialog-footer">
      <el-button type="primary" @click="submitForm">确 定</el-button>
    </div>
  </div>
</template>
<script>
import { codemirror } from 'vue-codemirror'
// 组件样式
import 'codemirror/lib/codemirror.css'
// 主题
import 'codemirror/theme/eclipse.css'

// 语言模式
import 'codemirror/mode/javascript/javascript.js'
import 'codemirror/mode/css/css.js'
import 'codemirror/mode/xml/xml.js'
// 代码展开折叠
import 'codemirror/addon/fold/foldcode.js'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/brace-fold.js'

export default {
  components: { codemirror },
  props: {
    activeRule: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      eventList: [
        { label: 'blur', value: 'blur' },
        { label: 'change', value: 'change' },
        { label: 'click', value: 'click' }
      ],
      cmOptions: {
        mode: 'text/javascript', // 实现javascript代码高亮
        tabSize: 4, // tab的空格宽度
        styleActiveLine: true, // 设置光标所在行高亮true/false
        lineNumbers: true, // 显示行号
        theme: 'eclipse', // 设置主题cobalt/monokai
        json: true,
        readOnly: false, // 设置为只读true/false;也可设置为"nocursor"失去焦点
        lineWrapping: false,
        foldGutter: true,
        gutters: [
          'CodeMirror-lint-markers', // 代码错误检测
          'CodeMirror-linenumbers',
          'CodeMirror-foldgutter' // 展开折叠
        ]
      },
      code: '(formData) => {\n // 在此编写代码\n}',
      formData: {
        name: 'change',
        inject: []
      }
    }
  },
  mounted() {
    var emit = this.activeRule.emit
    if (emit) {
      this.formData.name = emit[0].name
      this.code = emit[0].inject[0]
    }
  },
  methods: {
    submitForm() {
      this.formData.inject[0] = this.code
      this.$emit('success', this.formData)
    }
  }
}
</script>

<style scoped>
</style>

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值