低代码平台-源码生成(Java freemarker)

基于vue3的低代码平台-源码生成(Java freemarker)

前言

源码生成是采用的数据+模板的形式,这就需要通过模板引擎将数据放入到事先写好的模板中去,本文使用的是java的freemarker模板引擎,作为一款老牌的模板引擎,在网上有诸多学习资料,再次就不赘述了,本文着重讲解如何通过json+freemarker的形式生成源码文件。

传入的json文件

其中包含普通组件button,容器组件layout

格式简介:

**“pageName”😗*页面名称

**“children”😗*页面或容器组件包含的组件列表

**”component"😗*组件名称(用于匹配组件的模板文件如ScButton.ftl)

**“attributes”😗*组件的属性列表

**“events”😗*组件的事件列表

**“ecVueInfo”😗*vue的

**“css”😗*vue的

[
  {
    "type": "page",
    "pageName": "page1",
    "label": "测试页面",
    "children": [
      {
        "component": "ScButton",
        "label": "按钮",
        "animations": [],
        "events": {
          "click": {
            "enable": true,
            "method": "to"
          }
        },
        "attributes": {
          "type": "primary",
          "size": "default",
          "label": "按钮",
          "plain": false,
          "round": false,
          "disabled": false,
          "circle": false,
          "loading": false,
          "loading-icon": "Loading",
          "icon": "",
          "autoInsertSpace": false,
          "autofocus": false,
          "native-type": "button",
          "color": "",
          "dark": false
        },
        "styles": {
          "display": "inline-flex"
        },
        "status": {
          "active": false,
          "activeContainer": false,
          "isHidden": false,
          "lock": false
        },
        "type": "common",
        "bindClass": "",
        "id": "de076629-485d-4a95-af40-c3d2b09fdc91",
        "featherId": "editor"
      },
      {
        "component": "ScLayout",
        "label": "行",
        "event": {},
        "attributes": {
          "justify": "start",
          "align": "start",
          "gutter": 0,
          "col": [
            {
              "span": 24,
              "offset": 0,
              "push": 0,
              "pull": 0,
              "xs": null,
              "sm": null,
              "md": null,
              "lg": null,
              "xl": null
            },
            {
              "span": 24,
              "offset": 0,
              "push": 0,
              "pull": 0,
              "xs": null,
              "sm": null,
              "md": null,
              "lg": null,
              "xl": null
            }
          ]
        },
        "styles": {},
        "children": [
          {
            "component": "container",
            "attribute": "col",
            "label": "列",
            "id": "8535e009-de5b-4f4a-a631-91cd5b2cb948",
            "event": {},
            "attributes": {},
            "styles": {},
            "children": [
              {
                "component": "ScButton",
                "label": "按钮",
                "animations": [],
                "events": {
                  "click": {
                    "enable": true,
                    "method": "test"
                  }
                },
                "attributes": {
                  "type": "primary",
                  "size": "default",
                  "label": "按钮",
                  "plain": false,
                  "round": false,
                  "disabled": false,
                  "circle": false,
                  "loading": false,
                  "loading-icon": "Loading",
                  "icon": "",
                  "autoInsertSpace": false,
                  "autofocus": false,
                  "native-type": "button",
                  "color": "",
                  "dark": false
                },
                "styles": {
                  "display": "inline-flex"
                },
                "status": {
                  "active": false,
                  "activeContainer": false,
                  "isHidden": false,
                  "lock": false
                },
                "type": "common",
                "bindClass": "hello",
                "id": "85a509c6-49b5-4de1-8a8b-b34dfa13cd62",
                "featherId": "8535e009-de5b-4f4a-a631-91cd5b2cb948"
              }
            ],
            "featherId": "0f866713-bad6-40b2-bcfc-214fd32125c7",
            "type": "container",
            "status": {
              "active": false,
              "activeContainer": false,
              "isHidden": false,
              "lock": false
            }
          },
          {
            "component": "container",
            "attribute": "col",
            "label": "列",
            "id": "e92f60af-747f-4f61-8894-0109757f9068",
            "event": {},
            "attributes": {},
            "styles": {},
            "children": [
              {
                "component": "ScButton",
                "label": "按钮",
                "animations": [],
                "events": {
                  "click": {
                    "enable": true,
                    "method": "test"
                  }
                },
                "attributes": {
                  "type": "primary",
                  "size": "default",
                  "label": "按钮",
                  "plain": false,
                  "round": false,
                  "disabled": false,
                  "circle": false,
                  "loading": false,
                  "loading-icon": "Loading",
                  "icon": "",
                  "autoInsertSpace": false,
                  "autofocus": false,
                  "native-type": "button",
                  "color": "",
                  "dark": false
                },
                "styles": {
                  "display": "inline-flex"
                },
                "status": {
                  "active": false,
                  "activeContainer": false,
                  "isHidden": false,
                  "lock": false
                },
                "type": "common",
                "bindClass": "hello",
                "id": "fffe3e81-3918-4e86-adcb-8a61d2e5c9c1",
                "featherId": "e92f60af-747f-4f61-8894-0109757f9068"
              }
            ],
            "featherId": "0f866713-bad6-40b2-bcfc-214fd32125c7",
            "type": "container",
            "status": {
              "active": false,
              "activeContainer": false,
              "isHidden": false,
              "lock": false
            }
          }
        ],
        "type": "container",
        "status": {
          "active": false,
          "activeContainer": false,
          "isHidden": false,
          "lock": false
        },
        "bindClass": "",
        "id": "0f866713-bad6-40b2-bcfc-214fd32125c7",
        "featherId": "editor"
      }
    ],
    "status": {
      "active": false
    },
    "data": {},
    "ecVueInfo": "export default{\nmounted(){\n},\ndata(){\nreturn{\n    \n    inputValue:\"hello code\"\n}},\nmethods:{\n    test(){\n        console.log('hello world')\n    },\n    to(){\n        this.router('routerPage')\n    }\n}}\n",
    "EcVue": {
      "$options": {
        "methods": {}
      },
      "_data": {
        "inputValue": "hello code"
      },
      "$refs": {}
    },
    "css": ".hello{\n    height: 100px;\n}",
    "id": "4a33d447-1f92-4ad7-8f9e-1e724dec147b"
  },
]

