[]
IVisualUpdateOptions、过滤模型与参数模型interface IFilterTarget {
column: string;
}
enum VisualFilterType {
Basic = 'basic',
Advanced = 'advanced',
Tuple = 'tuple',
}
interface IFilterBase {
target: IFilterTarget;
filterType: VisualFilterType;
}
enum BasicFilterOperator {
In = 'In',
NotIn = 'NotIn',
}
interface IBasicFilter extends IFilterBase {
operator: BasicFilterOperator;
values: any[];
}
enum AdvancedFilterOperator {
LessThan = 'LessThan',
LessThanOrEqual = 'LessThanOrEqual',
GreaterThan = 'GreaterThan',
GreaterThanOrEqual = 'GreaterThanOrEqual',
Contains = 'Contains',
DoesNotContain = 'DoesNotContain',
StartWith = 'StartWith',
DoesNotStartWith = 'DoesNotStartWith',
EndWith = 'EndWith',
DoesNotEndWith = 'DoesNotEndWith',
Is = 'Is',
IsNot = 'IsNot',
}
interface IAdvancedFilterCondition {
value: any;
operator: AdvancedFilterOperator;
caseSensitive?: boolean;
}
enum AdvancedFilterLogicalOperator {
And = 'And',
Or = 'Or',
}
interface IAdvancedFilter extends IFilterBase {
conditions: IAdvancedFilterCondition[];
logicalOperator: AdvancedFilterLogicalOperator;
}
type ITuple = { value: any }[];
interface ITupleFilter {
target: IFilterTarget[];
filterType: VisualFilterType;
operator: BasicFilterOperator;
values: ITuple[];
}
type IFilter = IBasicFilter | IAdvancedFilter | ITupleFilter;interface IParameterPair {
name: string;
value: any[];
}
interface IWatchedParameter {
meta: {
name: string;
display: string;
dataType: string;
type: string;
multiValue: boolean;
};
value: any[];
}interface IVisualUpdateOptions {
isViewer: boolean;
isMobile: boolean;
isEditing: boolean;
isFocus: boolean;
dataViews: any;
updateType: string;
properties: any;
language: string;
scale: number;
filters: IFilter[];
watchedParameters?: {
[key: string]: IWatchedParameter;
};
viewport: {
width: number;
height: number;
scale: number;
};
}字段 | 说明 |
|---|---|
| 是否为查看模式 |
| 是否为移动端 |
| 是否为编辑状态 |
| 是否为聚焦状态 |
| 按 |
| 更新类型 |
| 属性模型 |
| 当前语言 |
| 缩放比例 |
| 当前过滤器列表 |
| 监听到的仪表板参数 |
| 视口宽高与比例 |
WynVisual 主类与生命周期declare class WynVisual {
static Models = {
Filter: {
BasicFilter, AdvancedFilter, TupleFilter,
},
};
static Enums = {
FilterType,
BasicFilterOperator,
AdvancedFilterOperator,
AdvancedFilterLogicalOperator,
UpdateType
};
constructor(dom: HTMLDivElement, host: VisualNS.VisualHost, updateOptions: VisualNS.IVisualUpdateOptions);
update(options: VisualNS.IVisualUpdateOptions): void;
getInspectorHiddenState(updateOptions: VisualNS.IVisualUpdateOptions): string[];
getActionBarHiddenState(updateOptions: VisualNS.IVisualUpdateOptions): string[];
getColorAssignmentConfigMapping(dataViews: VisualNS.IDataView[]): VisualNS.IColorAssignmentConfigMapping;
onResize(): void;
onDestroy(): void;
}方法 | 说明 | 是否常用 / 必需 |
|---|---|---|
| 初始化 DOM、host、selectionManager、图表实例等 | 必需 |
| 数据或属性变化后触发 | 必需 |
| 返回不应显示在属性面板的路径列表 | 可选 |
| 返回不应显示在操作栏的路径列表 | 可选 |
| 颜色分配映射定义 | 启用 |
| 尺寸变化时触发 | 必需 |
| 销毁时触发,做资源清理 | 必需 |
import '../style/visual.less';
export default class Visual extends WynVisual {
private container: HTMLDivElement;
private host: VisualNS.VisualHost;
private properties: any;
private isMock: boolean = false;
private selectionManager: VisualNS.SelectionManager;
constructor(dom: HTMLDivElement, host: VisualNS.VisualHost, options: VisualNS.IVisualUpdateOptions) {
super(dom, host, options);
this.container = dom;
this.host = host;
this.properties = options.properties || {};
this.selectionManager = host.selectionService.createSelectionManager();
this.initializeDOM();
this.bindEvents();
this.update(options);
}
public update(options: VisualNS.IVisualUpdateOptions): void {
this.properties = options.properties;
this.processData(options.dataViews);
this.render();
}
public onResize(): void {
this.render();
}
public onDestroy(): void {
// 清理资源
}
}dataViews 与 propertiespublic update(options: VisualNS.IVisualUpdateOptions) {
const dataView = options.dataViews[0];
if (!dataView || !dataView.plain) {
this.isMock = true;
return;
}
const plainData = dataView.plain;
const labelFields = plainData.profile.label.values;
const valueFields = plainData.profile.value.values;
const data = plainData.data;
data.forEach(item => {
const label = item[labelFields[0].display];
const value = item[valueFields[0].display];
// 处理数据...
});
}public update(options: VisualNS.IVisualUpdateOptions) {
this.properties = options.properties;
const placeholder = this.properties.placeholder || '请选择...';
const textStyle = this.properties.textStyle;
const borderColor = this.properties.borderColor || '#d9d9d9';
this.applyStyles();
}private processData(dataViews: VisualNS.IDataView[]): void {
const dataView = dataViews[0];
if (!dataView || !dataView.plain || !dataView.plain.profile.label.values.length) {
this.isMock = true;
this.data = this.getMockData();
return;
}
this.isMock = false;
const plainData = dataView.plain;
const labelField = plainData.profile.label.values[0];
const valueField = plainData.profile.value?.values[0];
this.data = plainData.data.map(item => {
const selectionId = this.host.selectionService.createSelectionId();
selectionId.withDimension(labelField, item);
return {
label: item[labelField.display],
value: valueField ? item[valueField.display] : null,
selectionId: selectionId
};
});
}VisualHost 服务总览eventService方法 | 说明 | 示例用途 |
|---|---|---|
| 通知开始渲染 |
|
| 通知渲染成功结束 |
|
| 通知渲染异常 | 捕获渲染错误 |
| 注册 action bar 自定义事件回调 | 响应 |
示例:
this.visualHost.eventService.renderStart();
try {
this.render();
this.visualHost.eventService.renderFinish();
} catch (e) {
this.visualHost.eventService.renderError();
}localizationManager方法 | 说明 |
|---|---|
| 读取本地化字符串 |
toolTipService接口:
interface ITootipPosition {
x: number;
y: number;
}
interface ILabelFields {
label: string;
value: string;
}
interface ITooltipConfig {
position: ITootipPosition;
title?: string;
fields?: ILabelFields[];
selectionId?: SelectionId;
selected?: SelectionId[];
menu?: boolean;
}方法 | 说明 |
|---|---|
| 显示 tooltip |
| 隐藏 tooltip |
| 移动 tooltip |
示例:
this.host.toolTipService.show({
position: { x: event.clientX, y: event.clientY },
title: '数据详情',
fields: [
{ label: '名称', value: item.label },
{ label: '数值', value: item.value.toString() }
],
selectionId: item.selectionId,
selected: this.selectionManager.getSelectionIds(),
menu: true
});propertyService方法 | 说明 |
|---|---|
| 动态更新在 |
示例:
this.visualHost.propertyService.setProperty('showBorder', !this.properties.showBorder);formatService方法 | 说明 |
|---|---|
| 判断是否为自动单位 |
| 根据值数组计算真实单位 |
| 格式化数值 |
DisplayUnit 枚举示例:
enum DisplayUnit {
Auto = 'auto',
None = 'none',
Hundreds = 'hundreds',
Thousands = 'thousands',
TenThousand = 'tenThousands',
HundredThousand = 'hundredThousand',
Millions = 'millions',
TenMillion = 'tenMillion',
HundredMillion = 'hundredMillion',
Billions = 'billions',
Trillions = 'trillions',
}selectionService、SelectionId、SelectionManagerselectionService 方法:
方法 | 说明 |
|---|---|
| 创建选择管理器 |
| 创建选择 ID |
SelectionId 方法:
方法 | 说明 |
|---|---|
| 绑定度量字段 |
| 绑定维度字段 |
补充说明:
原始文档将 SelectionId 描述为一种“向量式”对象:它保存了与当前数据点绑定的维度和度量信息。
除交叉筛选外,它还会被复用于 tooltip 展示、选中态判断,以及 keep / exclude / drill / jump 等命令的 payload。
SelectionManager 方法:
方法 | 说明 |
|---|---|
| 设置选中项 |
| 获取选中项 |
| 获取选中数量 |
| 选择一个或多个 ID |
| 清除指定或全部选择 |
| 注册选择变更回调 |
| 检查是否已选中 |
| 检查是否为空 |
示例:
await this.selectionManager.select(selectionId, true);
await this.selectionManager.clear();
const isSelected = this.selectionManager.contains(selectionId);configurationManager方法 | 说明 |
|---|---|
| 读取 |
示例:
const amapToken = this.visualHost.configurationManager.get('amapToken');assetsManager方法 | 说明 |
|---|---|
| 返回打包图片的 base64 字符串 |
示例:
this.container.style.backgroundImage = `url(${host.assetsManager.getImage('testImg')})`;commandService方法 | 说明 |
|---|---|
| 执行 dashboard 命令 |
execute 的参数结构可以概括为:
interface ICommandDescription {
name: string;
payload: {
target?: string;
index?: number;
pageName?: string;
selectionIds?: SelectionId[];
position?: { x: number; y: number };
scenarioId?: string;
enterAnimation?: ScenarioAnimationType;
delay?: number;
duration?: number;
};
}支持的命令在原始文档中包括:
SwitchTab
SwitchPage
Keep
Exclude
Drill
Jump
ShowScenario
HideScenario
ToggleScenario
补充说明:
SwitchTab 用于切换选项卡容器;当同时传 index 与组件名时,index 优先生效。
SwitchPage 用于切换仪表板页面;如果 page index 或 page name 无效,将不会切换页面。页面很多时更推荐传页面名。
Keep / Exclude 通过 selectionIds 执行保留或排除。
Drill / Jump 可附带 position,用于在指定点击位置弹出或执行钻取/跳转。
ShowScenario / HideScenario / ToggleScenario 通过 scenarioId、动画、延时、持续时间控制组件显示状态。
示例:
this.host.commandService.execute([{
name: 'SwitchPage',
payload: { index: 0 }
}]);
this.host.commandService.execute([{
name: 'Keep',
payload: { selectionIds }
}]);filterService前提:需在 options.common.filter 中设置为 true 才能启用。
方法 | 说明 |
|---|---|
| 应用 Basic / Tuple / Advanced 过滤器 |
| 清除过滤器 |
补充说明:
过滤器对象会出现在 updateOptions.filters 中,可在 update() 时恢复先前过滤状态。
applyFilter 的目标对象来自数据视图中的 field profile,因此通常先从 dv.plain.profile... 取字段配置,再构造过滤器。
BasicFilter 类似:
SELECT * FROM table WHERE Dept IN (DTD1, DTD2, DTD3)常见方法:
setOperator/getOperator
setValues/getValues
toJSON/fromJSON
contains
remove/add
isEmpty
常见用法:
单维度标签筛选
在点击项时切换 In / NotIn
从 options.filters[0] 恢复过滤状态
TupleFilter 类似:
SELECT * FROM DataTable
WHERE (Team = 'DTD1' AND Prod = 'Wyn')
OR (Team = 'DTD2' AND Prod = 'SpreadJS');常见方法:
createTuple(dp, depth?)
setOperator/getOperator
setValues/getValues
toJSON/fromJSON
contains
remove/add
isEmpty
补充说明:
TupleFilter 适合多维组合筛选。
createTuple(dp, depth?) 可直接根据数据点生成元组,避免手写多列值结构。
AdvancedFilter 支持:
小于 / 小于等于 / 大于 / 大于等于
包含 / 不包含
开头是 / 开头不是
结尾是 / 结尾不是
是 / 不是
逻辑运算符:And / Or
补充说明:
文本比较时可通过 caseSensitive 控制是否区分大小写。
典型场景是数值区间筛选与文本模式匹配筛选。
contextMenuService方法 | 说明 |
|---|---|
| 显示上下文菜单 |
| 隐藏上下文菜单 |
补充说明:
原始 API 中 show 接收 IContextMenuConfig,核心字段是 position: { x, y }。
文档特别说明:执行 keep / exclude / drill / jump 后,上下文菜单会默认关闭,因此大多数情况下无需手动再调用一次 hide()。
moduleManager方法 | 说明 |
|---|---|
| 从宿主获取模块 |
原始资料明确列出支持的 modules:
echarts
lodash
html2canvas
使用前需在 visual.json > dependencies 中声明,例如:
{
"dependencies": ["echarts"]
}parameterService方法 | 说明 |
|---|---|
| 设置仪表板静态参数(非绑定类型)值 |
补充说明:
该服务用于设置静态参数,不是直接改写数据绑定字段。
支持传入单个 IParameterPair,也支持一次传入数组批量设置多个参数。
与 watchParameter 配合时,常见模式是:在一个组件里调用 setParameter,在另一个组件里通过 options.watchedParameters 响应变化。
示例:
this.host.parameterService.setParameter({
name: 'parameterName',
value: ['value1', 'value2']
});{
"name": "mainColorAssignment",
"type": "ColorAssignment",
"displayName": "Enable Color Assignment",
"defaultValue": true
}.style('fill', function (d: any) {
if (options.mainColorAssignment) {
return options.mainColorAssignment[d.data.size] || color(d.package);
} else {
return color(d.package);
}
});public getColorAssignmentConfigMapping(dataViews: VisualNS.IDataView[]): VisualNS.IColorAssignmentConfigMapping {
if (!dataViews.length) {
return null;
}
const plain = dataViews[0].plain;
const colorProfile = plain.profile.series.values[0];
const dimProfile = plain.profile.dimensions.values[0];
if (!colorProfile || !dimProfile) {
return null;
}
const colorValues = plain.data.map(d => d[colorProfile.display]);
return {
mainColorAssignment: {
values: Array.from(new Set(colorValues)),
type: 'dimension',
columns: [colorProfile],
},
};
}第 4 章定义了属性面板里的颜色分配器。
第 5 章定义了运行时如何告诉宿主“哪些值可以被赋色”。
这两部分必须配套,缺一不可。
public update(options: VisualNS.IVisualUpdateOptions): void {
this.watchedParameters = options.watchedParameters || {};
const parameterDefaultValue = this.watchedParameters['parameterDefaultValue'];
if (parameterDefaultValue && parameterDefaultValue.value) {
const meta = parameterDefaultValue.meta;
console.log('参数名称:', meta.name);
console.log('参数类型:', meta.dataType);
console.log('是否多值:', meta.multiValue);
console.log('参数值:', parameterDefaultValue.value);
}
}this.host.parameterService.setParameter([
{ name: 'param1', value: ['value1'] },
{ name: 'param2', value: ['value2', 'value3'] }
]);private setDefaultValueFromParameter(parameter: VisualNS.IWatchedParameter): void {
const values = parameter.value;
if (!values || values.length === 0) return;
values.forEach(paramValue => {
const matchedNode = this.findNodeByValue(paramValue);
if (matchedNode) {
this.selectNode(matchedNode);
}
});
}const filter = new WynVisual.Models.Filter.BasicFilter(
fieldProfile,
WynVisual.Enums.BasicFilterOperator.In,
selectedValues
);
this.host.filterService.applyFilter(filter);element.addEventListener('contextmenu', (e) => {
e.preventDefault();
this.host.contextMenuService.show({
position: { x: e.clientX, y: e.clientY }
});
});