摘要:一个系统中可能会有很多很多页面,但经过比较可能会发现有部分模块非常相似,有的模块差别非常大,无论是手动引入组件还是其他方式,都会带来不小开发以及测试量,为此我们想能不能通过碎片的形式将页面组合起来呢?
一、碎片的理解
所谓的碎片主要分为两种
(1)那种很细很基础的组件,比如我们经常用的一个输入框, 一个表格,一个echat图。
(2)带一定业务逻辑的复杂模块,比如日志展示组件,智能分析模块等。
试想下有了这两种页面是不是很多页面都能通过拖拽的形式就行拼装出来呢?
二、碎片的展示
碎片千奇百怪,我们不可能一次引入所有组件,这样下次新增的时候又得修改代码。
这里我们使用动态注册的方式,如下所示:
代码语言:javascript复制toggle(info) {
this.registerComponent(info.componentPath).then((Component) => {
new Component({
el: `#${info.componentName}`,
store: this.$store,
router: this.$router,
data: {
customData: info.customData,
},
});
});
},
registerComponent(path) {
// eslint-disable-line
const _import = require('../../utils/_importRegist');
return _import(path).then(component => Vue.extend(component.default));
},
动态引入之后呢,就是动态展示的问题,这里使用的是is关键字,这样的话任何组件,包括输入框等表单类的组件也能展示。
代码语言:javascript复制<component
:componentName="item.name"
:conTainDrillDownData="drillDownData"
:customData="item.customData"
:initDirect="initDirect"
:is="item.componentName"
:style="item.style"
:styleList="item.style"
:wathcFrom="item.wathcFrom"
></component>
三、事件
我们经常遇到的一些交互,大部分是由数据改变驱动的,比如联动、下钻。常见的是由于某个数据变化,触发回调,然后有了相应的操作,这边呢,因为组件的独立性,没有这种回调关系。那么是不是可以反过来想,每个组件都有自己的独立数据仓库,然后数据改变传递到仓库,然后每个独立组件都可以监听很多个仓库,任何一个改变可以触发相应的操作。
代码语言:javascript复制watch: {
queryFormData(newVal, oldVal) {
const newValOfJSON = JSON.parse(newVal);
this.wathcFrom.forEach((item) => {
if (JSON.stringify(newValOfJSON[item]) !== JSON.stringify(JSON.parse(oldVal)[item])) {
this.doSomething({ ...newValOfJSON[item] });
}
});
},
},
代码语言:javascript复制还有一些特殊事件我们可以用模版化的方式实现,也就是选择具体功能的函数模版,然后配置上去。
当然这里还可以动态输入函数,然后用new Function去实现,但考虑到用户可能不会写这个函数,就暂时没这么去实现。
四、数据源
目前主要通过固定解析JSONSchema的模版函数去实现,当数据源接入的越来越多,这种函数模版就需要逐渐扩充。还有就是调取数据源需要的一些参数,参数无非分为两部分,固定参数(常量),和逻辑分析参数,
这块也是参考了非常多的系统后得出的适合当前项目业务的方案,类似的方案有操作数据库表的方式等。
出于安全原因,代码就不透露了。
五、容器
碎片有了之后就需要一个容器来盛放,这里定义了一个统一入口组件来盛放。
这样会带来一些细节问题,如嵌套关系父组件套子组件,子组件套父组件的情况,这里采用类似路由懒加载,通过采用回调函数函数的形式引入组件可以解决这个问题。
代码语言:javascript复制const ContainBox = () => import('./ContainBox.vue');
还有就是当配置了多个页面,页面跳转势必带来组件被复用的问题,这里通过判断路由跳转,给组件绑定不同key的形式来解决。
六、模版
比如我们配置给组建的文案肯定不是一层不变的,有时候随着联动事件触发,会发生一些相应的变化或者说一些逻辑关系的处理。这里我们可以采用类似于vue的模版去解析变量,比如解析:{{clientArea}}的数据。
代码语言:javascript复制templateMatch(str, queryArg) {
// 模版匹配
let lineName = str;
const reg = /{{(. ?)}}/g;
if (reg.test(lineName)) {
lineName = lineName.replace(reg, (...args) => this.getValue(args[1], queryArg));
}
return lineName;
},
代码语言:javascript复制getValue(val, data) {
return val.split('.').reduce((data, currentVal) => {
if (!data[currentVal]) {
return '';
}
return data[currentVal];
}, data);
},
然后还有一些技术细节就不再赘述,比如使用mix合并公用代码、new Function等。
七、定制化需求
目前定制化需求,主要是通过手动输入一些定制化函数,然后页面动态进行解析。
八、用户配置界面
用户配置界面是通过用户拖拽生成的,大小等是通过grid网格布局的形式,让用户随意拖动摆放
代码语言:javascript复制 <grid-layout
:layout.sync="layout"
:col-num="50"
:row-height="20"
:is-draggable="true"
:is-resizable="true"
:vertical-compact="true"
:use-css-transforms="true"
ref="gridlayout"
>
<grid-item v-for="(item, index) in layout"
class="gridItem bg-white"
:key="item.i"
:static="false"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
@moved="movedEvent"
@resized="resizeEvent"
ref="layoutItem"
@click.native="handleClick(item, index)"
>
</grid-layout>
通过配置界面生成JSONSchema 用户所有的配置数据都写入其中,然后通过前面的步骤进行解析。