You need to enable JavaScript to run this app.
导航
自定义图表数据结构与插件属性配置
最近更新时间:2024.04.19 10:46:38首次发布时间:2024.03.26 16:16:45

1.概述

图表数据结构和插件属性配置是实现自定义可视化的核心环节之一,本文旨在为您介绍关于图表插件开发中的数据结构设计和属性配置的相信内容。您可以通过本文深入了解到插件开发中关键字段的详细说明,以及如何为您的图表插件设定属性,从而确保您的自定义可视化插件不仅功能完备,而且能够满足特定的业务需求和用户偏好。(目前,自定义可视化功能还在内测阶段,若您对该功能有具体需求,欢迎联系火山引擎商务团队,以获取更多购买和合作的详细信息)。
注意:在您阅读并完成本文的配置项前,需要先依次完成《自定义可视化概述》《自定义图表插件结构与开发》中的相关配置。

2.图表数据结构

本产品内置图表和自定义图表遵循通用的图表数据结构 VizData 进行渲染。
在开发自定义图表时,您需要关注 VizData 中的相关字段。

type FieldId = string
interface VizData {  
    // 字段位置信息  
    locationMap: Record<string, FieldId[]>;  
    // 字段信息  
    fieldMap: Record<FieldId, FieldInfo>;  
    // 图表数据  
    datasets: Record<FieldId, string>[];
}

2.1 字段说明

(1)locationMap
字段位置信息,记录每一个自定义字段的 FieldId 数组
(2)fieldMap
描述 FieldId 对应的完整字段信息,FieldInfo 类型如下:

interface FieldInfo {  
    // 字段别名(名称)  
    alias: string;  
    // 字段类型  
    type: string;  
    // 字段值域  
    domain: string[];
}

注意:排序功能后,fieldInfo 中的 domain 字段将同时包含字段的值域和排序结果。需要参考 domain 字段的值顺序实现图表排序。
(3)datasets
图表查询结果数组。数组中的单个对象表示查询结果中的一行,对象的 key 为字段id,对象的 value 为字段值。

2.2 示例

以示例插件包为例:Pie Chart图表是一个基于echarts绘制的饼图,有1个维度字段,1个指标字段。
以下为示例插件包注册自定义字段的相关代码:

export const activate = (context) => {  
    context.vizQueryChartRenderer.register({  
        fields: [
          {
            label: '维度',
            location: 'dimensions',
            fieldType: 0,
          },
          {
            label: '指标',
            location: 'measures',
            fieldType: 1,
          }
        ],
    })
}

2.2.1 获取字段id

您可以基于 VizDatalocationMap 获取 FieldId
首先通过 locationMap[location] 获取 location 上的 FieldId 数组,然后使用对应的索引获取字段的 FieldId
Pie Chart图表有 'dimensions', 'measures' 2个 location,每个 location 有且只有1个字段。
如下示例获取Pie Chart图表所有自定义字段的 FieldId

const { locationMap } = vizData
const xId = locationMap['dimensions'][0]
const yId = locationMap['measures'][0]

2.2.2 获取字段详细信息

您可以基于 VizDatafieldMap 获取 FieldInfo,即字段详细信息。
如下示例获取了Pie Chart图表['dimensions'][0]对应字段的详细信息:

const { locationMap, fieldMap } = vizData
const xId = locationMap['dimensions'][0]
const xFieldInfo = fieldMap[xId]
const { alias, type, domain } = xFieldInfo

2.2.3 将查询结果转换为合适的数据结构

VizDatadatasets 表示单次查询的查询结果,类型为 Record<FieldId, string>[]。如果您希望将Pie Chart图表的 datasets 转化为 { x: string, y: string }[] 的格式,可以参考以下代码:

const { locationMap, datasets } = vizData
const xId = locationMap['dimensions'][0]
const yId = locationMap['measures'][0]
const data: { x: string, y: string, z: string }[] = datasets.map(item => ({
    x: item[xId],
    y: item[yId],
}))

本产品的可视化查询模块为了保留精度将所有 datasets 数据返回为 string 格式。
如果需要聚合过后的数值字段,您可以直接将其转化为 number 类型:

