手把手带你开发一个低代码可视化平台(一)(如何开发低代码平台)
起因
我在月初开源的商城零代码可视化搭建平台Mall-Cook受到大家喜爱,使我深受鼓励,谢谢大佬们的支持。本着授人以鱼不如授人以渔思想,决定在项目新建shelf分支,从零开发一个"干净"的可视化搭建平台,带大家开发一个自己的可视化搭建平台。
可视化搭建核心
架构
先看一下我理解的无代码可视化搭建平台架构,可视化搭建平台主要有两块:
- 项目拖拽搭建平台的构建
- 物料接入平台流水线的构建
核心
可视化搭建的核心是什么?
开发搭建平台使我们搭起了可视化构建的架子,接下来只需要根据实际使用场景更新物料库"运营"平台。 而在更新物料库时(老物料的迭代与新物料的开发),我们也需要同步修改平台来兼容物料库。那怎么解决这个问题呢?
我的解决办法是,利用json Schema协议规范Json的作用,用规范的Json来描述物料,然后用物料属性解析器来解析、构造物料的属性面板,我们更新物料库时只需要修改对应Json即可。最后提供Schema生成器可视化生成Json,避免用户手动修改Json,完成一整条物料接入平台流水线的构建。
这时我们再回到问题,我认为可视化搭建的核心是一套规范!
在平台中我们利用Schema协议定义了物料属性规范,然后遵循规范构建一条标准接入的流水线。我们在开发同理可以定义其他规范,完成组件交互、页面生命周期、远程接口调用等标准流水线(此平台为无代码平台,开发低代码平台需要上述功能)。
画虎画皮难画骨,我认为这才是可视化搭建的难点!
开发拖拽搭建面板
我们使用的拖拽插件是vuedraggable,主要逻辑为拖拽模板物料到页面面板,深拷贝物料配置数据到页面配置中。
核心代码
<!-- 物料模板列表 --> <draggable v-model="widgets" // 拖拽列表数据源 :options="{ group:{ name: 'itxst', // 可拖拽列组,相同表名可相互推拽 pull: 'clone' // 拖拽模板物料,复制到目标列表 }, sort: false // 是否可推拽排序 }" :clone="handleClone" // 复制模板物料执行方法 animation="300" // 动画延迟 > <div v-for="(item, index) in widgets" :key="index" class="control-widgets-item" > <i class="iconfont" :class="item.icon"></i> <span class="f13">{{ item.label }}</span> </div> </draggable>
<!-- 页面面板 --> <draggable v-model="mList" // 拖拽列表数据源 group="itxst" // 可拖拽列组,相同表名可相互推拽 ghostClass="ghost" // 拖动元素的占位样式class chosenClass="chosen" // 选中目标的样式class selector="selector" :animation="500" // 动画延迟 :sort="true" // 是否可推拽排序 class="panel" > <component v-for="item in mList" :key="item.id" :is="item.component" v-bind="item" ></component> </draggable>
// 拷贝物料模板 handleClone(cmp) { return { ...this.$cloneDeep(model), // 深拷贝物料模板 id: this.$getRandomCode(8), // 生成物料id },
开发可嵌套物料
想要实现可嵌套物料主要有三点:
- 物料中包含slot,已供子物料存放
- 物料配置中增加children属性,用于存放子物料配置
- 递归渲染物料实现无限层级嵌套
核心代码
<!-- 递归可嵌套组件 --> <draggable v-model="list" group="itxst" ghostClass="ghost" chosenClass="chosen" selector="selector" :animation="500" :sort="true" :class="[isWidget ? 'nest-child' : 'nest-area']" > <component v-for="item in list" :key="item.id" :is="item.component" v-bind="item" > // 包含slot的组件才能进行嵌套渲染 <ControlNestWidget :widgets.sync="item.children" // 子物料列表 :isWidget="true" // 是否为子物料 ></ControlNestWidget> </component> </draggable>
开发物料容器
接下来我们为页面的物料加一个保姆工具栏,使用容器组件可以解耦代码功能,功能如下所示
<!-- 物料操作容器 --> <div class="shape" @click.stop="setcurComponent(widget)" ref="shape"> <!-- 选中组件高亮 --> <div v-if="isCurComponent(widget.id)" class="shape-solid event-none"></div> <!-- 组件工具栏 --> <div v-if="show" class="shape-tab" :style="{ right: getRightStyle() }"> <!-- 选中显示删除按钮 --> <template v-if="isCurComponent(widget.id)"> <i class="iconfont icon-shanchu tab-icon f16" @click.stop="delComponent(chontrol.widgets, widget.id)" ></i> </template> <!-- 未选择显示物料名 --> <span v-else>{{ widget.label }}</span> </div> <!-- 插槽 --> <slot></slot> </div>
<!-- 使用物料容器 --> <widget-shape v-for="item in list" :key="item.id" :widget="item"> <component :is="item.component" v-bind="item" ></component> </widget-shape>
因为有嵌套组件,所以删除物料时应递归遍历删除
// 删除物料 delComponent(list, id) { // 遍历查找目标下标 let index = list.reduce((pre, cur, i) => { return cur.id == id ? i : pre; }, -1); // 找到目标,删除物料 if (index >= 0) { list.splice(index, 1); } else { // 递归子物料 list .filter((c) => c.children) .forEach((c) => { this.delComponent(c.children, id); }); } }
下一节预告
我们已经开发了可视化搭建的架子,下一节会讲述:
- 使用json描述物料
- 开发物料属性解析器,解析生成属性面板
- 开发属性面板基础类型组件(string、number等