转至元数据结尾
转至元数据起始


1. 内容概述



Visual api 用于呈现自定义的可视化效果。它提供了一些有用的界面帮助作者做一些工作,例如显示提示、交叉筛选、管理选择状态和 i18n。

2. Interface



interface IFilterTarget {
    // dataset: string;
    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',
}
interface IAdvancedFilterCondition {
    value: any;
    operator: AdvancedFilterOperator;
  }
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 IVisualUpdateOptions {
    isViewer: boolean;
    isMobile: boolean;
    isEditing: boolean;
    isFocus: boolean;
    dataViews: any;
    updateType: string;
    properties: any;
    // docTheme: RuntimeNS.IDocTheme;
    language: string;
    scale: number;
    filters: IFilter[];
}

VisualUpdateOption是可视化更新时的数据包(包含聚合更改或属性更改)。

  • isViewer:表示在预览仪表板时是否呈现该组件。
  • isMobile::表示是否在移动端显示该组件。
  • isEditing:表示组件的编辑状态。当触发了编辑操作时,值为true。
  • isFocus: 表示组件的聚焦状态。当触发了聚焦操作时,值为true。
  • dataViews: 在capabilities.json中定义的计算数据视图(dataViewMapping)。
  • properties: 在 capabilities.json 中定义的属性模型(options.visual)。
  • docTheme: 选择的文档主题。
  • language: 当前语言。
  • scale: 比例因子。
  • filters: 用来影响其他组件。


3. Visual



要实现的主类。

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;
}
  • 更新将调用每个聚合更改和属性更改。

  • getInspectorHiddenState返回值路径列表,表示此属性不应显示在属性设置中。

  • getActionBarHiddenState返回值路径列表,表示此属性不应显示在操作栏中。
  • getColorAssignmentConfigMapping 返回一个颜色赋值映射对象,表示应该给哪个值赋值颜色。
  • 在调整组件对象的大小时将调用onResize。
  • 卸载时将调用onDestroy。
  • 静态属性Models和Enums为自定义可视化插件提供一些类或枚举。

4. Color Assignment 示例




//capabilities.json
{
    options: {
        visual: [
            {
                properties: [{
                    "name": "mainColorAssignment",
                    "type": "ColorAssignment",
                    "displayName": "Enable Color Assignment",
                    "defaultValue": true
                }]
            }
        ]
    }
}
 
// visual.ts
export default class Visual extends WynVisual {
  //...others method
 
  private render() {
    //...
    const circle = node.append('circle')
      .attr('r', function (d: any) { return d.r; })
      .style('fill', function (d: any) {
        //consume the color assignment property
        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;
    }
 
 
    // make the values in series color assignmentable
    const colorValues = plain.data.map(d => d[colorProfile.display]);
    return {
      mainColorAssignment: {
        values: Array.from(new Set(colorValues)),
        type: 'dimension',
        columns: [colorProfile],
      },
    };
  }
}


5. VisualHost



eventService

EventService 用于调度一些渲染事件,并且用户可以注册操作栏自定义事件处理程序。

方法interface说明示例
renderStart
renderStart(): void


用于通知仪表板该组件的渲染状态

// Update method will be invoke while something changed.
public update(options: VisualNS.IVisualUpdateOptions) {
    this.visualHost.eventService.renderStart();

    try {
      this.render(); // render method is used to update UI
      this.visualHost.eventService.renderFinish();
    } catch (e) {
      this.visualHost.eventService.renderError();
    }
  }
renderError
renderError(): void


renderFinish 
renderFinish(): void


registerOnCustomEventCallback 

registerOnCustomEventCallback(fn: (name: string) => void)

用于注册操作栏事件

callback 是自定义事件处理程序



// Register the callback in visual constructor. It will print the  action name when you click the corresponding button on action bar.
this.visualHost.eventService.registerOnCustomEventCallback((name: string) => {
      console.log(name);
    });


localizationManager

LocalizationManger 用于在此处提供一些本地化的字符串。

方法interface说明示例
getDisplay 
getDisplay(displayNameKey: string): string 

返回本地化字符串

const noDataText = this.visualHost.localizationManager.getDisplay('NoData');
this.container.textContent = noDataText;


toolTipService

TooltipService 用来控制提示的显示/隐藏。

方法interface说明示例
show
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: VisualImplNS.ITooltipConfig): void