解析json文件

由于json文件解析后是一个数组,需要先通过 JSONArray.parseArray()将json文件解析为List类型的对象,

freemarker 用于生成模板时需要传入Map<String,Object>类型的参数,故通过JSONObject.parseObject()将列表中的每一项转换为Map<String,Object>类型,

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class analysisJson {
    static String path="";
    private String JsonString;
    public analysisJson(String path){
        this.path = path;
    }
    public void readJson(String path){
        //从给定位置获取文件
        File file = new File(path);
        BufferedReader reader = null;
        //返回值,使用StringBuffer
        StringBuffer data = new StringBuffer();
        //
        try {
            reader = new BufferedReader(new FileReader(file));
            //每次读取文件的缓存
            String temp = null;
            while((temp = reader.readLine()) != null){
                data.append(temp);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭文件流
            if (reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        this.JsonString =data.toString();
    }

    public  List<Map<String,Object>> JsonToMap(){
        this.readJson(path);
        String JsonString=this.JsonString;
        List<JSONObject> list = null;
        List<Map<String,Object>>  mapList = new ArrayList<Map<String,Object>>();
        try{
            // 将json字符串转换成jsonObject
            //字符串转map
            list = JSONArray.parseArray(JsonString,JSONObject.class);
            list.forEach(item->{
                mapList.add(JSONObject.parseObject(item.toJSONString(), new TypeReference<Map<String, Object>>(){}));
            });
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            return mapList;
        }
    }
}

用到的freemarker指令介绍

if:if指令效果通java中的if一致,不过当判定条件为false时,对应位置会产生一个空白区域,这时就需要通过compress指令将空白强制删除掉(同时也会删除首行缩进),当需要判断value值的类型时,通过**?is_~~~**的形式来进行判断,

?is_boolean判断是否为boolean

?is_string判断是否为字符串,

?is_number判断是否为数字

更多类型判断可看:很少使用的和专家级的内建函数 - FreeMarker 中文官方参考手册 (foofun.cn)

示例:

<#if condition>
  ...
<#elseif condition2>
  ...
<#elseif condition3>
  ...
...
<#else>
  ...
</#if>

**include:**在include指令的位置引入其他模板文件

**${属性名}😗*获取对应属性的值

**list:*list指令可以类比为for循环,其中当循环的对象为Map(json {})类型时可以通过?keys as k的方式来调用对应的key值,当循环遍历时想要知道对应项的下标值可以通过_index*的方式。

示例:

<#list children as item>
    	${item_index}
      <#include "./"+item.component+".ftl">
</#list>
    
<#list attributes?keys as k>
    	${k}
</#list>

**?string(‘true’, ‘false’)😗*当属性值为boolean类型时,无法直接通过 属性名进行引用的需要通过 ∗ {属性名}进行引用的需要通过* 属性名进行引用的需要通过{属性名?string(‘true’, ‘false’)}*的方式进行使用

示例:

boolenaValue:true
${boolenaValue?string('true', 'false')}

核心模板

首先我们会创建一个核心模板,也就是一个.vue文件的架子,通过include指令引入组件的模板,同时freemarker会将item作为参数给到组件模板中去,并通过item.属性的方式进行属性的调用。

<template>
    <div>
        <#list children as item>
            <#include "./"+item.component+".ftl">
        </#list>
    </div>
</template>
<script>
${ecVueInfo}
</script>
<style scoped>
${css}
</style>

button模板

通过list循环将默认值为false,“”的属性过滤掉,这样生成的组件上将不会有过多的属性

<el-button
        <#compress>
            <#if item.bindClass!=''>class="${item.bindClass}"</#if>
            <#list item.attributes?keys as k>
                <#if item.attributes[k]?is_boolean && item.attributes[k] >:${k}=${item.attributes[k]?string('true', 'false')}<#elseif item.attributes[k]?is_string>${k}="${item.attributes[k]}"</#if>
            </#list>
            <#list item.events?keys as k>
                <#if item.events[k].enable>@${k}="${item.events[k].method}"</#if>
            </#list>
        </#compress>
>
    ${item.attributes.label}</el-button>

Layout布局模板

layout模板是一个典型的容器模板,其中可以包含多个col,每个col中也包含多个组件

<el-row
        <#compress>
            <#if item.bindClass!=''>class="${item.bindClass}"</#if>
            <#list item.attributes?keys as k>
                <#if k!='col'>
                    <#if item.attributes[k]?is_boolean && item.attributes[k] >:${k}=${item.attributes[k]?string('true', 'false')}<#elseif item.attributes[k]?is_string>${k}="${item.attributes[k]}"</#if>
                </#if>
            </#list>
        </#compress>
>
    <#list item.children as ctem>
        <el-col
                <#compress>
                    <#list item.attributes['col'][ctem_index]?keys as k>
                        <#if k!='col'>
                            <#if item.attributes['col'][ctem_index][k]?is_boolean && item.attributes['col'][ctem_index][k] >
                                :${k}=${item.attributes['col'][ctem_index][k]?string('true', 'false')}
                            <#elseif item.attributes['col'][ctem_index][k]?is_string && item.attributes['col'][ctem_index][k] != ''>
                                ${k}="${item.attributes['col'][ctem_index][k]}"
                            <#elseif item.attributes['col'][ctem_index][k]?is_number && item.attributes['col'][ctem_index][k] != 0>
                                :${k}="${item.attributes['col'][ctem_index][k]}"
                            </#if>
                        </#if>
                    </#list>
                </#compress>
        >
            <#list ctem.children as item>
                <#include "./"+item.component+".ftl">
            </#list>
        </el-col>
    </#list>
</el-row>

调用

import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.IOException;

public class createFile {
    public static void main(String[] args) throws Exception {
        BuildConfig buildConfig = new BuildConfig();
        Template template = buildConfig.setTemplate();
        analysisJson analysisJson=new analysisJson("src/main/resources/static/template/Json/page.json");
        analysisJson.JsonToMap().forEach(item->{
            //将数据填充到模板对象中生成文件
            try {
                buildConfig.setFileName(item.get("pageName").toString());
                buildConfig.setFilenameSuffix(".vue");
                template.process(item, buildConfig.setFileDemo());
            } catch (TemplateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}

结果

<template>
  <div>
    <el-button
        color=""
        icon=""
        label="按钮"
        loading-icon="Loading"
        type="primary"
        native-type="button"
        size="default"
        @click="to">
      按钮
    </el-button>
    <el-row
        justify="start"
        align="start">
      <el-col
          :span="24">
        <el-button
            class="hello"
            color=""
            icon=""
            label="按钮"
            loading-icon="Loading"
            type="primary"
            native-type="button"
            size="default"
            @click="test">
          按钮
        </el-button>

      </el-col>
      <el-col
          :span="24">
        <el-button
            class="hello"
            color=""
            icon=""
            label="按钮"
            loading-icon="Loading"
            type="primary"
            native-type="button"
            size="default"
            @click="test">
          按钮
        </el-button>

      </el-col>
    </el-row>
  </div>
</template>
<script>
export default {
  mounted() {
  },
  data() {
    return {
      inputValue: "hello code"
    }
  },
  methods: {
    test() {
      console.log('hello world')
    },
    to() {
      // this.router('routerPage')
    }
  }
}

</script>
<style scoped>
.hello {
  height: 100px;
}
</style>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值