vue3低代码平台-前端出码

前言

​ 在上一篇基于freemarker生成源码后,考虑到还需要起一个后端服务才能生成代码并下载,增加的不少麻烦,便想着试试通过前端生成源码并导出。前端的源码导出不止可以用到低代码平台上,当前端项目有一些简易的导出工作也可以通过这个方式实现对应的功能,项目开源地址:https://gitee.com/xxt2286621910/easy-code要是对项目感兴趣,欢迎加入到项目的开发中来

简介

​ 导出代码需要用到file-saver(用于生成下载的文件),jszip(用于将多个页面压缩使用),prettier(格式化前端文本代码),ecTemplate(用于装配json数据生成对应的文件)ecTemplate是由于没找到合适的便自己动手实现了一个简易的模板引擎,若有比较好的解决方案,希望大家可以在评论区给出。

功能实现

模板编辑

模板功能实现是基于 `` ,${}可以调用方法,或者直接使用三元表达式进行判断。自定义一个for循环的方法用于向模板中插入对应的属性值。includeTemplate 用于查询其他的模板用作模板嵌套使用,ComponentListStore.componentTemplates 用于保存组件的模板

**ecTemplateFor:**当传入的list为数组类型时向回调函数中传入value和index,若为Object则传递value和key

**includeTemplate:**当需要嵌套使用模板时便可传入对应的模板名,查询ComponentListStore.componentTemplates 并返回对应模板内容

import {coreTemplate} from "./coreTempalte/coreTemplate";
import {ComponentListStore} from "../stores/counter";
// 核心模板
export function ecTemplate(data){
    return coreTemplate(data)
}
// 循环功能list 循环的列表、method回调函数
export function ecTemplateFor(list,method){
    let result = ''
    if(Array.isArray(list)){
        let length = list.length
        for (let i = 0; i < length; i++) {
            if(method(list[i],i)!==''){
                result = result+method(list[i],i)+'\n'
            }
        }
    }else{
        for(let key in list){
            if(method(list[key],key)!==''){
                result = result+method(list[key],key)+'\n'
            }
        }
    }
    return result
}
// 用于查询子模版。用作子模版嵌套使用
export function includeTemplate(template,param){
    const componentListStore = ComponentListStore()
    if(componentListStore.componentTemplates.hasOwnProperty(template)){
        return componentListStore.componentTemplates[template](param)
    }
    return ""
}
// 生成通用模板 加载事件、属性、ref等
export function generalTemplate(param,filtrate){
    let filtrates = {attr:[]}
    if(filtrate){
        filtrates = filtrate
    }
    return `\n
        ${param.styles && JSON.stringify(param.styles) !== "{}"?`:style='${JSON.stringify(param.styles)}'`:''}\n
        ${param.bindClass && param.bindClass !==""?`class="${param.bindClass}"`:''}\n
        ${generateAttribute(param.attributes,param.defaultAttributes,filtrates.attr)}\n
        ${ecTemplateFor(param.events, (item,k) => {
        if (item.enable) {
            return `@${k}="${item.method}"`
        }
        return ''
    })}\n
    `
}
// 属性加载方法 attributes 属性列表、defaultAttributes 默认属性值列表、filtrate 过滤不展示的属性
export function generateAttribute(attributes,defaultAttributes,filtrate){
    return `\n
        ${ecTemplateFor(attributes, (item,k) => {
        if(filtrate && filtrate.indexOf(k)!==-1){
            return ''
        }
        if ((typeof item === "boolean" && defaultAttributes[k] !== item)) {
            return `:${k}="${item}"`
        }
        if ((typeof item === "number" && item !== 0) && defaultAttributes[k] !== item) {
            return `:${k}="${item}"`
        }
        if ((typeof item === "string") && item !== '' && defaultAttributes[k] !== item) {
            if(k.includes("-slot")){
                return ""
            }
            if(k.includes("Ref")){
                return `ref="${item}"`
            }else if(k === "modelValue"){
                return `v-model="${item}"`
            }else if(k.includes("-bindValue")){
                return `:${k.replace("-bindValue","")}="${item}"`
            }else{
                return `${k}="${item}"`
            }
        }
        return ''
    })}`
}

ScButton模板

generalTemplate 加载通用的模板方法,用于传入属性、事件等配置,{attr:[“label”]}过滤掉attributes中的label属性

template: (param)=>{
    return `
    <el-button
    ${generalTemplate(param,{attr:["label"]})}
    >
    ${param.attributes.label !==""?`${param.attributes.label }`:''} 
    </el-button>
    `
}

ScLayout模板

