手搓低代码表单(四)预览

经过低代码平台产出的代码,最终要运行在浏览器上,我们在平台上提供一个预览功能,可以让我们预览拖拽后生成的表单页面。

预览器的功能区由两部分组成:

  • 源码预览
    • script脚本代码预览
    • template模板代码预览
    • style样式代码预览
  • 页面展示效果预览

效果图如下:

所谓预览就是要模拟真实的使用场景,这里也有两个选择

  • 一个是直接产出dist包,在浏览器直接加载即可预览
  • 一个是产出源码(vue文件),然后再编译源码,在浏览器预览,优点是可以针对源码进行二开

我们只看第二种方案,因为第二种其实也包括第一种了,产出源码就是要将schema源数据表示的表单转换为我们熟悉的vue单文件组件代码,整体的思路是:

  1. schema转换为vue单文件组件代码(字符串)
  2. 使用eval执行字符串代码,得到一个vue组件
  3. 然后将vue组件挂载到页面上,即可预览

源码生成

下面展开来说:

script脚本代码生成

js部分我们最终的产物应该如下结构所示:

js 复制代码
    const str = `export default {
    components: {},
    props: {},
    data() {
       return {
          
       }
    },
    computed: {},
    watch: {},
    created() {
       
    },
    mounted() {},
    methods: {
    
    }
}`

整个结构都与常见的单文件组件的JS脚本部分类似,我们继续分析在js脚本部分需要处理的内容:

  • 表单数据初始化,例如el-formmodel属性绑定的数据
  • 表单校验规则,例如el-formrules属性绑定的数据

表单的数据初始化

表单数据初始化比较简单,我们只需要将formConfig中的formModel属性转换为data()中的属性即可

js 复制代码
    data() {
      return {
        ${formConfig.formModel}: {
        }
      }
    }

接下来就是要把每个表单项的__vModel__属性收集起来,绑定到formConfig.formModel上,而表单项对应的value值为schema中的defaultValue

js 复制代码
data() {
  return {
    ${formConfig.formModel}: {
      ${fields.map(el => `${el.__vModel__}: ${el.__config__.defaultValue}`).join(',\n')}
    }
  }
}

表单的校验规则

就像表单数据初始化一样,el-formrules属性,只需要把schema中的regList属性转换为rules属性即可,在data中定义一个formConfig.formRules属性,用来存放校验规则,然后遍历schema中的regList属性,将其插入到formConfig.formRules即可

js 复制代码
  data() {
    return {
      ${formConfig.formRules}: {
        ${fields.map(el => {
          const rules = []
          el.__config__.regList.forEach(rule => {
            rules.push({
              required: el.__config__.required,
              message: rule.message,
              trigger: 'blur',
              pattern: rule.pattern
            })
          })
          return `${el.__vModel__}: ${JSON.stringify(rules)}`
        }).join(',\n')}
      }
    }
  }

表单数据初始化和校验规则的初始化完成后,schema就转换为vue单文件组件中的的script代码,我们看下最终转换后的js代码:

