• 简单5分钟,将lowcode低代码融入到你的中后台管理系统


    背景

    你是否在做中后台项目中经常要重复做crud的业务逻辑,花费大量时间还时常有bug发生,但是现在只要几分钟就能让你快速连通前后端,拖拉拽实现后台业务逻辑。你就问香不香!

    技术选型

    🚀 选用百度出品的amis低代码开源框架, 基于amis-editor(React + TS),我们做的事情就是通过封装json数据上报、配置、自定义组件等,实现低代码管理后台实时更新,无需手动写json配置。如果你要在Vue中使用当然也可以。

    👍 简单一句话: 你不用敲代码了!!

    先附上github地址 github.com/ccj-007/low…

    amis官方文档 github.com/baidu/amis

    amis-editor编辑器 github.com/aisuda/amis…

    在原框架上实现了哪些功能

    1.支持url路由跳转对应的配置页面
    2.支持历史记录修改
    3.支持预览
    4.支持重置
    5.支持配置更新前端lowcode页面(不用敲代码喽!!!)
    6.通过路由及项目名配置查询
    7.支持切换环境

    如何使用

     npm i //安装依赖npm run start //通过devserve启动前端页面npm run server//启动node服务,默认3001端口 
    
    • 1

    注意

    1. 本地调试请在server文件夹下定义好文件名,本地调用通过文件名对应路由名。如果需要数据库连接,请定义好项目名和路由名。json配置在原来基础上,已经做了一个包裹, 核心数据配置在json属性内,为了方便定位以及后期维护扩展。

    {"json": {"type": "page","title": "Hello world","body": []},"routeName": "test2.json","itemName": "cms2"
    } 
    
    • 1
    • 2

    核心

    //src/App.tsximport React from 'react';
    import { Editor } from 'amis-editor';
    import './App.css'
    import axios from 'axios'
    
    interface StateType {json: anyrouteName: stringitemName: stringpreview: booleanhistoryList: any[]step: numbermaxHistoryNum: numberbaseURL: stringuseTestBaseURL: stringisLocalTest: boolean
    }
    
    class App extends React.Component {constructor(props: any) {super(props)this.state = {json: {type: "page"//确保是页面层级},routeName: 'client-admin', //默认为''itemName: "cms2", //默认为''preview: false,historyList: [],step: 0,maxHistoryNum: 10,baseURL: window.localStorage.getItem('baseURL') || 'https://dev.zzss.com', //正式开发使用useTestBaseURL: 'http://localhost:3001', //本地调试环境切换使用isLocalTest: true,//用于本地调试环境,正式开发请设置为false}}componentDidMount() {//获取url querythis.checkQuery()setTimeout(() => {this.getJSON()}, 0)}getJSON = () => {let { routeName, itemName, isLocalTest, baseURL, useTestBaseURL, } = this.stateif (!routeName || !itemName) {alert('请传入必要参数')return}let url = isLocalTest ? useTestBaseURL : baseURL//这里要请求对应的路由数据axios.post(url + '/api/getJSON',{routeName: this.state.routeName,itemName: this.state.itemName},).then((res: any) => {if (res.data.success === false) {alert(res.data.msg)return}let obj = res.datathis.clearJSON()let newObj = this.changeBaseURLtoDomain(obj)this.setState({json: newObj,historyList: [...this.state.historyList, newObj],}, () => {console.log("获取到最新的JSON", this.state.json);})}).catch((e) => {alert("获取后端json失败" + JSON.stringify(e))})}sendJSON = () => {let { routeName, itemName, isLocalTest, baseURL, useTestBaseURL } = this.stateif (this.state.json.type !== 'page') {alert('请确保在页面层级更新json')return}if (!routeName || !itemName) {alert('请传入必要参数')return}let obj = this.chengeDomaintoBaseURL(this.state.json)let url = isLocalTest ? useTestBaseURL : baseURLaxios.post(url + '/api/setJSON', {json: obj,routeName: this.state.routeName,itemName: this.state.itemName},{headers: {'Content-Type': 'application/json'}}).then((res) => {if (res.data.success === false) {alert(res.data.msg)return}if (res && res.data && res.data.json) {alert("配置成功")let obj = res.data.jsonthis.setState({json: obj})}}).catch((e) => {alert("存入配置失败" + JSON.stringify(e))})}//监听lowcode的json改变handleChange = (e: any) => {if (!e) returnthis.setState({json: e,historyList: [...this.state.historyList, e],step: this.state.step + 1}, () => {let { historyList, maxHistoryNum } = this.stateif (historyList.length > maxHistoryNum) {let limitObj = [...historyList].splice(-maxHistoryNum)this.setState({historyList: limitObj,step: this.state.step - 1})}console.log("change", this.state.historyList);})}//获取querycheckQuery = () => {let itemName = this.getQueryString('itemName')let routeName = this.getQueryString('routeName')if (itemName && routeName) {this.setState({itemName,routeName})}}// 获取查询字符串getQueryString = (name: string) => {var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");var r = window.location.search.substr(1).match(reg);if (r != null) {return unescape(r[2]);} else {return null};}//监听项目名输入inputItemName = () => {//@ts-ignorelet val = this.refs.itemName.value;this.setState({itemName: val})}//监听路由输入inputRouteName = () => {//@ts-ignorelet val = this.refs.routeName.value;this.setState({routeName: val})}//开始预览startPreview = () => {this.setState({preview: !this.state.preview})}//重置clearJSON = () => {this.setState({json: {}})}//上一步backHistoryJSON = () => {let { step, historyList } = this.stateif (step - 1 >= 0) {this.setState({step: step - 1,}, () => {this.setState({json: historyList[this.state.step]})})} else {alert('您当前没有历史记录')}}//下一步goHistoryJSON = () => {let { step, historyList } = this.statelet curStep = historyList.length - 1if (step < curStep) {this.setState({step: step + 1,}, () => {this.setState({json: historyList[this.state.step]})})} else {alert('已经是最新!')}}//根路径inputUrlName = () => {//@ts-ignorelet val = this.refs.baseURL.value;this.setState({baseURL: val,}, () => {window.localStorage.setItem('baseURL', this.state.baseURL)})}//转为domainchangeBaseURLtoDomain = (obj: any) => {let { baseURL } = this.stateif (!baseURL) returnlet str = JSON.stringify(obj)let res = str.replace(/${baseURL}/g, baseURL)return JSON.parse(res)}//转为${baseURL}chengeDomaintoBaseURL = (obj: any) => {let { baseURL } = this.stateif (!baseURL) returnlet str = JSON.stringify(obj)let urlReg = new RegExp(baseURL, 'g')let res = str.replace(urlReg, '${baseURL}')return JSON.parse(res)}render() {return (<>
    项目名: this.inputItemName()} />路由名: this.inputRouteName()} />设置baseURL: this.inputUrlName()} />
    )} } export default App;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    后端服务

     //server/app.js用于调试服务端
    const http = require("http");
    const fs = require('fs');
    const path = require('path');
    
    /**
     * 失败数据模型
     * @param {*} msg 消息 
     */
    function errModel (msg) {let obj = {success: false,msg}return JSON.stringify(obj)
    }
    
    http.createServer(function (req, res) {res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Headers', 'Content-Type');res.setHeader('Content-Type', 'application/json;');res.setHeader("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");console.log(req.url);console.log(req.method);if (req.method == 'OPTIONS') {res.writeHead(200, {'Content-Type': 'text/plain','Access-Control-Allow-Origin': '*','Access-Control-Allow-Headers': 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild, sessionToken','Access-Control-Allow-Methods': 'PUT, POST, GET, DELETE, OPTIONS'});res.end('');}if (req.method === 'POST' && req.url === '/api/setJSON') {let item = '';// 读取每次发送的数据req.on('data', function (chunk) {item += chunk.toString();});// 数据发送完成req.on('end', function () {let items = JSON.parse(item)if (items.routeName && items.itemName) {let file = path.join(__dirname, `${items.routeName}.json`)// json文件需要存入路径fs.writeFileSync(file, item)//将数据返回到客户端res.write(item);res.end();} else {res.write(errModel('文件配置失败, 检查路由或项目名是否正确'));res.end();}});}if (req.method === 'POST' && req.url === '/api/getJSON') {let item = '';// 读取每次发送的数据req.on('data', function (chunk) {item += chunk.toString();});// 数据发送完成req.on('end', function () {let items = JSON.parse(item)if (items.routeName && items.itemName) {let file = path.join(__dirname, `${items.routeName}.json`)fs.readFile(file, 'utf-8', function (err, data) {if (err) {console.log(err);res.write(errModel('请检查路由是否正确'));res.end();} else {let obj = JSON.parse(data)res.write(JSON.stringify(obj.json));res.end();}});} else {res.write(errModel('请检查路由或项目名是否正确'));res.end();}});}
    
    }).listen(3001); // 监听的端口 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    如何在Vue的前端项目中使用 ?

    1. 在静态目录public中的index.html引入对应的sdk,sdk官网有可以自行下载

      
    
    • 1

    2. 在路由允许的情况下调用封装的方法,即可渲染lowcode页面

     import Vue from 'vue'import defaultConfig from "./config";import axios from 'axios'var timer = nulllet defaultOptions = {method: 'local', // 'http' | 'local' 通过接口返回或者本地静态文件夹获取routeName: '', //输入路由名(必填)itemName: '', //项目名(必填)}let newOptions//修改后的配置/** * 在路由允许的情况下调用可生成对应lowcode页面 * @param {DOM} DOM  * @param {Object} options  */export const getLowcodePage = (DOM, options = {}) => {newOptions = Object.assign(defaultOptions, options)let { routeName } = newOptionsif ( {let { data } = resstartAmis(data)console.log('http', data);}).catch((e) => {alert("获取后端json失败" + JSON.stringify(e))})}}//amis renderconst startAmis = (jsonObj) => {console.log("jsonObj", jsonObj);let amis = window.amisRequire('amis/embed');amis.embed(DOM, jsonObj, {data: {baseURL: process.env.VUE_APP_API_BASE_URL}}, defaultConfig)}//entrancecheck(routeName)}" style="margin: auto" />
    
    • 1

    3. 做跨域代理

     //vue.config.jsdevServer: {proxy: {//测试lowcode使用'/api': {target: 'http://localhost:3001',changeOrigin: true,},}}, 
    
    • 1

    4. 开始调用方法

    
    
    
    
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    (附源码)springboot某高校绩效考核管理 毕业设计 012208
    科学家绘制全球140多万个湖泊和水库的水下地形图
    环形单链表问题
    虚拟机来安装Linux的优势
    Map常见的遍历方式-keySet 和 entrySet
    基于JavaWeb+SSM+Vue“鼻护灵”微信小程序系统的设计和实现
    Docker 镜像拉取
    MongoDB 2023年度纽约 MongoDB 年度大会话题 -- 企业级从传统数据库到NOSQL,你会更好...
    django setting.py中的SECRET_KEY
    多线程之JUC队列与数组
  • 原文地址:https://blog.csdn.net/weixin_53312997/article/details/126633735