合并两棵树,key同则使用旧值覆盖新值,不同则为增删

238 阅读15分钟

组件结构

  • 每个组件都包含以下几个文件夹
    image.png
  • index.js中编写对应的交互逻辑
  • config.js负责描述组件的相关信息,比如:版本信息,组件类型等等,此处我们只需要关注其中的config对象(即右侧配置信息)即可, 由于具体的config对象太过冗长,所以放至文末。

config.js中内容

// config.js

const ComponentDefaultConfig = {
  id: "", //组件ID
  name: "玫瑰图",
  dashboardId: "",
  moduleType: "chart",
  moduleName: "nightingale",
  moduleVersion: "1.2.6",
  ......
  config: [...],   // ******组件更新主要比对的就是这个config******
  ......
  drillDownArr: [],
  triggers: [],
};

export default ComponentDefaultConfig;

组件升级

注:由于不可能将所有的配置项一股脑全放在右边,所以我们平台内部为右侧封装了选项卡、折叠组件、输入框、拖拽条等等组件来对繁多的配置进行归类和细分,见图1中红框部分。

注:由于我们内部使用了选项卡、折叠面板等组件,这会使得组件的config层级很深,理论上可以是无限层级

最终右侧渲染出的组件完全是由组件config中的信息来决定的。

比如,组件config中有如下配置: image.png 经过内部处理后,那么最终将在右侧渲染出如下组件 image.png


什么是组件升级?

image.png 图1

以上图1中version: 1.2.6的玫瑰图来 举个🌰, 当前玫瑰图一共有 “标题设置”、“环形属性”、“标签设置”、“图例设置”、“辅助”五个大的分类配置项,而这些分类下又有许多更细粒度的配置项。
如果我现在想更改已有配置中的某些默认值,或者我需要新增or删除某个配置项,那么我就需要更改玫瑰图组件右侧相应的配置信息(即config对象)。

此时需要考虑一种情况:假设我们升级后的玫瑰图版本为1.2.7, 在将1.2.6版本组件升级为1.2.7的过程中,原来用户配置好的信息我们是需要保留的,比如组件的位置、大小等信息,否则用户花费了时间配置好的组件一点升级后所有的配置信息就都变成1.2.7版本的默认值了,那不就芭比Q了吗😂。

所以组件升级要做的事就很明确了:比对新旧config