显示提示(tooltip)

  • position: 鼠标光标位置
  • title: 提示的标题
  • fields: 需要显示在提示信息中的字段
  • selectionId:显示字段的另一种方式,提示信息将显示您绑定到selectionId的字段
  • selected: 所有选择的 selectionId
  • menu:应在提示上显示菜单,例如向下钻取,保留和排除。


 // this function will bind to the chart mouse enter event. It will show a tooltip with title, fields, selection info and menu buttons.
private mouseEnterHandler = (node: any) => {
    this.visualHost.toolTipService.show({
      position: {
        x: d3.event.x,
        y: d3.event.y,
      },
      title: node.data.color,
      fields: [{
        label: this.valueField,
        value: node.data.value,
      }],
      selectionId: node.data.selectionId,
      selected:  this.selectionManager.getSelectionIds(),
      menu: true,
    });
  }
hide
hide(): void

隐藏提示(tooltip)

  // hide the tooltip when the mouse leave the chart shape
private mouseLeaveHandler = () => {
    this.visualHost.toolTipService.hide();
  }
move
move(pos: VisualImplNS.ITootipPosition): void

移动提示(tooltip)

  // update the tooltip position while mouse move on chart
private mouseMoveHandler = (e: any) => {
    this.visualHost.toolTipService.move({
        x: d3.event.x,
        y: d3.event.y,
    });
  }


propertyService

PropertyService 用于更新在 capabilities.json 中定义的属性(options.visual)。

方法interface说明示例
setProperty
setProperty(propertyName: string, value: any): void

更新属性

// when the button be clicked. The property of showBorder will be change.


private onToggle = () => {
    this.visualHost.propertyService.setProperty('showBorder', !this.properties.showBorder);
  }


formatService

FormatService用来设置格式化。

方法inferface说明示例
isAutoDisplayUnit
isAutoDisplayUnit(displayUnit: DisplayUnit): boolean
判断 displayUnit 是否为DisplayUnit.Auto
enum DisplayUnit {
  Auto = 'auto',
  None = 'none',
  Hundreds = 'hundreds',
  Thousands = 'thousands',
  TenThousand = 'tenThousands',
  HundredThousand = 'hundredThousand',
  Millions = 'millions',
  TenMillion = 'tenMillion',
  HundredMillion = 'hundredMillion',
  Billions = 'billions',
  Trillions = 'trillions',
}
getAutoDisplayUnit
getAutoDisplayUnit(values: number[]): DisplayUnit

当displayUnit为DisplayUnit.Auto时,得到realDisplayUnit。


format
format(format: string, value: number): string

格式化数值。

第一个参数格式来自 Format Property或 DataView Profile 项目。

默认 displayUnit 为 DisplayUnit.None;

const formatService = this.visualHost.formatService;
const { displayUnit, labelFormat } = updateOptions.properties;
 
let realDisplayUnit = displayUnit;
if (formatService.isAutoDisplayUnit(displayUnit)) {
    const values = dataPoints.map(dataPoint => dataPoint.value);
    realDisplayUnit = formatService.getAutoDisplayUnit(values);
}
 
dataPoints.forEach((dataPoint) => {
    // prepare a formated text for label.
    dataPoint.labelText = formatService.format(labelFormat, dataPoint.value, realDisplayUnit);
})

selectionService

方法interface说明示例
createSelectionManager
createSelectionManager(): SelectionManager

创建一个SelectionManager。

this.selectionManager = this.visualHost.selectionService.createSelectionManager();
createSelectionId
createSelectionId(): SelectionId

为数据点创建SelectionId。

const selectionId = this.visualHost.selectionService.createSelectionId();

SelectionId

SelectionId就像一个向量。它包含绑定到它的维度和度量。将 selection id 附加到数据点后,在图表中单击时就可以触发。

还可以用SelectionId 做很多事情,例如交叉筛选、呈现选择状态、显示提示信息和链接文档。

方法interface说明示例
withMeasurewithMeasure(profile: RuntimeNS.IFieldProfile): SelectionId

将度量字段绑定到selection id。



  public update(options: VisualNS.IVisualUpdateOptions) {
    console.log(options);
    const dataView = options.dataViews[0];
    // convert the data view to datas which can be used in chart
    if (dataView) {
      const plainData = dataView.plain;


      // get the fields via different data role.
      const valueField = plainData.profile.values.values[0].display;
      const sizeField = plainData.profile.series.values[0].display;
      const colorField = plainData.profile.dimensions.values[0].display;

      const items = plainData.data.reduce((result: any, item: any, i: number) => {
        if (item[valueField]) {
          // create a selection id for a data point.
          const selectionId = this.visualHost.selectionService.createSelectionId();

          // set the dimensions of the selection id. This will affect on the crossfilter result and others.
          selectionId
            .withDimension(plainData.profile.dimensions.values[0], item)
            .withDimension(plainData.profile.series.values[0], item);

            // attach the selection id on data model. so that you can access it on hit test
          result.push({
            size: (item[sizeField] || '-') + '',
            value: item[valueField],
            color: (item[colorField] || '') + '',
            id: i,
            selectionId,
          });
        }
        return result;
      }, []);

      // refresh the chart with new data items
      this.render(items);
    }
  };
