[动态界面 json2render] 动态界面中加载自定义模板组件

需求

最近碰到一个需求,web 界面本身是基于配置动态渲染出来,页面里面某个位置的组件是一个特殊节点 slot,该节点本⾝不会被渲染,用户自定义了一个组件,该组件的定义保存成 json 格式文件,特殊节点加载这个配置文件后渲染出该组件,界面里会有多个这种特殊节点加载多个用户自定义组件

这个需求 json2render 动态界面组件正好可以实现这个功能,json2render 是一个 web 界面动态渲染库,可以根据 json 数据动态渲染界面,是根据动态表单组件 vjform 设计思想的重构版本,采用 proxy 重写了表达式数据关联关系的逻辑,分离了表达式关联关系的转换实现与渲染框架(vue、react)的依赖,最新版本引入了 dependency injection 机制,使扩展新的功能更加科学合理,目前 json2render 渲染方式提供 vue3 支持

效果

具体效果如下

img.gif

示例中主界面是通过 json 数据动态呈现,里面有个地方选择组件,在选择组件后加载组件的 json 数据,动态呈现到下面的特殊节点区域

实现

json2render 作为动态呈现组件本身内部也可以渲染 json2render实现嵌套渲染

首先安装 @json2render/vue-fullelement-plus 并在项目中引用,@json2render/vue-full 包含了对 vue3 的完整支持

npm i @json2render/vue-full element-plus
复制代码
import { createApp } from 'vue'
import App from './App'
import Element from 'element-plus'
import JRender from '@json2render/vue-full'
import 'element-plus/lib/theme-chalk/index.css'

createApp(App)
  .use(Element)
  .use(JRender)
  .mount('#app')
复制代码

实现一个 vue 界面, App.js

<script>
import { defineComponent } from "vue"

export default defineComponent({
  setup() {
    return {
      // 主界面的配置,可以写死也可以远程读 json 文件
      // 常规的网络请求资源问题,这里就不实现怎么读了
      config: {} 
    }
  }
})
</script>

<template>
  <v-jrender
    :fields="config.fields"
    :datasource="config.datasource"
    :listeners="config.listeners"
  />
</template>
复制代码

主界面的实现 json 数据如下

{
  "datasource": {
    "template": {
      // fetch 类型数据源用于读取远程资源
      "type": "fetch", 
      // 根据选择的组件名称读取对应json文件数据
      // 资源存储在 /data/components 目录下
      "url": "#:/data/components/${model.name}.json", 
      "auto": false,
      "props": { "method": "GET", "params": {} },
      "defaultData": false
    }
  },
  "listeners": [
    {
      // 定义一个监听,在选择的组件名称变化后数据源重新请求自定义组件json数据
      "watch": "$:model.name",
      "actions": [{ "handler": "@:template.request()" }]
    }
  ],
  "fields": [
    {
      "component": "h1",
      "text": "加载模板组件"
    },
    {
      "component": "el-select",
      "model": "model.name",
      "children": [
        {
          "component": "el-option",
          "props": { "label": "组件1", "value": "cmp1" }
        },
        {
          "component": "el-option",
          "props": { "label": "组件2", "value": "cmp2" }
        },
        {
          "component": "el-option",
          "props": { "label": "监听触发", "value": "listeners" }
        }
      ]
    },
    {
      "component": "div",
      "props": {
        "style": {
          "border": "1px dashed silver",
          "padding": "1.25rem",
          "marginTop": "0.75rem"
        }
      },
      "children": [
        {
          "component": "p",
          "condition": "$:!template.data",
          "text": "加载自定义组件",
          "props": { "style": { "textAlign": "center" } }
        },
        {
          "component": "v-jrender",
          "condition": "$:template.data",
          "props": "$:template.data"
        }
      ]
    }
  ]
}
复制代码

在 /data/components 目录下实现三个自定义模板组件

cmp1.json

{
  "fields": [{ "component": "p", "text": "段落文本" }]
}
复制代码

cmp2.json

{
  "modelValue": {},
  "fields": [
    { "component": "el-input", "model": "model.text" },
    { "component": "p", "text": "#:输入了: ${model.text||''}" }
  ]
}
复制代码

listeners.json

{
  "listeners": [
    {
      "watch": "$:model.on",
      "actions": [
        {
          "condition": "$:!!model.on",
          "handler": "@model.value:model.value + 1"
        },
        {
          "condition": "$:!model.on",
          "handler": "@model.value:0"
        }
      ]
    },
    {
      "watch": "$:model.value",
      "actions": [
        {
          "condition": "$:!!model.on && model.value < 100",
          "timeout": "$:model.num * 100",
          "handler": "@model.value:model.value + 1"
        }
      ]
    }
  ],
  "fields": [
    {
      "component": "el-input-number",
      "model": "model.num",
      "props": { "min": 0, "max": 5 }
    },
    {
      "component": "el-button",
      "text": "Go",
      "props": { "onClick": "@model.on:true" }
    },
    {
      "component": "el-button",
      "text": "Reset",
      "props": { "onClick": "@model.on:false" }
    },
    {
      "component": "el-progress",
      "props": {
        "strokeWidth": 24,
        "textInside": true,
        "percentage": "$:model.value"
      }
    }
  ],
  "modelValue": { "num": 0, "value": 0 }
}
复制代码

具体的代码可以看 json2render 项目 templateload 的示例

国内网慢可看 国内镜像

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享