起因
我在月初开源的商城零代码可视化搭建平台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等
本文暂时没有评论,来添加一个吧(●'◡'●)