withDimensionwithDimension(profile: RuntimeNS.IFieldProfile, dataPoint: RuntimeNS.IDataPoint): SelectionId

将纬度字段绑定到selection id。




SelectionManager

Selection manager 用于管理选择状态,并触发交叉筛选。

方法interface说明示例
setSelectionIds
setSelectionIds(ids: Array<SelectionId>)
setter
getSelectionIds
getSelectionIds(): Array<SelectionId>
getter
getCount
getCount(): number
获取所选selectionId的计数
select
select(id: SelectionId | Array<SelectionId>, multiSelect: boolean = false): Promise<void>

选择一个或多个selectionId。multiselect表示应该支持选择多个selectionId。它返回一个许可,并将在选择完成后解决。

  private clickHandler = (node: any) => {
    d3.event.stopPropagation();
	// get selection id which bind on data model
    const selectionId = node.data.selectionId
	// if it has been selected, unselect it.
    if (!this.selectionManager.contains(selectionId)) {
      this.selectionManager.select(selectionId, true);
    } else {
      this.selectionManager.clear(selectionId);
    }

	// show tooltip
    this.visualHost.toolTipService.show({
      position: {
        x: d3.event.x,
        y: d3.event.y,
      },
      title: node.data.color,
      fields: [{
        label: this.valueField,
        value: node.data.value,
      }],
      selectionId: node.data.selectionId, // the field which display in tooltip
      selected: this.selectionManager.getSelectionIds(), // all the selected selectionId, will be use in keep/exclude and drill.
      menu: true,
    });
  }
clear
clear(id?: SelectionId): Promise<void>

清除选中的或者全部 SelectionId。


registerOnSelectCallback
registerOnSelectCallback(onSelectCallback: (ids: SelectionId[]) => void)

Register a callback, the callback will be invoke after every selection change.

注册 callback,每次选择更改后都将调用callback。


this.selectionManager.registerOnSelectCallback(() => {
	// while the selection state changed, should update ui, to highlight the shape which has been selected
      this.render();
    });
contains
contains(id: SelectionId): boolean

检查目标id 是否被选中。


isEmpty
isEmpty(): boolean

检查 selection manager 是否包含选中的selectionId。





configurationManager

ConfigurationManger should work with the configuration in visual.json. If you defined a configuration item. you can access it via this.

ConfigurationManger  应与 visual.json中的配置项一起使用。如果定义一个配置项目,就可以通过这个进行访问。

methodinterfacedescriptionsample
get
get(key: string): string

获取配置项

// to access the amapToken in configuration
const amapToken = this.visualHost.configurationManager.get('amapToken');

assetsManager

AssetsManager  应与 visual.json 中的 assets 一起使用。当前您可以在assets 中定义图片资源,它们将被打包。可以通过这个访问图片。

methodinterfacedescriptionsample
getImage
getImage(imageName: string): string

返回base64字符串

// to access the image in assets
this.container.style.backgroundImage = `url(${host.assetsManager.getImage('testImg')})`;


commandService

CommandService 提供了一些仪表板命令,例如切换选项卡容器。

方法interface说明示例
execute
interface ICommandDescription {
    name: string;
    payload: {
      target: string;
    };
  }


execute(commandsDesc: Array<ICommandDescription>): void
执行命令
// run a command to active a tab container


this.visualHost.commandService.execute([{
      name: 'SelectTab',
      payload: {
        target: 'myTabContainer',
        index: 0,
      }
    }])

SwitchTab

Command Name描述说明示例
SwitchTab

用户可以使用这个命令来切换选项卡组件中的标签。

  • SwitchTab 命令是专为选项卡设计的命令。

  • 标签的索引将通过参数命令传递。
    以下是 SwitchTab 的接口。

  • 如果 index 小于0,将切换到0;如果 index 大于标签计数-1,将切换到最后一个标签。

  • 如存在多个标签时,建议使用选项卡的组件名称来定位标签。

  • 如果 index 和组件名称同时被传递,则 index 优先生效。

  • 在Visual Plugin中使用 command service 来调用`SwitchTab`命令。

  • 在命令被立即执行后,选项卡标签将显示为预期的样子。

  • 如果执行失败,返回值将是 false,否则是 true。