oldConfig === 旧config        newConfig === 新config
合并后的最终config === finalConfig

  1. 新增 配置项:新config中含有 { name: 'fontSizeA', value: 'fontSizeA' }, 而oldConfig中没有这个新配置项,那么最后的finalConfig中含有{ name: 'fontSizeA', value: 'fontSizeA' }。
  2. 删除配置项:新config中不包含有 { name: 'fontSizeB', value: 'fontSizeB' }, 而oldConfig中含有{ name: 'fontSizeB', value: 'fontSizeB' },,那么最终的finalConfig中不应该含有 { name: 'fontSizeB', value: 'fontSizeB' 。
  3. 合并配置项:新config中含有 { name: 'fontSizeC', value: 24 },旧config中含有{ name: 'fontSizeC', value: 48 },那么此时最终的finalConfig中应该包含的是 { name: 'fontSizeC', value: 48 }

可以看到,第三种情况相比之下更加复杂,但一言以蔽之就是: 取newConfig中的key,oldConfig中的value

分析

举个🌰:如果要将 环形属性 > 环形系列 > 系列1 > 颜色 的值从 #d11212 改为 天青色#87ceeb,那么就需要更改到下图中标注的那个value。

下图是玫瑰图组件的config

image.png

观察config数据结构后,可以总结出以下几点:

  1. 可以将此数据结构抽象成一棵 , 树的子节点全部存放在options或者value中,name是每个节点唯一的key。其它的属性暂且可先不管。
  2. 每个配置项都是一个{ },这个{ }中至少有一个key是options或者value, options是平台封装的组件所附加的层级,比如选项卡的选项内容就保存options数组,而value才是最终配置项的值
  3. value的值只有两种情况, [] 或者 (string | number),当然,最后一级的value必定是(string | number)

编码思路

  1. 我第一时间的想法是:直接遍历oldConfig,然后每遍历一层时便通过深度优先去newConfig中寻找对应的key,结果早已注定————爆栈😂。(这个思路确实挺low的)

  2. 重整旗鼓之后,我决定试一试将两棵树拍平成两个数组,然后再在对这两个数组循环遍历时通过id来一一比对然后合并,但是想了想发现算法的复杂度太高了,首先拍平两棵理论上不限层级的树就非常的耗时,更别说比对处理完两个数组后还有再将最终得到的数组再拼回成树了。而且两个size很大的数组比对的过程的时间复杂度也不会低,在最坏的情况下会达到O(n^2),何况这个n肯定不会小……

  3. 最后,我就想起了曾经看过的vue2中的diff算法,vue2 diff算法中比对新老节点时就有一种这样的情况,只不过vue2中的更复杂一点。 最终,对传入的oldConfig和newConfig进行逐层遍历,先遍历oldConfig,后遍历newConfig。在遍历每一层的oldConfig的时候,将对应的oldConfig中的key值和value值保存在对应的Map中,然后在遍历单层newConfig的时候通过newConfig中的key去对应的Map中查询是否有值存在,存在则取出Map中的值(old Value)并赋值给newConfig中的key,不存在则不做任何操作,这样子的话newConfig中的key的值就保持不变。(这个方法改变的是引用,所以需要根据实际需求来考虑是否将入参newConfig深拷贝一层后再传进来)

此外,这里面用到了我最近学的TS知识,包括泛型,工具类的使用以及利用never来收敛类型的技巧。对此阶段的我来讲,解决了这个问题确实让我兴奋,虽然以后看这段代码可能会觉得“破绽百出”😂,但是这种开心的感觉其实和很多东西一样,何必天长地久,只要曾经拥有 即可!!!

//  mergeSameAndAddDiff.ts

/* eslint-disable no-case-declarations */
/* eslint-disable @typescript-eslint/no-unused-vars */

/******** @ToTop newConfig/oldConfig中 <同层级> 的 <任意一项> 中的name不可以重复 **********/

type TIndexSignature = {
  [k: string]: string | number;
};

type TConfigItem = {
  name?: string;
  displayName?: string;
  type?: string;
  config?: TIndexSignature;
  value?: any;
  options?: any;
  range?: any;
  hasSwitch?: boolean;
  defaultExpand?: boolean;
  showDetail?: boolean;
  activeKey?: string | number;
  direction?: string;
  language?: string;
  showExpand?: string; // 是否展示下面的全屏按钮
  readOnly?: boolean; // 是否可编辑
  disabled?: boolean; //<自定义列中属性> 如果改项配置为true,则后面的添加和删除不可用
  keepSameSeriesCountsOfOldConfig?: boolean; // 标记某些组件中的<系列数量或者列的数量>在组件升级后不可被更改。(比如:原来有3条折线,新版本折线图组件默认配置只有2条,那么组件升级后仍需要展示3条折线)
};

//@Mark 创建至少含有一个给定 KeysType中 的类型,并保持其余的Keys
type RequireAtLeastKey<
  ObjectType,
  KeysType extends keyof ObjectType = keyof ObjectType
> = KeysType extends keyof ObjectType ? ObjectType & Required<Pick<ObjectType, KeysType>> : never;
// 每个configItem中必须至少包含有 value 或者 options 中的一个作为key
type THasTargetKeyConfigItem = RequireAtLeastKey<TConfigItem, "value" | "options">;
type TConfigArr = THasTargetKeyConfigItem[];

// 目前 粒度更细(多层级)的配置项 只能放在 value数组或者options数组中, 简单配置(一层对象)直接将value归为“other类型”,
//@Mark 以后随着组件多样性以及丰富性可能扩展出其它key名 用来实现<value|options>一样的功能,那么相应的 mergeSameAndAddDiff 中的逻辑也需要进行对应的扩展, TMayChangeFlag 用于枚举出这些key,并确保coder 会在mergeSameAndAddDiff中加上<新key>对应的处理逻辑
type TMayChangeFlag = "special" | "value" | "options" | "other";

//@Must Notice!!!  mergeSameAndAddDiff方法会改变入参引用,请根据具体需要决定传入的入参是否需要先深拷贝一次
const mergeSameAndAddDiff = (oldConfig: TConfigArr, newConfig: TConfigArr) => {
  // console.log("旧的config", oldConfig);
  // console.log("新的config~~~~~~~~", newConfig);
  const recursiveFn = (oldConfig: TConfigArr, newConfig: TConfigArr) => {
    const valueMap = new Map();
    const otherMap = new Map(); // value 为 (string、number) || {}
    const optionMap = new Map();
    // 处理  表格自定义列 这类特殊的配置项
    const specialMap = new Map();
    oldConfig.forEach((x: TConfigItem) => {
      const { name, value, options } = x;
      // 表格自定义列
      if (name === "customColumn") {
        specialMap.set(name, value);
      } else if (Array.isArray(value)) {
        //@Mark 开发者配置组件时,应该保证同一层级(value || options)下 每项配置名(name)的唯一性,如果不小心copy多了一份,在map中重复set相同的key也仅仅只是覆盖,所以此处不判断map中是否已经有key为 name 的项
        valueMap.set(name, value);
      } else {
        otherMap.set(name, value);
      }
      if (Array.isArray(options)) {
        optionMap.set(name, options);
      }
    });
    newConfig.forEach((item: TConfigItem) => {
      // keepSameSeriesCountsOfOldConfig 主要用于标记下述特殊情况:
      // 比如折线图或者饼图中一个系列代表着一根折线或者一个区块,以折线图为例,折线图默认配置了两条折线,但是用户在1.1版本配置了3条折线,然后某天在将1.1 升级成1.2后,会发现原来的3条折线变成了2条(或者1条折线变2条)。这时需要开发者在对应组件可能存在这种变数的时候在组件config对应的位置加上keepSameSeriesCountsOfOldConfig标记,以确保组件升级功能正常运行。
      const { name, value, options, keepSameSeriesCountsOfOldConfig } = item;

      // @Mark 选择器类型组件的配置信息中同时包含 options 和 value 两个选项,当这两项都存在的时候,直接走type为value时的逻辑
      let type: TMayChangeFlag = "other";
      // 因为某些原因,升级组件时 表格中自定义列的config中没有加keepSameSeriesCountsOfOldConfig
      if (name === "customColumn" || keepSameSeriesCountsOfOldConfig === true) {
        type = "special";
      } else if (Array.isArray(value)) {
        type = "value";
      } else if (Array.isArray(options)) {
        type = value ? "other" : "options";
      }

      switch (type) {
        case "special":
          if (specialMap.has(name)) {
            if (name === "customColumn" || keepSameSeriesCountsOfOldConfig === true) {
              const newTableValue = item.value;
              const newTableValueLength = newTableValue.length;
              const oldTableValue = specialMap.get(name);
              const oldTableValueLength = oldTableValue.length;

              // 以升级表格时 自定义列数举例:
              // 如果 新版本表格自定义列数 多于  旧版本表格自定义列数
              if (newTableValueLength >= oldTableValueLength) {
                // 将旧自定义列config 以新config中自定义列的列数(newTableValueLength)分为两段,
                const oldTableNeedUpdateValue = oldTableValue.slice(0, newTableValueLength);
                const oldTableRestValue = oldTableValue.slice(newTableValueLength);
                const hadMergeTableValue = mergeSameAndAddDiff(
                  oldTableNeedUpdateValue,
                  newTableValue
                );
                item.value = hadMergeTableValue.concat(oldTableRestValue);
              } else {
                // 如果 新版本表格自定义列数 少于 旧版本表格自定义列数
                // 这种情况下,我们只需要从新config中截取和旧config中相当的列数配置
                const targetNewTableValue = newTableValue.slice(0, oldTableValueLength);
                const hadMergeTableValue = mergeSameAndAddDiff(oldTableValue, targetNewTableValue);
                item.value = hadMergeTableValue;
              }
            }
            // TODO 留待其它特殊的组件
            // else{}
          }
          // specialMap.clear();
          break;
        case "value":
          if (valueMap.has(name)) {
            const oldValue = valueMap.get(name);
            recursiveFn(oldValue, item.value);
          }
          break;
        case "options":
          if (optionMap.has(name)) {
            const oldOptions = optionMap.get(name);
            recursiveFn(oldOptions, item.options);
          }
          break;
        case "other":
          // @Mark 因为有些组件的某些选项中会有value数组中不是对象的情况(value用来记录值而不是记录组件配置项信息)比如:常规表格组件中“排序方式”项中的value:['descend'],那么最终递归到这个value时,item为字符串'descend',此时使用一般的逻辑item.value = xxx 就会报错,所以此处针对item类型加一个判断
          if (typeof item === "object") {
            //<1> 如果旧config中有此配置项,让其覆盖新config中的此项
            //<2> 但也有可能此项对于旧config来讲是新增的,那么map中必然查无此项,所以直接使用新config中的配置
            if (otherMap.has(name)) {
              const oldConstant = otherMap.get(name);
              item.value = oldConstant;
            }
          }
          break;
        default:
          // 变量type 在此处收敛为nerve类型
          const aToolConstant: never = type;
          break;
      }
    });
  };
  recursiveFn(oldConfig, newConfig);
  return newConfig;
};

export { mergeSameAndAddDiff };

config.js中具体的config值

// config.js  中的config项
{
      displayName: "位置尺寸",
      name: "dimension",
      type: "dimensionGroup",
      config: {
        lock: false,
      },
      value: [
        {
          displayName: "X轴坐标",
          name: "left",
          value: 231,
        },
        {
          displayName: "Y轴坐标",
          name: "top",
          value: 232,
        },
        {
          displayName: "宽度",
          name: "width",
          value: 800,
        },
        {
          displayName: "高度",
          name: "height",
          value: 500,
        },
      ],
    },
    {
      displayName: "默认隐藏",
      name: "hideDefault",
      type: "checkBox",
      value: false,
    },
    {
      displayName: "玫瑰图设置",
      name: "pieSetting",
      options: [
        {
          name: "标题设置",
          value: [
            {
              hasSwitch: true,
              defaultExpand: true,
              displayName: "主标题设置",
              name: "mainTextSetting",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "showMainTextSetting",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "水平位置",
                  name: "mainTextHorizontal",
                  type: "range",
                  value: 50,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
                {
                  displayName: "垂直位置",
                  name: "mainTextVertical",
                  type: "range",
                  value: 47,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
                {
                  displayName: "文本样式",
                  name: "mainTextStyle",
                  type: "textFullStyleGroup",
                  value: [
                    {
                      displayName: "",
                      name: "fontFamily",
                      value: "Microsoft Yahei",
                    },
                    {
                      displayName: "",
                      name: "fontSize",
                      value: 32,
                    },
                    {
                      displayName: "",
                      name: "textColor",
                      type: "color",
                      value: "#3ad4e8",
                    },
                    {
                      displayName: "",
                      name: "bold",
                      value: false,
                    },
                    {
                      displayName: "",
                      name: "italic",
                      value: false,
                    },
                    {
                      displayName: "字距",
                      name: "letterSpacing",
                      value: 0,
                    },
                    {
                      displayName: "行距",
                      name: "lineHeight",
                      value: 0,
                    },
                  ],
                },
              ],
            },
            {
              hasSwitch: true,
              defaultExpand: true,
              displayName: "副标题设置",
              name: "subTextSetting",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "showSubTextSetting",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "标题文本",
                  name: "subText",
                  type: "input",
                  value: "总量",
                },
                {
                  displayName: "水平位置",
                  name: "subTextHorizontal",
                  type: "range",
                  value: 50,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
                {
                  displayName: "垂直位置",
                  name: "subTextVertical",
                  type: "range",
                  value: 36,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
                {
                  displayName: "文本样式",
                  name: "subTextStyle",
                  type: "textFullStyleGroup",
                  value: [
                    {
                      displayName: "",
                      name: "fontFamily",
                      value: "Microsoft Yahei",
                    },
                    {
                      displayName: "",
                      name: "fontSize",
                      value: 32,
                    },
                    {
                      displayName: "",
                      name: "textColor",
                      type: "color",
                      value: "#fff",
                    },
                    {
                      displayName: "",
                      name: "bold",
                      value: true,
                    },
                    {
                      displayName: "",
                      name: "italic",
                      value: false,
                    },
                    {
                      displayName: "字距",
                      name: "letterSpacing",
                      value: 0,
                    },
                    {
                      displayName: "行距",
                      name: "lineHeight",
                      value: "48px",
                    },
                  ],
                },
              ],
            },
          ],
          key: "1",
        },
        {
          name: "环形属性",
          value: [
            {
              hasSwitch: false,
              defaultExpand: true,
              displayName: "环形位置",
              name: "ringPosition",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "showRingPosition",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "水平位置",
                  name: "ringPositionX",
                  type: "range",
                  value: 50,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
                {
                  displayName: "垂直位置",
                  name: "ringPositionY",
                  type: "range",
                  value: 40,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
              ],
            },
            {
              hasSwitch: false,
              defaultExpand: false,
              displayName: "环形半径",
              name: "Radius",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "show",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "外层半径",
                  name: "outerRadius",
                  type: "number",
                  value: 50,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
                {
                  displayName: "内层半径",
                  name: "innerRadius",
                  type: "number",
                  value: 40,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "%",
                  },
                },
              ],
            },
            {
              hasSwitch: true,
              defaultExpand: false,
              displayName: "区块样式",
              name: "blockStyle",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "showBlockStyle",
                  type: "switch",
                  value: false,
                },
                {
                  displayName: "圆角弧度",
                  name: "blockRadius",
                  type: "range",
                  value: 0,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "px",
                  },
                },
                {
                  displayName: "阴影颜色",
                  name: "blockStrokeColor",
                  type: "color",
                  value: "#5ab9b9",
                },
                {
                  displayName: "模糊半径",
                  name: "blurRadius",
                  type: "range",
                  value: 15,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "px",
                  },
                },
                {
                  displayName: "区块间距",
                  name: "blockGap",
                  type: "number",
                  value: 2,
                  config: {
                    step: 1,
                    suffix: "",
                  },
                },
              ],
            },
            {
              displayName: "环形系列",
              name: "ringSeries",
              type: "tabArray",
              defaultActiveKey: "1",
              config: {
                template: [
                  {
                    flag: "specialItem",
                    displayName: "系列1",
                    name: "series1",
                    type: "object",
                    value: [
                      {
                        displayName: "映射",
                        name: "mapping",
                        type: "input2",
                        value: [
                          {
                            displayName: "字段名",
                            name: "fieldName",
                            type: "input",
                            value: "系列一",
                          },
                          {
                            displayName: "显示名",
                            name: "displayName",
                            disabled: true,
                            type: "input",
                            value: "",
                          },
                        ],
                      },
                      {
                        displayName: "颜色",
                        name: "themePureColor",
                        type: "color",
                        value: "#d11212",
                      },
                    ],
                    key: "1",
                  },
                ],
              },
              value: [
                {
                  flag: "specialItem",
                  displayName: "系列1",
                  name: "series1",
                  type: "object",
                  value: [
                    {
                      displayName: "映射",
                      name: "mapping",
                      type: "input2",
                      value: [
                        {
                          displayName: "字段名",
                          name: "fieldName",
                          type: "input",
                          value: "系列一",
                        },
                        {
                          displayName: "显示名",
                          name: "displayName",
                          disabled: true,
                          type: "input",
                          value: "",
                        },
                      ],
                    },
                    {
                      displayName: "颜色",
                      name: "themePureColor",
                      type: "color",
                      value: "#d11212",
                    },
                  ],
                  key: "1",
                },
                {
                  flag: "specialItem",
                  displayName: "系列2",
                  name: "series2",
                  type: "object",
                  value: [
                    {
                      displayName: "映射",
                      name: "mapping",
                      type: "input2",
                      value: [
                        {
                          displayName: "字段名",
                          name: "fieldName",
                          type: "input",
                          value: "系列二",
                        },
                        {
                          displayName: "显示名",
                          name: "displayName",
                          disabled: true,
                          type: "input",
                          value: "",
                        },
                      ],
                    },
                    {
                      displayName: "颜色",
                      name: "themePureColor",
                      type: "color",
                      value: "#db9006",
                    },
                  ],
                  key: "2",
                },
                {
                  flag: "specialItem",
                  displayName: "系列3",
                  name: "series3",
                  type: "object",
                  value: [
                    {
                      displayName: "映射",
                      name: "mapping",
                      type: "input2",
                      value: [
                        {
                          displayName: "字段名",
                          name: "fieldName",
                          type: "input",
                          value: "系列三",
                        },
                        {
                          displayName: "显示名",
                          name: "displayName",
                          disabled: true,
                          type: "input",
                          value: "",
                        },
                      ],
                    },
                    {
                      displayName: "颜色",
                      name: "themePureColor",
                      type: "color",
                      value: "#f0f35d",
                    },
                  ],
                  key: "3",
                },
                {
                  flag: "specialItem",
                  displayName: "系列4",
                  name: "series4",
                  type: "object",
                  value: [
                    {
                      displayName: "映射",
                      name: "mapping",
                      type: "input2",
                      value: [
                        {
                          displayName: "字段名",
                          name: "fieldName",
                          type: "input",
                          value: "系列四",
                        },
                        {
                          displayName: "显示名",
                          name: "displayName",
                          disabled: true,
                          type: "input",
                          value: "",
                        },
                      ],
                    },
                    {
                      displayName: "颜色",
                      name: "themePureColor",
                      type: "color",
                      value: "#15bc38",
                    },
                  ],
                  key: "4",
                },
                {
                  flag: "specialItem",
                  displayName: "系列5",
                  name: "series5",
                  type: "object",
                  value: [
                    {
                      displayName: "映射",
                      name: "mapping",
                      type: "input2",
                      value: [
                        {
                          displayName: "字段名",
                          name: "fieldName",
                          type: "input",
                          value: "系列五",
                        },
                        {
                          displayName: "显示名",
                          name: "displayName",
                          disabled: true,
                          type: "input",
                          value: "",
                        },
                      ],
                    },
                    {
                      displayName: "颜色",
                      name: "themePureColor",
                      type: "color",
                      value: "#87ceeb",
                    },
                  ],
                  key: "5",
                },
                {
                  flag: "specialItem",
                  displayName: "系列6",
                  name: "series6",
                  type: "object",
                  value: [
                    {
                      displayName: "映射",
                      name: "mapping",
                      type: "input2",
                      value: [
                        {
                          displayName: "字段名",
                          name: "fieldName",
                          type: "input",
                          value: "系列六",
                        },
                        {
                          displayName: "显示名",
                          name: "displayName",
                          disabled: true,
                          type: "input",
                          value: "",
                        },
                      ],
                    },
                    {
                      displayName: "颜色",
                      name: "themePureColor",
                      type: "color",
                      value: "#1814c3",
                    },
                  ],
                  key: "6",
                },
              ],
            },
          ],
          key: "2",
        },
        {
          name: "标签设置",
          value: [
            {
              hasSwitch: true,
              defaultExpand: true,
              displayName: "标签样式",
              name: "labelSetting",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "showLabel",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "line-width",
                  name: "lineWidth",
                  type: "number",
                  value: 2,
                  config: {
                    min: 0,
                    max: 100,
                    step: 1,
                    suffix: "px",
                  },
                },
                {
                  displayName: "line1-length",
                  name: "line1Length",
                  type: "number",
                  value: 20,
                  config: {
                    min: 0,
                    max: 500,
                    step: 1,
                    suffix: "px",
                  },
                },
                {
                  displayName: "line2-length",
                  name: "line2Length",
                  type: "number",
                  value: 10,
                  config: {
                    min: 0,
                    max: 500,
                    step: 1,
                    suffix: "px",
                  },
                },
              ],
            },
            {
              hasSwitch: false,
              defaultExpand: false,
              displayName: "标签字段",
              name: "labelShowFields",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "show",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "系列名",
                  name: "labelSeriesName",
                  type: "switch",
                  value: false,
                },
                {
                  displayName: "    ",
                  name: "labelSeriesNameTextStyle",
                  themeColor: "themeTextColor",
                  type: "chartText",
                  value: {
                    fontFamily: "微软雅黑",
                    color: "#fff",
                    fontSize: 14,
                    fontWeight: "normal",
                  },
                },
                {
                  displayName: "跟随系列",
                  name: "seriesNameUseSeriesColor",
                  type: "checkBox",
                  value: true,
                },
                {
                  displayName: "数据名",
                  name: "labelDataName",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "    ",
                  name: "labelDataNameTextStyle",
                  type: "chartText",
                  themeColor: "themeTextColor",
                  value: {
                    fontFamily: "微软雅黑",
                    color: "#fff",
                    fontSize: 14,
                    fontWeight: "normal",
                  },
                },
                {
                  displayName: "跟随系列",
                  name: "dataNameUseSeriesColor",
                  type: "checkBox",
                  value: true,
                },
                {
                  displayName: "数据值",
                  name: "labelDataValue",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "    ",
                  name: "labelDataValueTextStyle",
                  type: "chartText",
                  themeColor: "themeTextColor",
                  value: {
                    fontFamily: "微软雅黑",
                    color: "#fff",
                    fontSize: 14,
                    fontWeight: "normal",
                  },
                },
                {
                  displayName: "跟随系列",
                  name: "dataValueUseSeriesColor",
                  type: "checkBox",
                  value: true,
                },
                {
                  displayName: "百分比",
                  name: "labelPercentage",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "    ",
                  name: "labelPercentageTextStyle",
                  type: "chartText",
                  themeColor: "themeTextColor",
                  value: {
                    fontFamily: "微软雅黑",
                    color: "#fff",
                    fontSize: 14,
                    fontWeight: "normal",
                  },
                },
                {
                  displayName: "跟随系列",
                  name: "percentageUseSeriesColor",
                  type: "checkBox",
                  value: true,
                },
              ],
            },
          ],
          key: "3",
        },
        {
          name: "图例设置",
          value: [
            {
              hasSwitch: true,
              defaultExpand: true,
              displayName: "图例样式",
              name: "legendStyle",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "showLegend",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "偏移",
                  name: "legendOffset",
                  type: "inputNumber2",
                  value: [
                    {
                      displayName: "X",
                      name: "legendOffsetX",
                      type: "number",
                      value: 200,
                      config: {
                        min: -1000,
                        max: 1000,
                        suffix: "px",
                      },
                    },
                    {
                      displayName: "Y",
                      name: "legendOffsetY",
                      type: "number",
                      value: 400,
                      config: {
                        min: -1000,
                        max: 1000,
                        suffix: "px",
                      },
                    },
                  ],
                  showDetail: true,
                },
                {
                  displayName: "布局朝向",
                  name: "legendOrient",
                  options: [
                    {
                      name: "水平",
                      value: "horizontal",
                    },
                    {
                      name: "垂直",
                      value: "vertical",
                    },
                  ],
                  type: "select",
                  value: "horizontal",
                },
                {
                  displayName: "图例间距",
                  name: "legendItemGap",
                  type: "number",
                  value: 10,
                  config: {
                    min: -1000,
                    max: 1000,
                    step: 1,
                    suffix: "px",
                  },
                },
                {
                  displayName: "图形宽度",
                  name: "legendItemWidth",
                  type: "number",
                  value: 20,
                  config: {
                    min: 0,
                    max: 500,
                    step: 1,
                    suffix: "px",
                  },
                },
                {
                  displayName: "图形高度",
                  name: "legendItemHeight",
                  type: "number",
                  value: 15,
                  config: {
                    min: 0,
                    max: 500,
                    step: 1,
                    suffix: "px",
                  },
                },
              ],
            },
            {
              displayName: "文本样式",
              name: "legendTextStyle",
              type: "chartText",
              themeColor: "themeTextColor",
              value: {
                fontFamily: "微软雅黑",
                color: "#fff",
                fontSize: 12,
                fontWeight: "normal",
              },
            },
            {
              displayName: "标记图形",
              name: "symbolShape",
              options: [
                {
                  name: "emptyCircle",
                  value: "emptyCircle",
                },
                {
                  name: "circle",
                  value: "circle",
                },
                {
                  name: "rect",
                  value: "rect",
                },
                {
                  name: "roundRect",
                  value: "roundRect",
                },
                {
                  name: "triangle",
                  value: "triangle",
                },
                {
                  name: "diamond",
                  value: "diamond",
                },
                {
                  name: "pin",
                  value: "pin",
                },
                {
                  name: "arrow",
                  value: "arrow",
                },
                {
                  name: "none",
                  value: "none",
                },
              ],
              type: "select",
              value: "rect",
            },
            {
              displayName: "数量显示",
              name: "numberFormat",
              options: [
                {
                  name: "具体数量",
                  value: "specificNumber",
                },
                {
                  name: "百分比",
                  value: "percent",
                },
                {
                  name: "不作显示",
                  value: "noDisplay",
                },
              ],
              type: "select",
              value: "noDisplay",
            },
          ],
          key: "4",
        },
        {
          name: "辅助",
          value: [
            {
              hasSwitch: true,
              defaultExpand: true,
              displayName: "提示器",
              name: "pieTooltipSettings",
              type: "collapse",
              value: [
                {
                  displayName: "",
                  name: "tooltipShow",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "系列名",
                  name: "tooltipSeriesName",
                  type: "switch",
                  value: false,
                },
                {
                  displayName: "数据名",
                  name: "tooltipDataName",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "数据值",
                  name: "tooltipDataValue",
                  type: "switch",
                  value: true,
                },
                {
                  displayName: "数据值前缀",
                  name: "tooltipDataValueText",
                  type: "input",
                  value: "存储量:",
                },
                {
                  displayName: "百分比",
                  name: "tooltipPercentage",
                  type: "switch",
                  value: false,
                },
                {
                  displayName: "数据值前缀",
                  name: "tooltipPercentageText",
                  type: "input",
                  value: "",
                },
              ],
            },
          ],
          key: "5",
        },
      ],
      activeKey: "1",
      type: "tabs",
    },