template:(param)=>{
    return `
    <el-row
    ${generalTemplate(param)}
     >
    ${ecTemplateFor(param.children, (citem,k) => {
        return `<el-col ${generateAttribute(param.attributes['col'][k],param.defaultAttributes['col'][k],['span'])}
                :span="${param.attributes['col'][k]["span"]}">
            ${ecTemplateFor(param.children[k].children,(item2)=>{
            return includeTemplate(item2.component,item2)})}
        </el-col>`
    })}
     </el-row>
    `
}

格式化文本

​ 由于模板生成的问题可能会导致输出的源码看起来乱糟糟,变将根据模板引擎生成的文本通过formatText方法格式化之后在进行输出。格式化代码使用的是prettier,prettier.format()返回的是promise函数,由于生成页面时需要同时生成多个页面,并将多个页面压缩到一个zip中便增加了同步方法。prettier官网Prettier 中文网 · Prettier 是一个“有态度”的代码格式化工具

安装prettier

npm install prettier
import prettier from "prettier/standalone"
import parserHtml from "prettier/plugins/html" // 格式化html
import parserCss from "prettier/plugins/postcss" // 格式化css文本

// 格式化文本text文本、type语言类型
export async function formatText(text, type) {
    try {
        if (!type || type === "javascript" ) return text
        let result = ""
        let plugin = null
        let vueIndentScriptAndStyle = false
        if (type === "css") {
            plugin = parserCss
            type = "css"
        }
        if (type === "html") {
            type = "html"
            plugin = parserHtml
            vueIndentScriptAndStyle = true
        }
        if (plugin !== null) {
            await prettier.format(text, {
                parser: type,
                plugins: [plugin],
                vueIndentScriptAndStyle: vueIndentScriptAndStyle
            }).then(data => {
                result = data
            })
        }
        return result
    }catch (e){
        console.log(e)
        ElMessage({message: "有错误请检查后在进行保存", type: 'warning', duration: 2000, showClose: true,})
    }

}

出码

​ 通过循环遍历需要导出的数据,并根绝pageName参数作为文件名,页面数据通过模板引擎处理后,将结果通过formatText格式化最后将文本转为二进制对象,并放入zip.file中并导出

安装jszip

npm install jszip
// 输出源码
export async function generateCode(exportPage) {
    // 加载zip模块用于压缩多个页面
    let zip = new JSZip()
    for (const item of exportPage) {
        let data = await formatText(ecTemplate(item),"html")
        let blob = new Blob([data], {type: "text/json;charset=utf-8"});
        zip.file(item.pageName + ".vue", blob, {binary: true})
    }
    zip.generateAsync({type: "blob"}).then(content => {
        // 生成二进制流
        saveAs(content, "easyCode源码"); // 利用file-saver保存文件  自定义文件名
    });

}

exportPage格式

