DataColour DVP可视化平台的设计目标是向使用者提供一个强大、灵活的可视化解决方案。其中鉴于可视化项目的特殊性与用户的个性化需求,平台提供了一套完善的插件规范与运行时环境,让开发人员基于这些规范可以快度的建立出符合需求的、独特的视觉部件,让可视化项目可以真正的快速落地,并且逐渐形成积累,满足未来的需求。
平台对于部件开发者提供了如下支持:
集成开发工具:DataColour Cli
,使开发者可以脱离复杂的开发环境配置,结合已有的可视化服务,快速进入开发阶段。
部件开发模板:提供了一套完整的插件开发脚手架,基于TypeScript语言与Webpack打包管理,提高开发效率,降低开发的Bug率。
部件开发规范:平台提供了一套完备的插件规范,除此之外还提供了插件生命周期、平台调用接口等,让开发者可以轻松应对各种需求。
平台集成支持:平台提供了属性自动化配置方案、生命周期与事件管理等支持,让开发者可以快速建立出来可配置的属性编辑功能与生命周期事件调用扩展支持。
部件发布管理:平台提供了部件发布管理的功能,使开发者可以轻松的发布与更新开发的视觉部件,极大简化了环境的部署与迁移。
微前端技术栈:平台采用了乾坤微前端框架,可采用vue、react、angular等主流前端框架开发微应用组件,支持多技术栈融合。
平台采用了插件化/模块化的方式对部件进行管理,对于部件的开发提供了整套的开发模板及方案,开发语言可采用typescript或javascript。下面对于部件的开发进行详细的说明。
平台对于部件的加载采用了模块化的方式,加载器使用的是SystemJs,部件默认编译输出为umd模块,同时兼容其他格式,如amd,umd,system模块等。
平台支持微前端技术栈,通过QianKun微前端引擎的能力,让开发者可以选择擅长的前端技术进行组件开发,如:vue、react、angular等。SystemJs的使用请参照官网(https://github.com/systemjs/systemjs)
QianKun的使用请参照官网(https://github.com/umijs/qiankun)
部件目录的结构如下:
widgets
├── widget
│ ├── assets
│ │ └── widget.png
│ ├── bootstrap.js
│ ├── manifest.json
│ └── property.json
└── widgets.json
widgets:部件的根目录
widgets.json: 部件的注册配置文件
widget:部件目录,根据不同的部件,名称会不同
manifest.json:部件的清单描述文件
property.json:部件的属性描述文件
bootstrap.js:部件的入口文件,名称可自定义
assets:部件的资源目录
{
"name": "配置文件",
"version": "0.1.0",
"description": "指标控件配置",
"author": "FreezeSoul<freezesoul@gmail.com>",
"widgets": [{ "group": "通用控件", "path": "widget", "enable": true }]
}
widgets.json为平台部件的注册配置文件,结构如上面代码所示。
新增加的部件需要添加在widgets集合内,其中group标识了部件所属的组,path指定了部件的目录,enable为是否启用。
{
"id": "widget",
"tag": "Test",
"name": "测试控件",
"type": "simple",
"icon": "assets/widget.png",
"author": "FreezeSoul<freezesoul@gmail.com>",
"version": "0.1.0",
"description": "测试控件描述",
"bootstrap": "bootstrap.js"
}
manifest.json为部件的清单描述文件,结构如上面的代码所示。
id为部件唯一标识,请确保其在所有部件的唯一性,可根据部件业务意义命名,避免重复,一旦定义不可更改。tag可给部件定义一个标签。name为部件的名称。icon为部件的图标文件,目录相对部件目录。version为部件的版本号,更新部件后需要更新此版本号。description为部件的描述说明。bootstrap为部件的启动入口文件,需要指定。type为部件类型,simple为默认类型,即为默认支持的开发规范,microapp为微前端应用开发模式,采用qiankun微应用框架接入。
[
{
"name": "属性设置",
"type": "properties",
"children": [
{
"name": "基本",
"type": "group",
"children": [
{
"name": "文本测试",
"type": "text",
"default": "文本默认值",
"link": "test.text",
"tooltip": "文本测试提示"
},
{
"name": "多行文本测试",
"type": "textarea",
"default": "多行文本默认值",
"link": "test.textarea",
"tooltip": "多行文本测试提示"
},
{
"name": "数值测试",
"type": "number",
"default": 0,
"link": "test.number",
"tooltip": "数值测试提示"
},
{
"name": "布尔值测试",
"type": "boolean",
"default": false,
"link": "test.boolean",
"tooltip": "布尔值测试提示"
},
{
"name": "颜色测试",
"type": "color",
"default": "#FFFFFF",
"link": "test.color",
"tooltip": "颜色测试提示"
},
{
"name": "颜色多值测试",
"type": "colors",
"default": [
"#FE8463",
"#9BCA63",
"#FAD860",
"#60C0DD",
"#0084C6",
"#D7504B",
"#C6E579",
"#26C0C0",
"#F0805A",
"#F4E001",
"#B5C334"
],
"link": "test.colors",
"tooltip": "颜色多值测试提示"
},
{
"name": "资源测试",
"type": "resource",
"default": "",
"link": "test.resource",
"extension": ["png", "jpg", "gif", "bmp"],
"tooltip": "资源测试提示"
},
{
"name": "多值测试",
"type": "multiple",
"default": [0, 0, "0", "0"],
"link": "test.multiple",
"tooltip": "多值测试提示"
},
{
"name": "单选框测试",
"type": "radio",
"data": [
{
"value": "value1",
"text": "条目一"
},
{
"value": "value2",
"text": "条目二"
},
{
"value": "value3",
"text": "条目三"
}
],
"default": "value1",
"link": "test.radio",
"tooltip": "单选框测试提示"
},
{
"name": "复选框测试",
"type": "check",
"data": [
{
"value": "value1",
"text": "条目一"
},
{
"value": "value2",
"text": "条目二"
},
{
"value": "value3",
"text": "条目三"
}
],
"default": ["value1"],
"link": "test.check",
"tooltip": "复选框测试提示"
},
{
"name": "下拉测试",
"type": "select",
"data": [
{
"value": "value1",
"text": "条目一"
},
{
"value": "value2",
"text": "条目二"
},
{
"value": "value3",
"text": "条目三"
}
],
"default": "value1",
"link": "test.select",
"tooltip": "数值测试提示"
},
{
"name": "数组设置",
"type": "array",
"link": "test.array",
"tooltip": "多值测试提示",
"readonly": false,
"children": [
{
"name": "文本测试",
"type": "text",
"default": "文本默认值",
"link": "text",
"tooltip": "文本测试提示"
},
{
"name": "数值测试",
"type": "number",
"default": 0,
"link": "number",
"tooltip": "数值测试提示"
}
]
}
]
}
]
},
{
"name": "数据绑定",
"type": "bindings",
"children": [
{
"name": "序列绑定名称",
"type": "seriesBinding",
"tooltip": "序列绑定提示"
},
{
"name": "文本绑定名称",
"type": "textBinding",
"tooltip": "文本绑定提示"
},
{
"name": "列表绑定名称",
"type": "listBinding",
"tooltip": "列表绑定提示"
}
]
},
{
"name": "交互事件",
"type": "events",
"children": [
{
"name": "事件名称",
"tooltip": "事件名称提示"
}
]
},
{
"name": "导入配置",
"type": "imports",
"children": [
"path/property1.json",
"path/property2.json"
]
}
]
property.json为部件的属性描述文件,这里描述了部件的属性配置信息,数据绑定信息,交互事件信息。这些信息都与场景配置中的属性边栏、数据边栏、配置对象功能、生命周期与交互事件功能相关,配置前请先了解到这些功能的基本使用方式。
根结构为一个集合内容,目前支持四种类型的子对象,每种类型一项配置,分别用type进行了标识,type=properties 为属性配置信息, type=bindings为数据绑定信息,type=events为交互事件信息,type=imports为导入的外部配置。
属性配置信息最终会作为场景配置中部件的属性编辑器的配置元数据,它描述了部件属性配置功能与配置对象的映射关系,属性边栏即根据这些元数据而动态产生的配置界面。配置对象是部件的配置描述信息,是一个json形式的对象,部件通过此对象进行渲染,详情可以在后面的开发说明中了解到。
数据绑定信息是部件数据绑定功能的元数据,目前支持三种绑定协议。一般一个部件只与一个数据集产生绑定关系,也或没有绑定关系,所以bindings对象的children集合也可以空对象或根本没有bindings类型的对象。
交互事件信息定义了部件支持的交互事件,在使用中需要结合事件触发与配置的执行脚本联合产生作用。如果部件没有事件行为,同样events对象的children集合可以为空或根本没有events类型的对象。
导入外部配置目的是为了抽取公用部分的配置,只支持单层导入,但可导入多项,导入的配置中支持上面三种类型,导入后的配置会简单合并其children项,导入相对目录为widgets目录。注:导入的配置中不可再有imports的属性,以避免循环导入。
属性配置即:type=properties 下的children 目前支持如下类型:
类型(type) | 示例 | 说明 |
---|---|---|
group | { "name": "基本", "type": "group", "children": []} |
分组类型属性设置类型,在场景编辑器属性边栏中以一个折叠面板包裹其子对象,可以设置或不设置link,也可以嵌套group,但不建议过多。 |
text | {"name":"文本测试","type":"text","default":"文本默认值","link":"test.text","tooltip":"文本测试提示"} |
文本类型属性设置类型。 |
textarea | {"name":"多行文本测试","type":"textarea","default":"多行文本默认值","link":"test.textarea","tooltip":"多行文本测试提示"} |
多行文本类型属性设置类型。 |
number | {"name":"数值测试","type":"number","default":0,"link":"test.number","tooltip":"数值测试提示"} |
多行文本类型属性设置类型。 |
boolean | {"name":"布尔值测试","type":"boolean","default":false,"link":"test.boolean","tooltip":"布尔值测试提示"} |
布尔值类型属性设置类型。 |
color | {"name":"颜色测试","type":"color","default":"#FFFFFF","link":"test.color","tooltip":"颜色测试提示"} |
颜色类型属性设置类型。 |
colors | {"name":"颜色多值测试","type":"colors","default":["#FE8463","#9BCA63"],"link":"test.colors","tooltip":"颜色多值测试提示"} |
颜色多值类型属性设置类型。 |
resource | {"name":"资源测试","type":"resource","default":"","link":"test.resource","tooltip":"资源测试提示","extension": ["png"]} |
资源类型属性设置类型,extension指定扩展名,不设置/默认为图片资源。 |
multiple | {"name":"多值测试","type":"multiple","default":[0,0,"0","0"],"link":"test.multiple","tooltip":"多值测试提示"} |
多值类型属性设置类型,其中多值中内容是字符类型的,编辑器会使用文本输入模式,数值类型会使用数字输入模式。 |
radio | {"name":"单选框测试","type":"radio","data":[{"value":"value1","text":"条目一"},{"value":"value2","text":"条目二"}],"default":"value1","link":"test.radio","tooltip":"单选框测试提示"} |
单选框类型属性设置类型,data为选择数据。 |
check | {"name":"复选框测试","type":"check","data":[{"value":"value1","text":"条目一"},{"value":"value2","text":"条目二"}],"default":["value1"],"link":"test.check","tooltip":"复选框测试提示"} |
复选框类型属性设置类型,data为选择数据,返回值为集合。 |
select | {"name":"下拉测试","type":"select","data":[{"value":"value1","text":"条目一"},{"value":"value2","text":"条目二"}],"default":"value1","link":"test.select","tooltip":"数值测试提示"} |
下拉类型属性设置类型,data为选择数据。 |
array | {"name":"数组设置","type":"array","link":"test.array","tooltip":"多值测试提示","readonly": false,"children":[{"name":"文本测试","type":"text","default":"文本默认值","link":"test.text","tooltip":"文本测试提示"}]} |
数组类型属性设置类型,在场景编辑器属性边栏中会以一个可以动态创建子集合的标签页形式。内部可以包含其他类型的属性配置,也可以包含group分组类型。readonly指定是否只读,只读没有添加、删除支持 |
*注:
link是连接配置对象和属性输入内容的连接描述,通过字符串的方式描述了与配置对象的关系,以"."分割。除了group的link可选,其他类型的link都是必需设置的。
简单情况,如link = “test.text”的文本类型属性输入内容abc后,对应赋值后的属性配置对象为{test:{text:“abc”}}。
对于嵌套情况link是支持连接的,如group上存在一个link=“test.group”,children内部存在一个text类型的属性描述,其link=“test.text”,设置值为abc后,得到一个配置对象为{test:{group:{test:{text:"abc"}}}}。
重点说明一下array类型的属性描述,array是一个可以动态创建的数组类型,对应的配置对象为数组,如:{test:[]},内部支持上面同样的逻辑,代码对应了上面示例property.json转换后的配置对象。*
{
"test": {
"text": "文本测试",
"number": 0,
"textarea": "多行文本默认值",
"color": "#FFFFFF",
"colors": [
"#FE8463",
"#9BCA63",
"#FAD860",
"#60C0DD",
"#0084C6",
"#D7504B",
"#C6E579",
"#26C0C0",
"#F0805A",
"#F4E001",
"#B5C334"
],
"resource": "/resource/datacolour/043c70ba66c743199363ed5cd48c4eeb.png",
"multiple": [
0,
0,
"0",
"0"
],
"radio": "value1",
"check": [
"value1"
],
"select": "value1",
"array": [
{
"text": "文本默认值1",
"number": 0
},
{
"text": "文本默认值2",
"number": 0
}
]
}
}
绑定配置即:type=bindings 下的children 目前支持如下类型:
类型(type) | 示例 | 说明 |
---|---|---|
seriesBinding | {"name":"序列绑定","type":"seriesBinding","tooltip":"序列绑定提示"} |
序列绑定设置类型,用于支持存在category和values这种二维绑定上。 |
textBinding | {"name":"文本绑定","type":"textBinding","tooltip":"文本绑定提示"} |
文本绑定设置类型,用于绑定单个文本字段的绑定上。 |
listBinding | {"name":"列表绑定","type":"listBinding","tooltip":"列表绑定提示"} |
列表绑定设置类型,用于绑定整个二维表格的数据上。 |
说明:部件的绑定是支持多数据集的绑定的,而对于设置的绑定支持,会在场景配置中提供给部件相应的数据集关联能力,结合部件实现过程中setData的内容来完成数据渲染的需求。
交互事件配置即:type=events ,主要作用是配置部件的交互事件。
假设使用场景:部件设计内部有一个点击事件,点击触发的逻辑需要未来实现,可以通过平台生命周期/事件脚本编辑器编写脚本来灵活定制。针对这个要求,首先可以在children中声明其交互事件名称:{"name":"点击事件","tooltip":"点击部件触发"},然后在部件实现内部进行触发:widgetApii.emitEvent("点击事件", {}),而触发可通过实际部件的原生点击事件结合。
首先在部件内部代码逻辑中,完成事件的定义,通过原生事件触发定义的交互事件,传入名称与必要的参数
//部件声明的逻辑
$(this.container)
.off("click")
.on("click", (e) => {
this.widgetApi.emitEvent("点击事件", e);
});
后续可以在场景配置中,部件对应的事件中通过脚本执行复杂逻辑,或直接在属性页面配置跳转或筛选等操作
//声明周期事件中
console.log($event);
this.getApiPage().navigateStage(stageId,[]);
部件的开发可以采用typescript与javascript,但建议采用typescript的方式,下面以typescript的开发为例进行说明。
平台提供了部件的开发模板,通过模板可以打包、测试、发布部件。
部件需要按如下目录进行组织结构
├── assets
│ └── widget.png
├── manifest.json
├── property.json
└── widget.ts
其他文件都已进行了说明,下面重点说明widget.ts的开发
此接口部件需要实现,其中pageApi和widgetApi是系统赋值,无需实现,代码中可直接调用。
export interface WidgetInterface {
//页面API对象,系统赋值
pageApi?: ApiPageInterface;
//部件API对象,系统赋值
widgetApi?: ApiWidgetInterface;
//初始化
init(container: HTMLElement);
//设置参数
setOption(option: {});
//获取参数
getOption();
//设置数据
setData(data: Array<{ datasetId; data }>);
//渲染
render();
//缩放
resize();
//销毁
dispose();
}
页面api接口,部件代码中可调用对应的方法
/**
* @description 页面API代理类接口
* @author FreezeSoul
* @date 2019-12-20
* @export
* @interface ApiPageInterface
*/
export interface ApiPageInterface {
/**
* @description 是否编辑模式
* @memberof ApiPageInterface
*/
isEditMode(): boolean;
/**
* @description 获取内核根目录
* @returns {string}
* @memberof ApiPageInterface
*/
getCorePath(): string;
/**
* @description 获取系统信息对象
* @returns {Object}
* @memberof ApiPageInterface
*/
getSysInfo(): { accessToken: string; stageId: string; tenantId: string; variables: any; isEdit: boolean; version: string };
/**
* @description 获取场景路径
* @param stageId
* @param variables
* @returns {string}
* @memberof ApiPageInterface
*/
getStagePath(stageId: string, variables?: Array<{ name: string; value: string }>): string;
/**
* @description 获取部件根目录
* @returns {string}
* @memberof ApiPageInterface
*/
getWidgetsPath(): string;
/**
* @description 获取部件API对象集合
* @returns {Array<ApiWidgetInterface>}
* @memberof ApiPageInterface
*/
getApiWidgets(): Array<ApiWidgetInterface>;
/**
* @description 根据ID获取部件API对象
* @returns {ApiWidgetInterface}
* @memberof ApiPageInterface
*/
getApiWidgetById(id: string): ApiWidgetInterface;
/**
* @description 根据别名获取部件API对象
* @returns {ApiWidgetInterface}
* @memberof ApiPageInterface
*/
getApiWidgetByAliasName(aliasName: string): ApiWidgetInterface;
/**
* @description 根据ClassName获取部件API对象
* @returns {Array<ApiWidgetInterface>}
* @memberof ApiPageInterface
*/
getApiWidgetsByClassName(className: string): Array<ApiWidgetInterface>;
/**
* @description 根据GroupName获取部件API对象
* @returns {Array<ApiWidgetInterface>}
* @memberof ApiPageInterface
*/
getApiWidgetsByGroupName(groupName: string): Array<ApiWidgetInterface>;
/**
* @description 跳转场景
* @param stageId
* @param variables
* @memberof ApiPageInterface
*/
navigateStage(stageId: string, variables?: Array<{ name: string; value: string }>);
/**
* @description 设置变量
* @param variables
* @memberof ApiPageInterface
*/
setVariables(variables?: Array<{ name: string; value: string }>);
/**
* @description 设置变量
* @returns {Array<{ name: string; value: string }>}
* @memberof ApiPageInterface
*/
getVariables(): Array<{ name: string; value: string }>;
/**
* @description 获取缓存数据
* @param datasetId
* @memberof ApiPageInterface
*/
getDataById(
datasetId: string
): {
datasetId: string;
datasetName: string;
data: Array<any>;
success: boolean;
};
/**
* @description 获取缓存数据
* @param datasetName
* @memberof ApiPageInterface
*/
getDataByName(
datasetName: string
): {
datasetId: string;
datasetName: string;
data: Array<any>;
success: boolean;
};
/**
* @description 获取页面数据集信息
* @memberof ApiPageInterface
*/
getDatsetsInfo(): Array<string>;
/**
* @description 触发绑定事件
* @param widget
* @param eventName
* @param $event
* @memberof ApiPageInterface
*/
emitEvent(widget: ApiWidgetInterface, eventName: string, $event: any);
/**
* @description 订阅推送数据集
* @param {string} datasetId
* @param {string} constant
* @memberof ApiPageInterface
*/
subscribePushData(datasetId: string, constant: string, callback: (data: { datasetId: string; datasetName: string; data: Array<any>; success: boolean }) => void);
/**
* @description 检测插件存在
* @param {string} name
* @memberof ApiPageInterface
*/
existPlugin(name: string): boolean;
/**
* @description 注册个插件
* @param {string} name
* @param {*} plugin
* @memberof ApiPageInterface
*/
registerPlugin(name: string, plugin: any);
/**
* @description 取消注册插件
* @param {string} name
* @memberof ApiPageInterface
*/
unRegisterPlugin(name: string);
/**
* @description 获取一个插件
* @param {string} name
* @returns
* @memberof ApiPageInterface
*/
retrievePlugin(name: string);
}
部件api接口,部件代码中可调用对应的方法
/**
* @description 部件API代理类接口
* @author FreezeSoul
* @date 2019-12-20
* @export
* @interface ApiWidgetInterface
*/
export interface ApiWidgetInterface {
/**
* @description 获取部件ID
* @returns {string}
* @memberof ApiWidgetInterface
*/
getId(): string;
/**
* @description 显示部件
* @memberof ApiWidgetInterface
*/
showWidget();
/**
* @description 隐藏部件
* @memberof ApiWidgetInterface
*/
hideWidget();
/**
* @description 获取部件HTMLDOM对象
* @returns {HTMLElement}
* @memberof ApiWidgetInterface
*/
getElement(): HTMLElement;
/**
* @description 获取页面对象
* @returns {ApiPageInterface}
* @memberof ApiWidgetInterface
*/
getApiPage(): ApiPageInterface;
/**
* @description 获取部件对象
* @returns {WidgetInterface}
* @memberof ApiWidgetInterface
*/
getWidget(): WidgetInterface;
/**
* @description 获取aliasName
* @returns {string}
* @memberof ApiWidgetInterface
*/
getAliasName(): string;
/**
* @description 获取groupname
* @returns {string}
* @memberof ApiWidgetInterface
*/
getGroupName(): string;
/**
* @description 获取classname
* @returns {string}
* @memberof ApiWidgetInterface
*/
getClassName(): string;
/**
* @description 获取部件目录
* @returns {string}
* @memberof ApiWidgetInterface
*/
getWidgetPath(): string;
/**
* @description 获取部件的事件配置信息
* @returns {*}
* @memberof ApiWidgetInterface
*/
getEventsInfo(): Array<{ name: string; script: string }>;
/**
* @description 获取部件的绑定配置信息
* @returns {*}
* @memberof ApiWidgetInterface
*/
getBindingsInfo(): Array<{
property: string;
dataset: string;
schema: string;
binding: { text?: string; list?: Array<string>; category?: string; value?: Array<string> };
}>;
/**
* @description 根据绑定名称获取缓存数据
* @param bindingName
* @memberof ApiWidgetInterface
*/
getDataByBinding(
bindingName: string
): {
datasetId: string;
datasetName: string;
data: Array<any>;
success: boolean;
};
/**
* @description 触发绑定事件
* @param widget
* @param eventName
* @param $event
* @memberof ApiWidgetInterface
*/
emitEvent(eventName: string, $event: any);
}
WidgetInterface
为平台定义的部件实现接口,要求部件开发者按实际需求实现此接口。
ApiPageInterface
和ApiWidgetInterface
为平台提供的访问接口,向外提供了平台的功能支持。
在部件代码实现中,可以通过pageApi
和widgetApi
两个属性获取两个访问接口的实例(具体见WidgetInterface
接口定义),通过这两个实例调用可以做如场景页面过滤与场景页面导航等操作。
同时在页面配置过程中,也可以在部件生命周期或事件中获取到这两个访问接口,如在部件生命周期代码块中this
上下文就表示ApiWidgetInterface
实例,而在场景生命周期代码块中this
上下文则表示ApiPageInterface
实例,而两者也可以互相访问。这样,通过部件的生命周期与事件的能力再结合这两个接口就可以做到很多灵活的场景的定制要求了。
除了上面两个接口之外,平台在全局作用域下还注册Sysetm(window.System
),jQuery(window.$
),Lodash(window._
),LoadJs(window.loadJs
)等几个扩展支持,下面说明几个常规操作:
System.import
可以导入widget依赖的单个AMD外部库,如: System.import(scriptPath).then((module: any) => {...});
System.imports
可以导入widget依赖的多个AMD外部库,如: System.imports([scriptPath1,scriptPath2]).then((modules: any) => {...});
System.loadJs
可以用来重复导入执行JS和CSS文件,非AMD模式,如:System.loadJs([js1, css2]).then();
$
与_
是可以在部件的代码实现和部件的生命周期与事件中直接访问调用的开发注意事项:
注1:代码中的需要定义默认的部件配置信息(见下变量_option),通过接口setOption与getOption与平台传递,在渲染时部件可根据存储的配置内容进行动态的渲染。
注2: pageApi与widgetApi分别为平台暴露api接口,在部件代码中以及部件生命周期(this即widgetApi)与页面生命周期(this即pageApi)中均可访问。
注3:init接口方法支持异步操作,TS下可以使用async函数,JS下需要返回Promise对象,一般可作为异步资源的加载,比如依赖库资源的导入等。
注4:setData为数据推送上来后平台调用的接口方法,会传递上来一组数据集合(Array类型),对于部件绑定了多个数据集会凑齐其所有数据后传递给部件,多数据集推送频率尽量相同。
注5: render为部件的渲染接口方法,用以实现部件的渲染逻辑,此方法会根据不同的数据推送频率多次调用,没有数据绑定或者首次渲染会触发一次调用。
注6:api中的emitEvent方法作为触发事件的平台接口,对于配置了交互事件执行的脚本后,部件内部需要在事件逻辑中调用此方法触发配置的脚本执行,参数通过$event传递。
注7:部件开发中依赖其他AMD脚本组件需要通过System.import进行导入,目前平台已经默认导入jQuery、lodash等常用依赖库至全局上下文(即window下),可通过$、_直接访问。
注8: 对于导入的其他AMD脚本组件,路径建议通过getWidgetPath或getWidgetsPath获取当前部件路径或部件根目录后,进行资源路径拼接后导入,保证部署路径变更后正常使用。
注9: 对于部分组件需要暴露其插件能力的,可通过registerPlugin来注册一个插件对象,供其他部件在脚本逻辑中通过retrievePlugin获取插件,访问其能力。
注10: 对于需要获取当前页面指定数据集的数据内容可通过getDataById或getDataByName获取,获取到的数据为最新一次推送的数据集内容。
注11: 页面的过滤操作可通过setVariables来完成,只需要指定最新的过滤参数即可,如要获取当前页面下所有的过滤参数可通过getVariables拿到。
注12: 页面的跳转操作可通过navigateStage来完成,指定跳转场景ID,传入初始化过滤参数,即可完成页面跳转。
widget.ts为部件核心代码,建议文件名称按默认,基本代码结构如下
import { WidgetInterface } from "@interfaces/widgetInterface";
import "./assets/widget.less";
import templateString from "./assets/template.html";
export default class WidgetSiample implements WidgetInterface {
pageApi: ApiPageInterface;
widgetApi: ApiWidgetInterface;
//option为部件的配置对象,描述了部件的配置信息,部件通过此对象进行渲染,在场景配置过程中,属性设置后会写入到link对应的属性对象上的值。
_option: any = {
version: "1.0",
test: { text: "文本测试" },
func1: function(abc: string) {
console.log(abc);
}
};
container: HTMLElement;
constructor() {}
public async init(container: HTMLElement) {
this.container = container;
}
public setOption(option: any) {
this._option = _.cloneDeep(option);
}
public getOption() {
return _.cloneDeep(this._option);
}
public setData(data: any) {
console.log("数据打包上来了")
console.log(data)
}
public render() {
$(this.container).html(templateString);
$(this.container).find(".text-class").html(this._option.test.text);
}
public resize() {}
public dispose() {}
}
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory(root));
} else if (typeof exports === 'object') {
module.exports = factory(root);
} else {
root.Widget = factory(root);
}
})(typeof global !== "undefined" ? global: this.window || this.global,
function(root) {
'use strict';
class Widget {
constructor() {}
init(container) {
this.container = container;
let _this = this;
return new Promise(function(resolve){
System.import("abc").then(function($){
resolve();
});
});
}
setOption(option) {
this._option = _.cloneDeep(option);
}
getOption() {
return _.cloneDeep(this._option);
}
setData(data) {
console.log("数据打包上来了");
console.log(data);
}
render() {
$(this.container).html("<span style='color:#fff;'>javascript 部件 测试</span>");
}
resize() {}
dispose() {}
}
return function(){return {default:Widget}};
});
注: UMD模板请参考:https://github.com/umdjs/umd/blob/master/templates/returnExports.js
注:部件在初始化后,平台会将场景编辑时设置的参数返还给部件,调用setOption,在这时数据集推送数据如果已经抵达页面,平台会先调用setData给部件设置数据,再调用部件render,如数据未抵达,则会直接调用render,等待后面新一轮数据推送上来,再调用部件setData以及render,并且以此重复调用。对于设置了多个数据集绑定的部件,平台会等待多个数据集凑齐后,统一进行setData操作,请留意。
diagram
+-------------------------------+
| Widget constructor |
+---------------+---------------+
|
v
+---------------+---------------+
| Widget init |
+---------------+---------------+
|
v
+---------------+---------------+
| Widget setOption |
+---------------+---------------+
|
+---------------------------------+
| ^ |
| | v
| | +------------+-----------+
| | | Widget setData |
v | +------------+-----------+
+----------+----------+ | |
| Widget render | | |
+----------+----------+ | v
| | +----------+----------+
| | | Widget render |
| | +----------+----------+
| | |
| | |
+---------------------------------+
|
v
+---------------+---------------+
| Widget dispose |
+-------------------------------+
在TS模板与JS模板开发场景下,可以通过配置nginx来集成开发环境(推荐使用下面的集成开发工具),配置如下
server {
listen 9999 default_server;
listen [::]:9999 default_server;
location / {
proxy_pass http://测试服务器IP:PORT;
}
location ~ ^/core/widgets/(.*)$ {
proxy_pass http://127.0.0.1:8088/widgets/$1;
}
}
通过--path可指定部件路径名称,默认为widget。指定path后需要在widgets.json里进行相应配置。
注:path对应部件的目录名称,请针对具体情况进行设置。
npm run start-widget
npm run start-widget -- --path=部件路径
启动浏览器,打开测试地址 http://127.0.0.1:9999
/opt/google/chrome/chrome --disable-web-security --user-data-dir=/tmp/chrome_tmp
npm run build-widget:pro -- --path=部件路径
注:命令中的--path与调试模式作用一致。
http-server -p 8088
使用集成开发工具datacolour-cli开发更简单,免去了自行搭建开发调试环境。
全局安装cli
npm install -g datacolour-cli
datacolour-cli -h
datacolour-cli init 项目名称
在创建项目过程中,会拉取项目模板,在本地创建一个部件开发前端项目
在部件项目目录中输入如下命令,并按要求输入部件信息
datacolour-cli create
创建部件过程中,会根据部件模板创建一个部件实例,简化创建部件步骤
在部件项目目录中输入如下命令,并按要求选择一个部件
datacolour-cli debug
在部件调试时需要设置可视化服务器地址,命令会启动部件的调试模式,并自动打开浏览器
如需手工打开浏览器建议可以参考如下命令,打开后请访问测试地址 http://127.0.0.1:9999
#window
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir=~/chrome_tmp
#linux
/opt/google/chrome/chrome --disable-web-security --user-data-dir=/tmp/chrome_tmp
#osx
open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --disable-web-security --user-data-dir="/tmp/chrome_tmp"
在部件项目目录中输入如下命令,并按要求选择一个部件,构建后的部件在dist目录下
datacolour-cli build
构建的部件可根据需要手工上传至服务器的特定目录(official)
在部件项目目录中输入如下命令,并按要求选择一个部件,发布后的部件在publish目录下
datacolour-cli publish
发布部件后可通过平台的部件上传功能提交至平台,平台服务采用自动部署的方式部署至服务器特定目录(unofficial)
Usage: index [options] [command]
Options:
-v, --version output the version number
-h, --help output usage information
Commands:
init <name> 初始化Widget项目
list 列出所有Widget
create 创建一个Widget
debug 调试一个Widget
build 构建一个Widget
publish 发布一个Widget
buildAll 构建所有Widget
注:可通过window.clearDcCache()
清理本地缓存数据,如property.json修改后不能及时更新的问题。
<script src="./assets/jquery.js"></script>
<script src="./assets/crypto-js.js"></script>
<script>
function encryption(password) {
var iv = CryptoJS.enc.Latin1.parse("AES加密KEY");
var encrypted = CryptoJS.AES.encrypt(password, iv, {
iv: iv,
mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding
});
return encrypted.toString();
}
var tenantid = "1"; //租户ID
var stageid = "场景ID";
$.ajax({
url: "/auth/oauth/token",
headers: {
Authorization: "Basic dGVzdDp0ZXN0",
"Content-Type": "application/x-www-form-urlencoded",
"TENANT-ID": tenantid
},
type: "post",
data: {
username: "用户名",
password: encryption("密码"),
grant_type: "password",
scope: "server"
},
success: function(data) {
var variables = encodeURIComponent(JSON.stringify([{"name":"参数名称","value":"参数数值"}]));
location.href = "http://39.101.138.43:8080/core/?accesstoken=" + data.access_token + "&stageid=" + stageid + "&tenantid=" + tenantid + "&variables=" + variables; //&version=temp
},
error: function(data) {
alert(data.responseJSON.msg);
}
});
</script>
请求模拟示例,密码password_encryption需替换
curl -H "Authorization:Basic dGVzdDp0ZXN0" -H "TENANT-ID:1" -d "username=admin&password=password_encryption&grant_type=password&scope=server" http://39.101.138.43:8080/auth/oauth/token
其中password_encryption的加密过程:
在线加密:https://oktools.net/aes
AES加密模式:CBC
填充:ZeroPadding
数据块:128位
密码与偏移量:AES加密KEY
输出:base64
字符集:utf8编码
其他:
注:对于URL中的参数传递,系统中需要建立对应的参数,并在相应的数据集中进行关联。在此之后,参数便可以传递至数据集中进行数据过滤等要求。前端同样也可以在生命周期中通过API获取到此参数。
系统对接中,如跨域嵌入可视化系统,可以通过如下方式导航或过滤场景
//场景跳转,过滤参数可选
window.frames[0].postMessage({eventName: 'navigateStage', eventData: {stageId:"场景ID",variables: []}}, '*');
//场景过滤,过滤参数必选
window.frames[0].postMessage({eventName: 'setVariables', eventData: {variables: [{"name":"参数名称","value":"参数数值"}]}}, '*');
如可视化系统中内嵌子页面,可在子页面初始化前事件中定义如下脚本
try {
let variables = this.getVariables();
let sessionidObj = _.find(variables, { name: "sessionid" });
if (!sessionidObj) {
if (window.parent.DataColourAPI) {
let parentVariables = window.parent.DataColourAPI.getApiPage().getVariables();
sessionidObj = _.find(parentVariables, { name: "sessionid" });
this.setVariables([
{ name: "sessionid", value: sessionidObj.value },
]);
}
}
$.ajaxSetup({
headers: { 'sessionid': sessionidObj.value }
});
} catch{
console.debug("set session failed!!!")
}
更多补充中...
说明:本地需要具备nodejs基础环境,安装过程不再复述。
- 安装集成开发工具:
npm install -g datacolour-cli
- 初始化部件开发项目:
datacolour-cli init
- 进入部件项目目录,创建一个部件:
datacolour-cli create
- 执行完创建命令后,会自动创建一个基础的部件代码结构,根据需求进行部件开发
- 在部件开发过程中可执行命令进行部件开发调试:
datacolour-cli debug
- 完成部件开发后执行发布命令:
datacolour-cli publish
,发布内容在publish目录中- 将发布完成的部件按要求通过平台部件管理功能上传至可视化平台,即可在场景中使用开发的部件了
平台支持微前端技术栈,采用qiankun微前端引擎,结合平台提供的组件规范体系进行微应用组件的开发或接入,使开发者可以选择擅长的技术栈进行组件开发,如:vue、react、angular等。
{
"id": "microapp-demo",
"tag": "Test",
"name": "测试微应用",
"type": "microapp",
"icon": "assets/widget.png",
"option": {},
"author": "FreezeSoul<freezesoul@gmail.com>",
"version": "0.1.0",
"description": "测试微应用控件描述",
"bootstrap": "http://39.101.138.43:8080/core/widgets/microapp/vue3/dist/"
}
说明:type需配置为microapp,id唯一指定并与子组件名称保持一致,option为默认配置信息(可选),初始化时会传入组件内部,可以与平台提供的配置编辑器结合使用,bootstrap为微前端子组件的部署入口地址,其他配置参考默认开发规范。更多请参照子组件开发模板。