js 复制代码
export default {
  components: {},
  props: {},
  data() {
    return {
      formData: {
        mobile: ''
      },
      rules: {
        mobile: [{
          required: true,
          message: '手机号格式错误',
          trigger: 'blur',
          pattern: '/^1(3|4|5|7|8|9)\d{9}$/'
        }]
      }
    }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {},
  methods: {}
}

template模板代码生成

template模板代码的产出有点麻烦,我们需要根据schema中的定义,产出对应的标签,并且需要绑定属性,除此之外还需要把这些标签组合在一起形成一个完整的表单组件

以下面的schema为例:

schmea 复制代码
{
    __config__: {
      label: '手机号',
      labelWidth: null,
      showLabel: true,
      changeTag: true,
      tag: 'el-input',
      tagIcon: 'input',
      defaultValue: '',
      required: true,
      layout: 'colFormItem',
      span: 24,
      // 正则校验规则
      regList: [{
        pattern: '/^1(3|4|5|7|8|9)\d{9}$/',
        message: '手机号格式错误'
      }]
    },
    // 组件的插槽属性
    __slot__: {
      prepend: '',
      append: ''
    },
    __vModel__: 'mobile',
    placeholder: '请输入手机号',
    style: { width: '100%' },
    clearable: true,
    'prefix-icon': 'el-icon-mobile',
    'suffix-icon': '',
    maxlength: 11,
    'show-word-limit': true,
    readonly: false,
    disabled: false
  }

最终要把上面的schema转换为如下的vue单文件组件中的template代码:

html 复制代码
<el-form ref="elForm" :model="formData" :rules="rules" label-width="100px">
  <el-form-item label="手机号" required prop="mobile">
    <el-input v-model="formData.mobile" clearable placeholder="请输入手机号" :style="{width: '100%'}" maxlength="11"
      show-word-limit prefix-icon='el-icon-mobile'>
  </el-form-item>
</el-form>

要把schema转换为vue单文件组件代码,可以拆成多个小任务:

  • 任务1:构建el-form标签及其属性
  • 任务2: 构建el-form-item并且将schema中的__config__属性转换为el-form-item标签的属性
  • 任务3: 构建el-input并且将schema中的其他属性(非__config__)转换为el-input标签上的属性

任务1

构建el-form标签及其属性需要依赖formConf中的配置

js 复制代码
const formConf = {
    formRef: 'elForm',
    formModel: 'formData',
    size: 'medium',
    labelPosition: 'right',
    labelWidth: 100,
    formRules: 'rules',
    gutter: 15,
    disabled: false,
    span: 24,
    formBtns: true
}
const disabled = schema.disabled ? `:disabled="${formConf.disabled}"` : ''
let str1 = `<el-form ref="${formConf.formRef}" :model="${formConf.formModel}" :rules="${formConf.formRules}" label-width="${formConf.labelWidth}px" ${labelPosition} ${disabled}>
  </el-form>`

上述代码中,str就是最终的el-form标签及其属性

任务2

构建el-form-item标签及其属性需要依赖config中的配置

javascript 复制代码
let label = `label="${config.label}"`
let labelWidth = `label-width="${config.labelWidth}px"`
const required = config.required ? 'required' : ''
let str2 = `<el-form-item ${label} ${labelWidth} ${required} prop="${scheme.__vModel__}"></el-form-item>`

上面的代码中,str就是最终的el-form-item标签及其属性

任务3

构建el-input标签及其属性需要依赖schema中的配置

javascript 复制代码
const maxLength = el.maxlength ? `maxlength="${el.maxlength}"` : ''
const tag = el.__config__.tag
const vModel = `v-model="${formConf.formModel}.${el.__vModel__}"`
let str3 = `<${tag} ${vModel} ${disabled} ${maxLength}>`

在构建el-input的时候,需要把el-inputv-model属性绑定到formData对象中,所以需要把formData对象中的属性名通过formModel属性获取,然后拼接起来。

最后我们把任务1、任务2、任务3的代码组合起来,就可以把schema转换为vue单文件组件代码的template代码。

js 复制代码
<str1>
  <str2>
    <str3>
    </str3>
  </str2>
</str1>

最终拼接后的template代码:

html 复制代码
<el-form ref="elForm" :model="formData" :rules="rules" label-width="100px">
  <el-form-item label="单行文本" required prop="mobile">
    <el-input v-model="formData.mobile" clearable placeholder="请输入手机号" :style="{width: '100%'}" maxlength="11"
      show-word-limit prefix-icon='el-icon-mobile'>
  </el-form-item>
</el-form>

通过以上两步,我们就可以把schema转换为vue单文件组件代码,style部分很少修改,这里就不写了

源码预览

代码的预览功能,我们通过beautify插件和monaco-editor编辑器来实现。 关于beautifymonaco-editor的使用,大家可以自行去问GPT,这里贴下部分代码:

js 复制代码
loadBeautifier(btf => {
    beautifier = btf
    this.htmlCode = beautifier.html(this.htmlCode, beautifierConf.html)
    this.jsCode = beautifier.js(this.jsCode, beautifierConf.js)
    this.cssCode = beautifier.css(this.cssCode, beautifierConf.html)

    loadMonaco(mo => {
       monaco = mo
       this.setEditorValue('editorHtml', 'html', this.htmlCode)
       this.setEditorValue('editorJs', 'js', this.jsCode)
       this.setEditorValue('editorCss', 'css', this.cssCode)
       this.runCode()
    })
})

loadBeautifier是负责加载beautify插件的,loadMonaco是负责加载monaco-editor编辑器的。htmlCodejsCode就是我们之前生成的template代码。 setEditorValue是负责将代码设置到编辑器中的,这样就能看到生成的代码。

页面展示效果预览

页面展示效果预览功能我们通过iframe的形式来实现,在此之前我们已经有了一个完整的单文件组件的代码,只要将这些代码字符串转换为可以运行的代码,就可以预览了。 在vue中,我们可以通过new Vue()的形式来创建一个组件,然后通过template属性来指定组件的模板,并添加一些属性,最后通过$mount来挂载到页面上。 我们的模板字符串大致如下:

js 复制代码
// preview.js
const str = `{
    components: {},
    props: {},
    data() {
       return {
          
       }
    },
    computed: {},
    watch: {},
    created() {
       
    },
    mounted() {},
    methods: {
    
    }
}`
let html = `
<el-form ref="elForm" :model="formData" :rules="rules" label-width="100px">
  <el-form-item label="手机号" required prop="mobile">
    <el-input v-model="formData.mobile" clearable placeholder="请输入手机号" :style="{width: '100%'}" maxlength="11"
      show-word-limit prefix-icon='el-icon-mobile'>
  </el-form-item>
</el-form>`

如上所示,str中的代码就是我们之前生成的单文件组件的js代码,html就是我们之前生成的template代码。 那么我们只需要使用eval来执行str中的代码,然后通过$mount来挂载到页面上,就可以预览了。

js 复制代码
let previewCom = eval(`(${str})`)
previewCom.template = `<div>${html}</div>`
new Vue({
    components: {
       previewCom: previewCom
    },
    template: `<div><previewCom/></div>`
}).$mount('#app')

就像上面的代码一样,我们把str中的代码注册为一个组件,然后在template中使用这个组件,这样就可以在页面展示效果预览功能中展示出来了。

至此,就实现了手搓低代码表单的预览功能,整体功能分为三个部分:

  1. 代码生成
  2. 代码预览
  3. 效果图预览 每个部分的核心功能都已经梳理了一遍,还有一些细节,我们没有提到,但不影响整体的实现。

我们整个手搓系列到今天也就完结了,这个系列主要讲了如何实现一个低代码表单,包括表单的配置、生成、预览等功能。 当然这个是一个非常基础的实现,并不像市面上的低代码平台那样复杂,但是通过这个系列,我们能够了解整个实现过程,了解其基本思想,并且能够自己实现一个简单的低代码表单,这样也就够了,生活不就是这样吗?知足常乐。

如果你喜欢我的内容,请点赞评论告诉我,我会努力做得更好!

系列链接

1. 手搓低代码表单(一)整体设计以及物料区开发 2. 手搓低代码表单(二)画布区开发 3. 手搓低代码表单(三)属性配置区

相关推荐
什么鬼昵称2 分钟前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色20 分钟前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_23438 分钟前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河40 分钟前
CSS总结
前端·css
NiNg_1_23440 分钟前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
读心悦41 分钟前
如何在 Axios 中封装事件中心EventEmitter
javascript·http
BigYe程普1 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
神之王楠1 小时前
如何通过js加载css和html
javascript·css·html
余生H1 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
花花鱼1 小时前
@antv/x6 导出图片下载,或者导出图片为base64由后端去处理。
vue.js