SwitchPage

Command Name描述说明示例
SwitchPage

用户可以使用这个命令来切换仪表板页面。

  • SwitchPage 命令是专仪表板设计的。

  • 如果 page index 或 page name 无效,将不进行页面切换。

  • 如果存在大量的页面,page index 就很难计算。建议使用页面名称(page name)。

  • 如果page index 和 page name 同时被传递,那么page index 优先生效。

  • 在Visual Plugin 中使用command service 来调用`SwitchPage`命令。

  • 在命令被立即执行后,页面切换将显示为预期的效果。

Keep/Exclude

Command Name描述说明示例
Keep/Exclude

用户可以使用这个命令来执行保留或排除操作。

// Keep & Exclude
this.host.commandService.execute([{
    name: 'Keep', // or 'Exclude'
    payload: {
        selectionIds,  
    },
}]);

Drill/Jump

Command Name描述说明示例
Drill/Jump

用户可以使用这个命令来执行钻取或跳转操作。

// Drill & Jump
this.host.commandService.execute([{
    name: 'Drill', // or 'Jump'
    payload: {
        selectionIds, // optional
        position: { x: event.clientX, y: event.clientY },
    },
}]);



filterService

FilterService用于派遣过滤器影响其他场景。过滤器将被列在 updateOptions.filters 中。

目前,您可以创建Basic,Tuple和Advanced Filter。

首先,你应该通过设置options.common.filter为 "true "来启用过滤器。


方法interfacedescription示例
applyFilter
interface IFilterTarget {
    // dataset: string;
    column: string;
}

enum VisualFilterType {
  Basic = 'basic',
  Advanced = 'advanced',
  Tuple = 'tuple',
}

 interface IFilterBase {
    target: IFilterTarget;
    filterType: VisualFilterType;
 }
 
applyFilter(filter: BasicFilter | TupleFilter | AdvancedFilter): void
这个方法用来唤醒过滤器。
dom.addEventListener('click', (e) => {
      const it = e.target as HTMLLIElement;
      if (it.hasAttribute('data-value')) {
        const val = it.dataset.value;

        if (this.filter.contains(val)) {
          this.filter.remove(val);
        } else {
          this.filter.add(val);
        }

        host.filterService.applyFilter(this.filter);
      } else if (it.getAttribute('data-role') === 'toggle') {
        this.filter.setOperator(this.filter.getOperator() === Enums.BasicFilterOperator.In ?
          Enums.BasicFilterOperator.NotIn :
          Enums.BasicFilterOperator.In
        );
        host.filterService.applyFilter(this.filter);
      }
    })
clean
clean(): void
清除过滤器
host.filterService.clean();


BasicFilter

与内置的标签列表类似,相当于SQL:

SELECT * FROM table WHERE Dept IN ( DTD1 , DTD2 , DTD3 )
方法interfacedescription示例
constructor
enum BasicFilterOperator {
  In = 'In',
  NotIn = 'NotIn',
}


interface IBasicFilter extends IFilterBase {
    operator: BasicFilterOperator;
    values: any[];
 }


constructor(targetProfile: RuntimeNS.IFieldProfile, operator: BasicFilterOperator = BasicFilterOperator.In, values: any[] = [])



targetProfile是数据视图中的过滤区域。
const profile = dv.plain.profile.dimensions.values[0];
const BasicFilter = WynVisual.Models.Filter.BasicFilter;
const filter = new BasicFilter(profile);
setOperator/getOperator
setOperator(operator: BasicFilterOperator) 
getOperator(): BasicFilterOperator

this.filter.setOperator(this.filter.getOperator() === WynVisual.Enums.BasicFilterOperator.In ?
          WynVisual.Enums.BasicFilterOperator.NotIn :
          WynVisual.Enums.BasicFilterOperator.In
        )

setValues/getValues
getValues(): any[] 
setValues(vals: any[])

filter.setValues(['DTD1', 'DTD2'])

toJSON/fromJSON
toJSON(): BasicNS.IBasicFilter


fromJSON(obj: BasicNS.IBasicFilter)
从updateOptions.filters中恢复过滤器。
const profile = dv.plain.profile.dimensions.values[0];
const filter = new BasicFilter(profile);
filter.fromJSON(options.filters[0]);