[
  {
    "type": "page",
    "pageName": "1",
    "label": "1",
    "children": [
      {
        "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": "e03373e1-d61b-421c-ad50-73ad0c905cd4",
            "event": {},
            "attributes": {},
            "styles": {},
            "children": [
              {
                "component": "ScButton",
                "label": "按钮",
                "animations": [],
                "events": {
                  "click": {
                    "enable": false,
                    "method": ""
                  }
                },
                "attributes": {
                  "type": "primary",
                  "size": "default",
                  "label": "按钮",
                  "plain": false,
                  "round": false,
                  "disabled": false,
                  "circle": false,
                  "loading": false,
                  "loading-icon": "Loading",
                  "icon": "",
                  "autoInsertSpace": false,
                  "color": "",
                  "dark": false
                },
                "styles": {},
                "shapeStyles": {
                  "display": "inline-flex"
                },
                "status": {
                  "active": false,
                  "activeContainer": false,
                  "isHidden": false,
                  "lock": false
                },
                "type": "common",
                "bindClass": "",
                "defaultAttributes": {
                  "type": "primary",
                  "size": "default",
                  "label": "按钮",
                  "plain": false,
                  "round": false,
                  "disabled": false,
                  "circle": false,
                  "loading": false,
                  "loading-icon": "Loading",
                  "icon": "",
                  "autoInsertSpace": false,
                  "color": "",
                  "dark": false
                },
                "id": "1991ebc8-ed13-4a45-a631-13c96e915a06",
                "featherId": "e03373e1-d61b-421c-ad50-73ad0c905cd4"
              }
            ],
            "featherId": "cff86dbe-8598-40d4-aca6-3e05c9dd3ccf",
            "type": "container",
            "status": {
              "active": false,
              "activeContainer": false,
              "isHidden": false,
              "lock": false
            }
          },
          {
            "component": "container",
            "attribute": "col",
            "label": "列",
            "id": "3212d0d4-4de0-462c-822d-68d740defa6c",
            "event": {},
            "attributes": {},
            "styles": {},
            "children": [],
            "featherId": "cff86dbe-8598-40d4-aca6-3e05c9dd3ccf",
            "type": "container",
            "status": {
              "active": false,
              "activeContainer": false,
              "isHidden": false,
              "lock": false
            }
          }
        ],
        "type": "container",
        "status": {
          "active": false,
          "activeContainer": false,
          "isHidden": false,
          "lock": false
        },
        "bindClass": "",
        "defaultAttributes": {
          "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
            }
          ]
        },
        "id": "cff86dbe-8598-40d4-aca6-3e05c9dd3ccf",
        "featherId": "editor"
      }
    ],
    "status": {
      "active": false
    },
    "data": {},
    "ecVueInfo": "export default{ \n name:\"1\", \n mounted(){ \n}, \n data(){\n return{ \n     colorValue:\"yellow\",\n     inputValue:\"hello\",\n     data:[\n         {value:\"name\",label:\"name\"}\n         ]\n }}, \n methods:{\n} \n}",
    "EcVue": {
      "$options": {
        "name": "1",
        "methods": {}
      },
      "_data": {
        "colorValue": "yellow",
        "inputValue": "hello",
        "data": [
          {
            "value": "name",
            "label": "name"
          }
        ]
      },
      "$refs": {}
    },
    "css": "",
    "id": "c29ec373-1b4d-458b-bd08-b0565f72c30e"
  },
  {
    "type": "page",
    "pageName": "2",
    "label": "2",
    "children": [
      {
        "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": "9e766ea8-202a-4c9c-ab36-079c14784240",
            "event": {},
            "attributes": {},
            "styles": {},
            "children": [],
            "featherId": "4f82046c-8379-45af-b1b9-9dbb52563923",
            "type": "container",
            "status": {
              "active": false,
              "activeContainer": false,
              "isHidden": false,
              "lock": false
            }
          },
          {
            "component": "container",
            "attribute": "col",
            "label": "列",
            "id": "daa4fb26-df81-4cf1-a09b-6b678d0e71cb",
            "event": {},
            "attributes": {},
            "styles": {},
            "children": [],
            "featherId": "4f82046c-8379-45af-b1b9-9dbb52563923",
            "type": "container",
            "status": {
              "active": false,
              "activeContainer": false,
              "isHidden": false,
              "lock": false
            }
          }
        ],
        "type": "container",
        "status": {
          "active": false,
          "activeContainer": false,
          "isHidden": false,
          "lock": false
        },
        "bindClass": "",
        "defaultAttributes": {
          "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
            }
          ]
        },
        "id": "4f82046c-8379-45af-b1b9-9dbb52563923",
        "featherId": "editor"
      },
      {
        "component": "ScButton",
        "label": "按钮",
        "animations": [],
        "events": {
          "click": {
            "enable": false,
            "method": ""
          }
        },
        "attributes": {
          "type": "primary",
          "size": "default",
          "label": "按钮",
          "plain": false,
          "round": false,
          "disabled": false,
          "circle": false,
          "loading": false,
          "loading-icon": "Loading",
          "icon": "",
          "autoInsertSpace": false,
          "color": "",
          "dark": false
        },
        "styles": {},
        "shapeStyles": {
          "display": "inline-flex"
        },
        "status": {
          "active": false,
          "activeContainer": false,
          "isHidden": false,
          "lock": false
        },
        "type": "common",
        "bindClass": "",
        "defaultAttributes": {
          "type": "primary",
          "size": "default",
          "label": "按钮",
          "plain": false,
          "round": false,
          "disabled": false,
          "circle": false,
          "loading": false,
          "loading-icon": "Loading",
          "icon": "",
          "autoInsertSpace": false,
          "color": "",
          "dark": false
        },
        "id": "eb016967-e3ca-44c3-8afe-3f46ae19e463",
        "featherId": "editor"
      }
    ],
    "status": {
      "active": false
    },
    "data": {},
    "ecVueInfo": "export default{ \n name:\"2\", \n mounted(){ \n}, \n data(){\n return{ \n }}, \n methods:{\n} \n}",
    "EcVue": {
      "$options": {
        "methods": {}
      },
      "_data": {},
      "$refs": {}
    },
    "css": "",
    "id": "7df39bbe-edaf-4bb8-a04f-7dc1b7d70b89"
  }
]

结果

<template>
  <div>
    <el-row>
      <el-col :span="24"> </el-col>
      <el-col :span="24"> </el-col>
    </el-row>

    <el-button> 按钮 </el-button>
  </div>
</template>
<script>
      export default{
   name:"2",
   mounted(){
  },
   data(){
   return{
   }},
   methods:{
  }
  }
</script>
<style scoped></style>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值