IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> superset二次开发-添加echarts图表:mix-line-bar -> 正文阅读

[JavaScript知识库]superset二次开发-添加echarts图表:mix-line-bar

效果预览如下:

在这里插入图片描述

一、前端(superset\superset-frontend)

1、superset\superset-frontend\src\visualizations目录下,新增MixLineBar文件夹,主要文件如下图:
在这里插入图片描述
2、MixLineBar文件夹内容:

2.1、images文件夹:存放新增图表的预览图:thumbnail.png和thumbnailLager.png
下载地址:https://echarts.apache.org/examples/zh/editor.html?c=mix-line-bar
注意图片尺寸问题
2.2、MixLineBar.js => 前端引入echarts进行新图表渲染

import echarts from 'echarts';
import d3 from 'd3';
import PropTypes from 'prop-types';
import { CategoricalColorNamespace } from '@superset-ui/core';

// 数据类型检查
const propTypes = {
  data: PropTypes.object,
  width: PropTypes.number,
  height: PropTypes.number,
};

function MixLineBar(element, props) {
  const { width, height, formData, x_data, series, legend } = props; // transformProps.js 返回的数据
  
  const fd = formData;
  // 配置y轴显示信息
  const left_y_min = fd.leftYMIn;
  const left_y_max = fd.leftYMax;
  const left_y_interval = fd.leftYInterval;
  const right_y_min = fd.rightYMin;
  const right_y_max = fd.rightYMax;
  const right_y_interval = fd.rightYInterval;
  // y轴别名
  const y_axis_label = fd.yAxisLabel;
  const y_axis_2_label = fd.yAxis2Label;
  // 右边y轴 对应的 指标列
  const right_y_column = fd.rightYColumn;
  // 为了适配颜色
  const colorFn = CategoricalColorNamespace.getScale(fd.colorScheme);
  let colors = [];
  if (colorFn && colorFn.colors) {
    colors = colorFn.colors;
  }
  const colors_len = colors.length;
  // y轴配置格式
  const yAxis_1 = {
    type: 'value',
    name: 'Left_Y_Axis',
    axisLabel: {
      formatter: '{value}',
    },
  };

  const yAxis_2 = {
    type: 'value',
    name: 'Right_Y_Axis',
    axisLabel: {
      formatter: '{value}',
    },
  };

  if (left_y_min !== undefined) {
    yAxis_1.mix = left_y_min;
  }
  if (left_y_max != undefined) {
    yAxis_1.max = left_y_max;
  }
  if (left_y_interval != undefined) {
    yAxis_1.interval = left_y_interval;
  }
  if (right_y_min != undefined) {
    yAxis_2.mix = right_y_min;
  }
  if (right_y_max != undefined) {
    yAxis_2.max = right_y_max;
  }
  if (right_y_interval != undefined) {
    yAxis_2.interval = right_y_interval;
  }
  if (y_axis_label != undefined) {
    yAxis_1.name = y_axis_label;
  }
  if (y_axis_2_label != undefined) {
    yAxis_2.name = y_axis_2_label;
  }

  // 处理series 显示的数据 [{'name':xx, 'type':xx, 'data':xx, 'yAxisIndex':xx}]
  // 重新请求时, 默认展示左y
  for (let i = 0; i < series.length; i++) {
    const serie = series[i];
    serie.yAxisIndex = 0;
    if (
      right_y_column != undefined &&
      right_y_column.indexOf(serie.name) >= 0
    ) {
      serie.yAxisIndex = 1;
    }
    if (colors_len > 0) {
      serie.itemStyle = {
        color: colors[i % colors_len],
      };
    }
  }

  const div = d3.select(element);
  const sliceId = `mix-bar-line-${fd.sliceId}`;
  const html = `<div id=${sliceId} style="height:${height}px; width:${width}px;"></div>`;
  div.html(html);
  // init echarts,light 为制定主题,可以查看官方api

  const myChart = echarts.init(document.getElementById(sliceId), 'light');
  // echarts 渲染图表的数据格式 在官网可以查看

  const option = {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'cross',
        crossStyle: {
          color: '#999',
        },
      },
    },
    legend: {
      data: legend, // [] x轴的数据
    },
    xAxis: [
      {
        type: 'category',
        data: x_data,
        axisPointer: {
          type: 'shadow',
        },
      },
    ],
    yAxis: [yAxis_1, yAxis_2],
    series,
  };

  myChart.setOption(option);
}

MixLineBar.displayName = 'Mix Line Bar';
MixLineBar.propTypes = propTypes;

export default MixLineBar;

2.3、MixLineBarChartPlugin.js =>superset中渲染构造新图表

import { t, ChartMetadata, ChartPlugin } from '@superset-ui/core';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';

const metadata = new ChartMetadata({
  name: t('Mix Line Bar'),
  description: '',
  credits: ['https://www.echartsjs.com/examples/en/editor.html?c=mix-line-bar'],
  thumbnail,
  useLegacyApi: true,
});

export default class MixLineBarChartPlugin extends ChartPlugin {
  constructor() {
    super({
      metadata,
      transformProps,
      loadChart: () => import('./ReactMixLineBar.js'), // 前端渲染逻辑
    });
  }
}

2.4、ReactMixLineBar.js => 注册新图表

import reactify from '@superset-ui/core/esm/chart/components/reactify';
import Component from './MixLineBar';

export default reactify(Component);

2.5、transformProps.js =>前后端数据传递

export default function transformProps(chartProps) {
  const { width, height, queriesData, formData } = chartProps;
  // formData 前端页面的数据 queriesData 后端返回的数据
  return {
    data: queriesData[0].data,
    width,
    height,
    formData,
    legend: queriesData[0].data.legend,
    x_data: queriesData[0].data.x_data,
    series: queriesData[0].data.data,
  };
}

3、superset\superset-frontend\src\visualizations\presets\MainPreset.js文件

// 开头导入
import MixLineBarChartPlugin from '../MixLineBar/MixLineBarChartPlugin';
// 末尾添加
new MixLineBarChartPlugin().configure({ key: 'mix_line_bar' }),

4、superset\superset-frontend\src\explore\components\controls\VizTypeControl\index.jsx

 // 找到 DEFAULT_ORDER 变量 数组末尾添加新图表
 'mix_line_bar',

5、superset\superset-frontend\src\explore\controlPanels\MixLineBar.js => superset中图表布局

/**
 *   https://echarts.apache.org/examples/zh/editor.html?c=mix-line-bar
 *   mix line bar
 */
import { t } from '@superset-ui/core';

export default {
    requiresTime: true,
    controlPanelSections: [
        {
            label: t('Chart Options'),
            expanded: true,
            controlSetRows: [
                ['color_scheme', 'label_colors'],
            ],
        },
        {
            label: t('X Axis'),
            expanded: true,
            controlSetRows: [
                ['groupby'],
            ],
        },
        {
            label: t('Line Type'),
            expanded: true,
            controlSetRows: [
                ['line_metrics'],
            ],
        },
        {
            label: t('Bar Type'),
            expanded: true,
            controlSetRows: [
                ['bar_metrics'],
            ],
        },
        {
            label: t('Real Y Axis 2 Display Columns'),
            expanded: true,
            controlSetRows: [
                ['right_y_column'],
            ],
        },
        {
            label: t('Y Axis 1 Scale Value Setting'),
            expanded: true,
            controlSetRows: [
                ['left_y_min', 'left_y_max', 'left_y_interval'],
                ['y_axis_label']
            ],
        },
        {
            label: t('Y Axis 2 Scale Value Setting'),
            expanded: true,
            controlSetRows: [
                ['right_y_min', 'right_y_max', 'right_y_interval'],
                ['y_axis_2_label']
            ],
        },
        {
            label: t('Query'),
            expanded: true,
            controlSetRows: [
                ['adhoc_filters', ['row_limit'], ['limit']],
            ],
        },

    ],
    controlOverrides: {  },
};

6、superset\superset-frontend\src\explore\controls.jsx =>新增自定义组件,适配新图表

  line_metrics: {
    ...metrics, // 继承
    multi: true, // 多选
    clearable: true, // 是否可调用, true当作sql
    validators: [], // 是否可以为空
    label: t('Line Type Metrics'),
    description: t('Metrics for which line type are to be displayed'),
  },
  bar_metrics: {
    ...metrics,
    multi: true,
    clearable: true,
    validators: [],
    label: t('Bar Type Metrics'),
    description: t('Metrics for which bar type are to be displayed'),
  },
  y_metrics_2: {
    ...metrics, 
    multi: true,
    validators: [],
    default:null,
    label: t('Y Axis 2 Columns'),
    description: t('Select the numeric columns to display in Right-Y-Axis'),
  },
    left_y_min: {
    type: 'TextControl', //文本输入
    label: t('Left Y Min'),
    renderTrigger: true,
    isInt: true,
    description: t('Left Y Min'),
  },
  left_y_max: {
    type: 'TextControl',
    label: t('Left Y Max'),
    renderTrigger: true,
    isInt: true,
    description: t('Left Y Max'),
  },
  left_y_interval: {
    type: 'TextControl',
    label: t('Left Y Interval'),
    renderTrigger: true,
    isInt: true,
    description: t('Left Y Interval'),
  },
  right_y_min: {
    type: 'TextControl',
    label: t('Right Y Min'),
    renderTrigger: true,
    isInt: true,
    description: t('Right Y Min'),
  },
  right_y_max: {
    type: 'TextControl',
    label: t('Right Y Max'),
    renderTrigger: true,
    isInt: true,
    description: t('Right Y Max'),
  },
  right_y_interval: {
    type: 'TextControl',
    label: t('Right Y Interval'),
    renderTrigger: true,
    isInt: true,
    description: t('Right Y Interval'),
  },
  y_axis_label: {
    type: 'TextControl',
    label: t('Y Axis Label'),
    renderTrigger: true,
    default: '',
  },
  y_axis_2_label: {
    type: 'TextControl',
    label: t('Y Axis 2 Label'),
    renderTrigger: true,
    default: '',
  },
 right_y_column: {
    type: 'SelectControl',
    freeForm: true,
    renderTrigger: true,
    multi: true,
    label: t('Y Axis 2 Column'),
    description: t('Choose or add metrics (label) to display in right y axis'),
  },

7、superset\superset-frontend\src\setup\setupPlugins.ts

// 开头引入
import MixLineBar from '../explore/controlPanels/MixLineBar';
// 末尾注册
.registerValue('mix_line_bar', MixLineBar)

8、superset\superset-frontend\src\explore\controlUtils文件夹下新增文件:expandControlConfig.js

import React from 'react';
import sharedControlComponents from '@superset-ui/chart-controls/lib/shared-controls/components';
import { controls as customizeControls } from '../controls';

export function expandControlType(controlType) {
  if (
    typeof controlType === 'string' &&
    controlType in sharedControlComponents
  ) {
    return sharedControlComponents[controlType];
  }
  return controlType;
}

export function expandControlConfig(control, controlOverrides = {}) {
  // one of the named shared controls
  if (typeof control === 'string' && control in customizeControls) {
    const name = control;
    return {
      name,
      config: { ...customizeControls[name], ...controlOverrides[name] },
    };
  } // JSX/React element or NULL

  if (
    !control ||
    typeof control === 'string' ||
    /* #__PURE__ */ React.isValidElement(control)
  ) {
    return control;
  } // already fully expanded control config, e.g.

  if ('name' in control && 'config' in control) {
    return {
      ...control,
      config: {
        ...control.config,
        type: expandControlType(control.config.type),
      },
    };
  } // apply overrides with shared controls

  if ('override' in control && control.name in customizeControls) {
    const { name, override } = control;
    return {
      name,
      config: { ...customizeControls[name], ...override },
    };
  }

  return null;
}

同目录下getSectionsToRender.ts

// expandControlConfig 文件引入方式做调整
import {
  ControlPanelConfig,
} from '@superset-ui/chart-controls';
import { expandControlConfig } from './expandControlConfig';

9、superset\superset-frontend目录下,安装指定版本的echarts
执行命令:npm install echarts@4.7.0 --save

二、后端(superset\superset)

1、superset\superset\viz.py

// 找到 METRIC_KEYS 变量 数组末尾添加2个字符串(自定义的组件)
"line_metrics", "bar_metrics",

2、后端注册mix-line-bar图表,注意:代码放在 def get_subclasses 之前

class MixLineBarViz(NVD3Viz):
    """ mix line bar"""
    viz_type = "mix_line_bar"
    verbose_name = _("Mix Line Bar")
    # 是否排序
    sort_series = False
    # 是否对time 做处理 _timestamp
    is_timeseries = False

    def query_obj(self):
        # check bar column, line column 是否重复
        bar_metrics = self.form_data.get('bar_metrics')
        line_metrics = self.form_data.get('line_metrics')
        if not bar_metrics and not line_metrics:
            raise Exception(_("Please choose metrics on line or bar type"))
        bar_metrics = [] if not bar_metrics else bar_metrics
        line_metrics = [] if not line_metrics else line_metrics
        intersection = [m for m in bar_metrics if m in line_metrics]
        if intersection:
            raise Exception(_("Please choose different metrics on line and bar type"))
        d = super().query_obj()
        return d

    def to_series(self, df, classed=""):
        """
         拼接 前端渲染需要的数据
        :param df:
        :param classed:
        :return: {'legend':[], 'bar':[], 'line':[]}
        """
        cols = []
        for col in df.columns:
            if col == "":
                cols.append("N/A")
            elif col is None:
                cols.append("NULL")
            else:
                cols.append(col)
        df.columns = cols
        series = df.to_dict("series")
        # [{}]
        bar_metrics = self.form_data.get('bar_metrics', [])
        bar_metrics = [] if not bar_metrics else bar_metrics
        line_metrics = self.form_data.get('line_metrics', [])
        line_metrics = [] if not line_metrics else line_metrics

        metrics = self.all_metrics
        legend, data = [], []
        for mt in metrics:
            m_label = utils.get_metric_name(mt)
            ys = series[m_label]
            if df[m_label].dtype.kind not in "biufc":
                continue
            legend.append(m_label)
            info = {
                "name": m_label,
                "data": [
                    ys.get(ds, None) for ds in df.index
                ],
                "type": ''
            }
            if mt in bar_metrics:
                info['type'] = 'bar'
            elif mt in line_metrics:
                info['type'] = 'line'
            else:
                continue
            data.append(info)
        chart_data = {
            'legend': legend,
            'data': data,
            'x_data': [str(ds) if not isinstance(ds, tuple) else ','.join(map(str, ds)) for ds in df.index]
        }

        return chart_data

    def get_data(self, df: pd.DataFrame):
        # 后端返回的数据
        df = df.pivot_table(index=self.groupby, values=self.metric_labels)
        chart_data = self.to_series(df)
        return chart_data

三、前后端启动联调即可

?????

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-16 21:57:36  更:2021-07-16 21:58:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 5:12:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码