背景

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

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

核心

//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<any, StateType> {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 (<><div className='tabbar'><div><span className='ml20'>项目名:</span><input type="text" ref='itemName' placeholder={this.state.itemName} onChange={() => this.inputItemName()} /><span className='ml20'>路由名:</span><input type="text" ref='routeName' placeholder={this.state.routeName} onChange={() => this.inputRouteName()} /><span className='ml20'>设置baseURL:</span><input type="text" ref='baseURL' placeholder={this.state.baseURL} onChange={() => this.inputUrlName()} /><button className='send-btn' onClick={this.getJSON}>获取页面</button></div><div><button className='send-btn' onClick={this.backHistoryJSON}>上一步</button><button className='send-btn' onClick={this.goHistoryJSON}>下一步</button><button className='send-btn' onClick={this.clearJSON}>重置</button><button className='send-btn' onClick={this.startPreview}>{this.state.preview ? '编辑' : '预览'}</button><button className='send-btn' onClick={this.sendJSON}>点击配置生效</button></div></div><Editorvalue={JSON.parse(JSON.stringify(this.state.json))}onChange={this.handleChange}preview={this.state.preview}/></>)}
}

export default App; 

后端服务

 //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); // 监听的端口 

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

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

 <link rel="stylesheet" href="./lowcode/amis/antd.css" /><link rel="stylesheet" href="./lowcode/amis/iconfont.css" /><script src="./lowcode/amis/sdk.js"></script> 

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 (<img src="http.get(`lowcode/pages/${routeName}.json`, {}, { emulateJSON: true }).then((res) => {let obj = JSON.parse(res.bodyText)if (obj) {startAmis(obj)}}).catch((error) => {console.log("error", error);})}if (newOptions.method === 'http') {//正式项目需要通过post请求传入对象{routeName, itemName}//目前调试使用,注意某些跨域情况在vue.config.js中做跨域代理axios.get('/api/getJSON').then((res) => {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" />

3. 做跨域代理

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

4. 开始调用方法

<template><div id='main-lowcode'> <div id="content-lowcode"></div></div>

</template>

<script>
import { getLowcodePage } from '@/lowcode/index'

export default {data() {return {}},created() {},mounted() {// 获取lowcode页面getLowcodePage('#content-lowcode', {method: 'http', //接口请求routeName: 'client-admin',itemName: 'cms2'})}
}
</script>

<style lang="less" scoped>

</style> 
Logo

一站式 AI 云服务平台

更多推荐