const data: { x: string, y: string, z: number }[] = datasets.map(item => ({
    x: item[xId],
    y: +item[yId],
}))

3.图表属性配置

3.1 自定义属性配置

自定义图表默认无图表属性配置。在注册自定义图表时,您可以通过 settings 字段定义属性配置表单。
如下的 register 方法为自定义图表注册了两个 SettingGroup,第一个包括一个 Input 组件和一个 Select 组件,第二个包括一个 CheckboxGroup 组件。

import { ContributeContext } from '@aeolus/extension-context'
import Localize, {
  localize
} from '@aeolus-developers/localize'
 
export enum FieldMap {
  Dimension = 'dimensions',
  Measure = 'measures',
}
 
export const activate = (context: ContributeContext) => {
  Localize.init(context.language)
  context.vizQueryChartRenderer.register({
    id: '@aeolus-developers/chartjs-line',
    fields: [
      {
        label: localize("settings.info.dimension"),
        location: FieldMap.Dimension,
        fieldType: 0,
      },
      {
        label: localize("settings.info.measure"),
        location: FieldMap.Measure,
        fieldType: 1,
      }
    ],
    constraints: [
      {
        [FieldMap.Dimension]: [1, 1],
        [FieldMap.Measure]: [1, 1],
      }
    ]
  })
}

3.2 界面说明

一个图表的配置包含多个 SettingGroup,一个 SettingGroup 包含多个 SettingItem

  • 每个 SettingGroup 在 UI 上包括它的 labelswitch, 其中 switch 用于控制整个 SettingGroup 是否生效。
  • 每个 SettingItem 在 UI 上包括它的 labelcomponent,其中 component 用于描述需要使用的自定义图表配置组件。

3.3 类型说明

接下来描述 context.vizQueryChartRenderer.registersettings 的类型参数。

interface VizQueryChartRendererRegisterParameters {
  /**
   * ensure it is unique for all chart renderers,
   * `extension-package-name:chart-name` is recommended
   * for example `@aeolus-developers/extension-3d-bar-chart`
   * */
  id: string;
  /**
   * custom fields
   * */
  fields: FieldGroup[];
  /**
   * fields constraints
   * */
  constraints?: Constraint[];
  /**
   * custom chart settings
   */
  settings?: SettingGroup[];
}

(1)SettingGroup
自定义图表配置的配置组

Property

Type

Description

key

string

在图表插件中通过[SettingGroup.key].[SettingItem.key]访问自定义配置值

label

string

配置组名称

switch

boolean

是否显示配置组开关,默认不显示

items

SettingItem[]

配置组包含的自定义配置项

(2)SettingItem
自定义配置项

Property

Type

Description

key

string

在图表插件中通过[SettingGroup.key][SettingItem.key]访问自定义配置值

label

string

配置项名称

component

SettingComponentType

配置项组件

(3)SettingComponentType
配置项组件,当前datawind提供的组件类别如以下所示:

type SettingComponentType =
  | CheckboxGroupType
  | InputType
  | InputNumberType
  | SelectType
  | SwitchType
  | RadioGroupType
  | ColorPickerType;

每一个 SettingItem 均需要描述 component 属性,对应的类型可以为以下组件类型的其中之一。
(4)CheckboxGroupType

interface CheckboxGroupType {
  type: "CheckboxGroup";
  props: Partial<{
    defaultValue: string[] | number[];
    options: {
      label: string;
      value: string | number;
    }[];
    direction: "vertical" | "horizontal";
  }>;
}

(5)InputType

interface InputType {
  type: "Input";
  props: Partial<{
    defaultValue: string;
  }>;
}

(6)InputNumberType

interface InputNumberType {
  type: "InputNumber";
  props: Partial<{
    defaultValue: number;
    min: number;
    max: number;
    step: number;
  }>;
}

(7)SelectType

interface SelectType {
  type: "Select";
  props: Partial<{
    mode: "default" | "multiple";
    defaultValue: string | string[] | number | number[];
    options: {
      label: string;
      value: string;
    }[];
  }>;
}

(8)SwitchType

interface SwitchType {
  type: "Switch";
  props: Partial<{
    defaultValue: boolean;
    checkedText: string;
    uncheckedText: string;
  }>;
}

(9)RadioGroupType

interface RadioGroupType {
  type: "RadioGroup";
  props: Partial<{
    type: "radio" | "button";
    defaultValue: string | number;
    options: {
      label: string;
      value: string | number;
    }[];
    direction: "vertical" | "horizontal";
  }>;
}

(10)ColorPickerType

interface ColorPickerType {
  type: "ColorPicker";
  props: Partial<{
    type: "default" | "linear" | "theme";
    defaultValue: string[];
  }>;
}

3.4 示例

3.4.1 插件注册

import { ContributeContext } from "@aeolus/extension-context";
 
export const activate = (context: ContributeContext) => {
  context.vizQueryChartRenderer.register({
    id: "@aeolus-developers/visual-data-explorer",
    extend: "table",
    settings: [
      {
        key: "checkboxGroup",
        label: "CheckboxGroup",
        switch: true,
        items: [
          {
            key: "base",
            label: "Base Component",
            component: {
              type: "CheckboxGroup",
              props: {
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                ],
              },
            },
          },
          {
            key: "vertical",
            label: "Vertical Layout",
            component: {
              type: "CheckboxGroup",
              props: {
                direction: "vertical",
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                ],
              },
            },
          },
          {
            key: "default",
            label: "Default Value",
            component: {
              type: "CheckboxGroup",
              props: {
                defaultValue: ["a", "b"],
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                ],
              },
            },
          },
        ],
      },
      {
        key: "input",
        label: "Input",
        switch: true,
        items: [
          {
            key: "base",
            label: "Base Component",
            component: {
              type: "Input",
              props: {},
            },
          },
        ],
      },
      {
        key: "inputNumber",
        label: "InputNumber",
        switch: true,
        items: [
          {
            key: "base",
            label: "Base Component",
            component: {
              type: "InputNumber",
              props: {},
            },
          },
          {
            key: "decimal",
            label: "Using min, max and step",
            component: {
              type: "InputNumber",
              props: {
                min: 0,
                max: 10,
                step: 0.5,
              },
            },
          },
        ],
      },
      {
        key: "select",
        label: "Select",
        switch: true,
        items: [
          {
            key: "base",
            label: "Base Component",
            component: {
              type: "Select",
              props: {
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                  { label: "D", value: "d" },
                ],
              },
            },
          },
          {
            key: "multiple",
            label: "Multiple",
            component: {
              type: "Select",
              props: {
                mode: "multiple",
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                  { label: "D", value: "d" },
                ],
              },
            },
          },
        ],
      },
      {
        key: "switch",
        label: "Switch",
        switch: true,
        items: [
          {
            key: "base",
            label: "Base Component",
            component: {
              type: "Switch",
              props: {},
            },
          },
        ],
      },
      {
        key: "radioGroup",
        label: "RadioGroup",
        switch: true,
        items: [
          {
            key: "base",
            label: "Base Component",
            component: {
              type: "RadioGroup",
              props: {
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                  { label: "D", value: "d" },
                ],
              },
            },
          },
          {
            key: "button",
            label: "Button Mode",
            component: {
              type: "RadioGroup",
              props: {
                type: "button",
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                  { label: "D", value: "d" },
                ],
              },
            },
          },
          {
            key: "vertical",
            label: "Vertical Layout",
            component: {
              type: "RadioGroup",
              props: {
                direction: "vertical",
                options: [
                  { label: "A", value: "a" },
                  { label: "B", value: "b" },
                  { label: "C", value: "c" },
                  { label: "D", value: "d" },
                ],
              },
            },
          },
        ],
      },
    ],
  });
};

3.4.2 图表渲染

在插件中,您可以通过 window.onmessage 监听插件图表 propertiesChange 事件,通过 e.data.data.settings 获取自定义配置值。

window.onmessage = (
  e: MessageEvent<{
    type: string;
    data: any;
  }>
) => {
  const { type, data } = e.data;
 
  if (type === "propertiesChange") {
    // outputs { vizData, settings }
    // using settings[settingGroup.key]?.[settingItem.key] to visit a value
    console.log(data);
  }
};