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

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

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

  • 源码预览
    • 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. 手搓低代码表单(三)属性配置区

相关推荐
桂月二二24 分钟前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb1 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
沈梦研1 小时前
【Vscode】Vscode不能执行vue脚本的原因及解决方法
ide·vue.js·vscode
hunter2062062 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb2 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角2 小时前
CSS 颜色
前端·css
轻口味2 小时前
Vue.js 组件之间的通信模式
vue.js
浪浪山小白兔3 小时前
HTML5 新表单属性详解
前端·html·html5
lee5763 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579653 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter