export function checkExtremeData()

in src/common/checkFunctions.ts [84:470]


export function checkExtremeData(
  data: any,
  chartName: string,
  config: any,
  width: number,
  height: number,
  dataSize: number,
  force: any,
): {
  isExtreme: boolean;
  data?: any;
  config?: any;
} {
  if (!dataSize || dataSize === 0 || !width || !height) {
    return {
      isExtreme: false,
    };
  }

  // 柱图
  // 检测数据量过少,极坐标与横着的情况不处理,分面也不处理
  // 暂时单独写,待统一
  if (
    chartName === 'G2Bar' &&
    config?.polar !== true &&
    (config?.column === undefined || config?.column === true) &&
    !config?.facet
  ) {
    // 图表宽度,50是估算的padding
    const length = width - 50;

    // 根据stack、dodgeStack等配置项计算一组柱子的宽度,从而计算至少需要几组柱子
    const barWidth = 24;
    const types = Array.from(new Set(data?.map((item: any) => item?.type)))?.length ?? 0;
    const dodges = Array.from(new Set(data?.map((item: any) => item?.dodge)))?.length ?? 1;
    const groups = config?.dodgeStack ? dodges : config?.stack ? 1 : types;
    const minCount = Math.floor(length / (groups * barWidth + 80));

    if (dataSize < minCount) {
      if (force === true || force?.extreme === true) {
        warn('Bar', '当前数据量较少,推荐关闭force配置项开启极端数据自适应。问题码#08');
        return {
          isExtreme: true,
        };
      }
      // x轴类型
      const axisType = config?.xAxis?.type ?? 'cat';

      // 原数据中类型
      const dataTypes = Array.from(new Set(data.map((x: any) => x.type)));

      // 颜色
      let colors = config?.colors ?? themes.category_20;
      if (Array.isArray(colors) && colors.length < dataTypes.length) {
        if (
          !(
            colors.length === 12 &&
            themes.category_12.every((val, index) => val === colors?.[index])
          ) &&
          !(
            colors.length === 20 &&
            themes.category_20.every((val, index) => val === colors?.[index])
          )
        ) {
          colors = [...colors, ...themes.category_20.slice(colors.length, dataTypes.length)];
        }
        while (colors.length < dataTypes.length) {
          colors = [...colors, ...colors.slice(0, dataTypes.length - colors.length)];
        }
      }

      const newData = [...data];
      let newColors = colors;
      let xAxis = {};
      const { extreme } = force ?? {};
      let needColor = false;

      let alignLeft = false;
      let showPlaceholder = false;

      // 计算最后一个柱子的y值
      const xValues = Array.from(new Set(data.map((item: any) => item.x)));
      const lastX = xValues[xValues.length - 1];
      const dodges = Array.from(new Set(data.map((item: any) => item.dodge)));
      const lastDodge = dodges[dodges.length - 1];
      let lastY = 0;
      if (config.dodgeStack && lastDodge) {
        lastY = data
          .filter((item: any) => item.x === lastX && item.dodge === lastDodge)
          .map((item: any) => item.y || 0)
          .reduce((pre: number, cur: number) => pre + cur);
      } else if (config.stack) {
        lastY = data
          .filter((item: any) => item.x === lastX)
          .map((item: any) => item.y || 0)
          .reduce((pre: number, cur: number) => pre + cur);
      } else if (config.dodge && lastDodge) {
        const filteredData = data.filter(
          (item: any) => item.x === lastX && item.dodge === lastDodge,
        );
        lastY = filteredData[filteredData.length - 1].y;
      } else {
        const filteredData = data.filter((item: any) => item.x === lastX);
        lastY = filteredData[filteredData.length - 1].y;
      }

      // 分类数据
      if (axisType === 'cat') {
        // 分类数据默认隐藏占位

        // 是否左对齐
        // 优先级: 用户配置>特殊情况(数据量为1)>默认配置
        /*
        alignLeft =
          force === false || extreme === false || extreme?.alignLeft === false
            ? true
            : extreme === true || extreme?.alignLeft === true
            ? false
            : dataSize === 1;
            */
        alignLeft = force === false || extreme === false || extreme?.alignLeft === false;

        // 是否显示占位
        // 优先级:用户配置>默认配置
        showPlaceholder =
          force === false || extreme === false || extreme?.showPlaceholder === false;

        // 左对齐,无占位
        if (alignLeft && !showPlaceholder) {
          const values = Array.from(new Set(data.map((item: any) => item.x)));
          for (let i = 0; i < minCount - dataSize; i += 1) {
            values.push(`widgets-pad-${i}`);
          }
          xAxis = { values };
          warn(
            'Bar',
            '当前数据量较少,已默认开启左对齐,推荐通过extreme配置项开启占位补全。问题码#08',
          );
        }
        // 左对齐且显示占位
        else if (alignLeft && showPlaceholder) {
          for (let i = 0; i < minCount - dataSize; i += 1) {
            newData.push({
              x: `widgets-pad-${i}`,
              y: lastY,
              type: 'widgets-pad-type',
            });
          }
          needColor = true;
          warn(
            'Bar',
            '当前数据量较少,已默认开启左对齐与占位补全,可通过extreme配置项进行关闭。问题码#08',
          );
        }
        // 无特殊处理
        else {
          warn('Bar', '当前数据量较少,推荐通过extreme配置项开启左对齐与占位补全。问题码#08');
        }
      }
      // 时间分类数据
      else if (axisType === 'timeCat') {
        // 计算最后一个柱子的y值
        /*
        let lastY = data?.[0]?.y ?? 0;
        let curMax = data?.[0]?.x ?? 0;
        data.forEach((item: any) => {
          if (item.x > curMax) {
            curMax = item.x;
            lastY = item.y;
          }
        });
        */

        // 时间分类数据默认开启

        // 是否左对齐
        // 优先级: 用户配置>默认配置
        //alignLeft = !(extreme === true || extreme?.alignLeft === true);
        alignLeft = force === false || extreme === false || extreme?.alignLeft === false;

        // 是否显示占位
        // 优先级:用户配置>默认配置
        showPlaceholder = !(extreme === true || extreme?.showPlaceholder === true);

        const values = Array.from(new Set(data.map((item: any) => item.x)));
        const minX = Math.min(...(values as number[]));
        const maxX = Math.max(...(values as number[]));
        const step = maxX !== minX ? Math.floor((maxX - minX) / dataSize) : 100;

        // 左对齐,无占位
        if (alignLeft && !showPlaceholder) {
          const newValues = [...values];
          for (let i = 0; i < minCount - dataSize; i += 1) {
            newValues.push(maxX + step * (i + 1));
          }
          xAxis = { values: newValues, ticks: values };
          warn(
            'Bar',
            '当前数据量较少,已默认开启左对齐,推荐通过extreme配置项开启占位补全。问题码#08',
          );
        }
        // 左对齐且显示占位
        else if (alignLeft && showPlaceholder) {
          for (let i = 0; i < minCount - dataSize; i += 1) {
            newData.push({
              x: maxX + step * (i + 1),
              y: lastY,
              type: 'widgets-pad-type',
            });
          }
          xAxis = {
            ticks: values,
          };
          needColor = true;
          warn(
            'Bar',
            '当前数据量较少,已默认开启左对齐与占位补全,可通过extreme配置项进行关闭。问题码#08',
          );
        }
        // 无特殊处理
        else {
          warn('Bar', '当前数据量较少,推荐通过extreme配置项开启左对齐与占位补全。问题码#08');
        }
      }

      if (needColor) {
        // 颜色处理
        if (Array.isArray(colors)) {
          newColors = [
            ...colors.slice(0, dataTypes.length),
            themes['widgets-color-container-background'],
          ];
        } else if (typeof colors === 'string') {
          newColors = [
            ...dataTypes.map(() => colors),
            themes['widgets-color-container-background'],
          ];
        } else if (typeof colors === 'function') {
          newColors = (type: string) => {
            if (type === 'widgets-pad-type') {
              return themes['widgets-color-container-background'];
            } else {
              return colors(type);
            }
          };
        }
      }

      return {
        isExtreme: true,
        data: newData,
        config: {
          ...(alignLeft
            ? {
                legend:
                  config?.legend === false || config?.legend?.visible === false
                    ? false
                    : {
                        items: dataTypes.map((t: string, index: number) => ({
                          name: t,
                          value: t,
                          marker: {
                            symbol: 'square',
                            style: {
                              fill: Array.isArray(newColors) ? newColors[index] : newColors(t),
                            },
                          },
                        })),
                        ...(config?.legend || {}),
                      },
                xAxis:
                  config?.xAxis === false || config?.xAxis?.visible === false
                    ? false
                    : {
                        ...xAxis,
                        autoHide: false,
                        autoEllipsis: true,
                        ...(config?.xAxis || {}),
                      },
              }
            : {}),
          ...(alignLeft && showPlaceholder
            ? {
                colors: newColors,
                tooltip: false,
              }
            : {}),
        },
      };
    }
  }
  // 线图
  else if (chartName === 'G2Line') {
    // 计算线的数量
    const lineCount = Array.from(new Set(data.map((d: any) => d.type))).length;
    let isEqual = false;
    if (lineCount === 1) {
      let temp = data?.[0]?.y;
      const filterArr = data?.filter((el: any) => el?.y !== temp);
      if (!filterArr?.length && temp !== null && temp !== undefined) {
        isEqual = true;
      }
    }

    // 只有一个点的时候,在Y轴中间,并开启label与symbol
    if (lineCount === 1 && dataSize === 1) {
      warn('Line', '当前线图数据较少,为优化展示,已自动开启标记和文本。');
      return {
        config: {
          yAxis: {
            ...config.yAxis,
            min: data?.[0]?.y > 0 ? 0 : data?.[0]?.y * 2,
            max: data?.[0]?.y > 0 ? data?.[0]?.y * 2 : 0,
          },
          // label判断自定义
          label: {
            layout: {
              // 数据少的时候默认可以开启label 防遮挡,后期可以自行维护防遮挡算法
              type: 'fixed-overlap'
            },
            ...(typeof config?.label === 'object' ? config?.label : {}),
            visible: true,
          },
          symbol: {
            ...(typeof config?.symbol === 'object' ? config?.symbol : {}),
            visible: true,
          },
        },
        isExtreme: true,
      };
    } else if (dataSize === lineCount) {
      // 多条线,每条线一个点时,开启symbol,label暂不开启
      warn('Line', '当前线图数据较少,为优化展示,已自动开启标记。');
      return {
        config: {
          // label: {
          //   ...(typeof config?.label === 'object' ? config?.label : {}),
          //   visible: true,
          // },
          symbol: {
            ...(typeof config?.symbol === 'object' ? config?.symbol : {}),
            visible: true,
          },
        },
        isExtreme: true,
      };
    } else if (lineCount === 1 && dataSize === 2) {
      // 一条线两个点,开启area、symbol和label
      warn('Line', '当前线图数据较少,为优化展示,已自动开启面积、标记、文本。');
      return {
        config: {
          // label判断自定义
          label: {
            layout: {
              // 数据少的时候默认可以开启label 防遮挡,后期可以自行维护防遮挡算法
              type: 'fixed-overlap'
            },
            ...(typeof config?.label === 'object' ? config?.label : {}),
            visible: true,
          },
          symbol: {
            ...(typeof config?.symbol === 'object' ? config?.symbol : {}),
            visible: true,
          },
          area: true,
        },
        isExtreme: true,
      };
    } else if (lineCount === 1 && isEqual) {
      return {
        config: {
          yAxis: {
            ...config.yAxis,
            min: data?.[0]?.y > 0 ? 0 : data?.[0]?.y * 2,
            max: data?.[0]?.y > 0 ? data?.[0]?.y * 2 : 0,
            tickCount: 3,
          },
        },
        isExtreme: true,
      };
    }
  }

  return {
    isExtreme: false,
  };
}