[]
        
首页
开发者学堂
文档
论坛
市场
生态机会
活动
在线Demo 免费试用
(Showing Draft Content)

Visual API与运行时实现

5.1 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;
    };
}

字段说明

字段

说明

isViewer

是否为查看模式

isMobile

是否为移动端

isEditing

是否为编辑状态

isFocus

是否为聚焦状态

dataViews

按 capabilities.json 映射后的数据视图

updateType

更新类型

properties

属性模型

language

当前语言

scale

缩放比例

filters

当前过滤器列表

watchedParameters

监听到的仪表板参数

viewport

视口宽高与比例

5.2 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;
}

生命周期与方法说明

方法

说明

是否常用 / 必需

constructor

初始化 DOM、host、selectionManager、图表实例等

必需

update

数据或属性变化后触发

必需

getInspectorHiddenState

返回不应显示在属性面板的路径列表

可选

getActionBarHiddenState

返回不应显示在操作栏的路径列表

可选

getColorAssignmentConfigMapping

颜色分配映射定义

启用 ColorAssignment 时必需

onResize

尺寸变化时触发

必需

onDestroy

销毁时触发,做资源清理

必需

主指南中的基础类结构示例

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 {
        // 清理资源
    }
}

5.3 代码中消费 dataViews 与 properties

读取平面数据视图

public 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();
}

数据与选择 ID 的组合处理

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
        };
    });
}

5.4 VisualHost 服务总览

1)eventService

方法

说明

示例用途

renderStart()

通知开始渲染

update() 开始时

renderFinish()

通知渲染成功结束

render() 完成后

renderError()

通知渲染异常

捕获渲染错误

registerOnCustomEventCallback(fn)

注册 action bar 自定义事件回调

响应 event action

示例:

this.visualHost.eventService.renderStart();
try {
  this.render();
  this.visualHost.eventService.renderFinish();
} catch (e) {
  this.visualHost.eventService.renderError();
}

2)localizationManager

方法

说明

getDisplay(displayNameKey: string)

读取本地化字符串

3)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;
}

方法

说明

show(config)

显示 tooltip

hide()

隐藏 tooltip

move(pos)

移动 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
});

4)propertyService

方法

说明

setProperty(propertyName, value)

动态更新在 capabilities.json 中定义的属性

示例:

this.visualHost.propertyService.setProperty('showBorder', !this.properties.showBorder);

5)formatService

方法

说明

isAutoDisplayUnit(displayUnit)

判断是否为自动单位

getAutoDisplayUnit(values)

根据值数组计算真实单位

format(format, value, displayUnit?)

格式化数值

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',
}

6)selectionService、SelectionId、SelectionManager

selectionService 方法:

方法

说明

createSelectionManager()

创建选择管理器

createSelectionId()

创建选择 ID

SelectionId 方法:

方法

说明

withMeasure(profile, dataPoint?)

绑定度量字段

withDimension(profile, dataPoint)

绑定维度字段

补充说明:

  • 原始文档将 SelectionId 描述为一种“向量式”对象:它保存了与当前数据点绑定的维度和度量信息。

  • 除交叉筛选外,它还会被复用于 tooltip 展示、选中态判断,以及 keep / exclude / drill / jump 等命令的 payload。

SelectionManager 方法:

方法

说明

setSelectionIds(ids)

设置选中项

getSelectionIds()

获取选中项

getCount()

获取选中数量

select(id, multiSelect?)

选择一个或多个 ID

clear(id?)

清除指定或全部选择

registerOnSelectCallback(fn)

注册选择变更回调

contains(id)

检查是否已选中

isEmpty()

检查是否为空

示例:

await this.selectionManager.select(selectionId, true);
await this.selectionManager.clear();
const isSelected = this.selectionManager.contains(selectionId);

7)configurationManager

方法

说明

get(key: string)

读取 visual.json > configuration 中声明并在系统后台配置的参数

示例:

const amapToken = this.visualHost.configurationManager.get('amapToken');

8)assetsManager

方法

说明

getImage(imageName: string)

返回打包图片的 base64 字符串

示例:

this.container.style.backgroundImage = `url(${host.assetsManager.getImage('testImg')})`;

9)commandService

方法

说明

execute(commandsDesc)

执行 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 }
}]);

10)filterService

前提:需在 options.common.filter 中设置为 true 才能启用。

方法

说明

applyFilter(filter)

应用 Basic / Tuple / Advanced 过滤器

clean()

清除过滤器

补充说明:

  • 过滤器对象会出现在 updateOptions.filters 中,可在 update() 时恢复先前过滤状态。

  • applyFilter 的目标对象来自数据视图中的 field profile,因此通常先从 dv.plain.profile... 取字段配置,再构造过滤器。

11)过滤器模型

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 控制是否区分大小写。

  • 典型场景是数值区间筛选与文本模式匹配筛选。

12)contextMenuService

方法

说明

show({ position })

显示上下文菜单

hide()

隐藏上下文菜单

补充说明:

  • 原始 API 中 show 接收 IContextMenuConfig,核心字段是 position: { x, y }。

  • 文档特别说明:执行 keep / exclude / drill / jump 后,上下文菜单会默认关闭,因此大多数情况下无需手动再调用一次 hide()。

13)moduleManager

方法

说明

getModule(name)

从宿主获取模块

原始资料明确列出支持的 modules:

  • echarts

  • lodash

  • html2canvas

使用前需在 visual.json > dependencies 中声明,例如:

{
  "dependencies": ["echarts"]
}

14)parameterService

方法

说明

setParameter(pairs)

设置仪表板静态参数(非绑定类型)值

补充说明:

  • 该服务用于设置静态参数,不是直接改写数据绑定字段。

  • 支持传入单个 IParameterPair,也支持一次传入数组批量设置多个参数。

  • 与 watchParameter 配合时,常见模式是:在一个组件里调用 setParameter,在另一个组件里通过 options.watchedParameters 响应变化。

示例:

this.host.parameterService.setParameter({
  name: 'parameterName',
  value: ['value1', 'value2']
});

5.5 Color Assignment:从属性定义到运行时消费

属性定义

{
  "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 章定义了运行时如何告诉宿主“哪些值可以被赋色”。

  • 这两部分必须配套,缺一不可。

5.6 参数监听、交叉筛选、提示、上下文菜单与命令执行

读取监听参数

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 }
  });
});

本章来源文件