contains
contains(value: any): boolean
是否包含在过滤器中的值
if (this.filter.contains(val)) {
     this.filter.remove(val);
} else {
     this.filter.add(val);
}

remove/add
remove(val: any)
add(val: any)

添加或移除值

if (this.filter.contains(val)) {
     this.filter.remove(val);
} else {
     this.filter.add(val);
}

isEmpty
isEmpty(): boolean



TupleFilter

它类似于BasicFilter,但它可以在多维度时生效.相当于SQL:

SELECT * FROM DataTable WHERE ( Team = "DTD1" AND Prod = "Wyn" ) OR ( Team = "DTD2" AND Prod = "SpreadJS" );
方法interfacedescription示例
constructor
type ITuple = { value: any }[];
interface ITupleFilter {
    target: IFilterTarget[];
    filterType: VisualFilterType;
    operator: BasicFilterOperator;
    values: ITuple[];
}


constructor(targetProfiles: RuntimeNS.IFieldProfile[], operator: BasicFilterOperator = BasicFilterOperator.In, values: BasicNS.ITuple[] = [])

const dimensionsProfiles = dv.plain.profile.dimensions.values;
const filter = new TupleFilter(dimensionsProfiles);
setOperator/getOperator
setOperator(operator: BasicFilterOperator)


getOperator(): BasicFilterOperator


getValues/setValues
getValues(): BasicNS.ITuple[]


setValues(vals: BasicNS.ITuple[])


toJSON/fromJSON
toJSON(): BasicNS.ITupleFilter 


fromJSON(obj: BasicNS.ITupleFilter) 


 contains
contains(tuple: BasicNS.ITuple): boolean


remove/add
remove(tuple: BasicNS.ITuple)


add(tuple: BasicNS.ITuple)


createTuple
createTuple(dp: RuntimeNS.IDataPoint, depth?: number): BasicNS.ITuple
按选定的数据点创建元组
this.items = dv.plain.data.reduce((res, dp) => {
        const label = Object.keys(dp).map(key => dp[key]).join('-');
        const tuple = filter.createTuple(dp);
        res.push({
          label,
          className: filter.contains(tuple) ? 'selected' : '',
          tuple: JSON.stringify(tuple),
        });


        return res;
      }, []);
isEmpty
isEmpty(): boolean



AdvancesFilter

高级过滤器提供了lessThan、greatThan和其他运算符来查询,目前不支持逻辑运算符 "Or"。


方法interfacedescription示例
constructor
enum AdvancedFilterLogicalOperator {
  And = 'And',
  Or = 'Or',
}
enum AdvancedFilterOperator {
  LessThan = 'LessThan',
  LessThanOrEqual = 'LessThanOrEqual',
  GreaterThan = 'GreaterThan',
  GreaterThanOrEqual = 'GreaterThanOrEqual',
}
interface IAdvancedFilterCondition {
    value: any;
    operator: AdvancedFilterOperator;
}
interface IAdvancedFilter extends IFilterBase {
    conditions: IAdvancedFilterCondition[];
    logicalOperator: AdvancedFilterLogicalOperator;
  }


constructor(targetProfile: RuntimeNS.IFieldProfile, logicalOperator: AdvancedFilterLogicalOperator = AdvancedFilterLogicalOperator.And, conditions: BasicNS.IAdvancedFilterCondition[] = [])


setLogicalOperator/getLogicalOperator
setLogicalOperator(operator: AdvancedFilterLogicalOperator)


getLogicalOperator(): AdvancedFilterLogicalOperator


setConditions/getConditions
setConditions(conditions: BasicNS.IAdvancedFilterCondition[])


getConditions(): BasicNS.IAdvancedFilterCondition[]

this.filter.setConditions([{
      value: min,
      operator: this.includeMinEle.checked ? Enums.AdvancedFilterOperator.GreaterThanOrEqual : Enums.AdvancedFilterOperator.GreaterThan,
    }, {
      value: max,
      operator: this.includeMaxEle.checked ? Enums.AdvancedFilterOperator.LessThanOrEqual : Enums.AdvancedFilterOperator.LessThan,
    }]);
toJSON/fromJSON
toJSON(): BasicNS.IAdvancedFilter


fromJSON(obj: BasicNS.IAdvancedFilter)


remove/add
remove(condtion: BasicNS.IAdvancedFilterCondition)


add(condtion: BasicNS.IAdvancedFilterCondition)


isEmpty
isEmpty(): boolean



  • 无标签