superset0.36接入echarts 图表:mix-line-bar

文章目录

  • superset和echarts版本
  • 集成echarts柱状折线图 mix-line-bar
    • 前端目录 superset-frontend
      • 主要修改的地方
        • 1、 superset-frontend/src/visualizations/ 目录下
          • 1-1 新增文件夹MixLineBar,
          • 1-2 新建文件夹 images 放入新增图表的图片
          • 1-3 新增文件 MixLineBarChartPlugin.js
          • 1-4 新增文件 ReactMixLineBar.js 注册
          • 1-5 新增文件 transformProps.js 前端后端数据转换
          • 1-6 新增文件 MixLineBar.js 前端渲染图表主要逻辑
        • 2、 修改 superset-frontend/src/visualizations/presets/MainPreset.js
        • 3、 修改 superset-frontend/src/explore/components/controls/VizTypeControl.jsx
        • 4、新增 superset-frontend/src/explore/controlPanels/MixLineBar.js
        • 5、修改 superset-frontend/src/explore/controls.jsx 新增的一些自定义组件
        • 6、修改 superset-frontend/src/setup/setupPlugins.ts
        • 7、修改package.json
    • 后端 py 修改 superset/viz.py
      • 1、修改地方 找到 METRIC_KEYS 数组后 添加2个字符串(自定义的组件)
      • 2、修改地方,新增新图表后端逻辑
    • echarts mix-line-bar 图表字段理解
      • 官方 构建新图表option 例子
      • echarts 配置手册 options 参数
      • echarts api文档
    • 结束语

superset和echarts版本

superset : 0.36

echarts : 4.7.0

集成echarts柱状折线图 mix-line-bar

前端目录 superset-frontend

首先0.36版本是比较新的版本,代码结构相比 0.30以前的改动还是比较大的,主要是前端的代码结构变化比较大, superset 把前端的插件单独放在一个superset-ui的项目中;superset中的前端代码主要放在superset-frontend的目录中

主要修改的地方

1、 superset-frontend/src/visualizations/ 目录下

1-1 新增文件夹MixLineBar,

主要新加的文件目录
superset0.36接入echarts 图表:mix-line-bar_第1张图片

1-2 新建文件夹 images 放入新增图表的图片

图片在echarts 上可以下载
https://www.echartsjs.com/examples/zh/index.html

选择你要接入的图表,然后右键另存为 就可以下载下来了
然后放在 images 文件下,可以转化为png

1-3 新增文件 MixLineBarChartPlugin.js
import { t } from '@superset-ui/translation';
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
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,
});

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

1-4 新增文件 ReactMixLineBar.js 注册
import reactify from '@superset-ui/chart/esm/components/reactify';
import Component from './MixLineBar';

export default reactify(Component);

1-5 新增文件 transformProps.js 前端后端数据转换

export default function transformProps(chartProps) {

    const {width, height, queryData, formData} = chartProps;
    // formData 前端页面的数据
    // queryData  后端返回的数据
    return {
        data: queryData.data,
        width,
        height,
        formData,
        legend: queryData.data.legend,
        x_data: queryData.data.x_data,
        series: queryData.data.data,
    };

}
1-6 新增文件 MixLineBar.js 前端渲染图表主要逻辑
import echarts from 'echarts';
import d3 from 'd3';
import PropTypes from 'prop-types';
import { CategoricalColorNamespace } from '@superset-ui/color';

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

function MixLineBar(element, props) {

    const {
        width,
        height,
        data,
        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);
    var colors = []
    if (colorFn && colorFn.colors) {
        colors = colorFn.colors
    }
    const colors_len = colors.length

    // y轴配置格式
    var yAxis_1 = {
        type: 'value',
        name: 'Left_Y_Axis',
        axisLabel: {
            formatter: '{value}'
        }
    }

    var 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++) {
        var 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.html(html); // init echarts,light 为制定主题,可以查看官方api var myChart = echarts.init(document.getElementById(sliceId), 'light'); // echarts 渲染图表的数据格式 在官网可以查看 var 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: series, }; myChart.setOption(option); } MixLineBar.displayName = 'Mix Line Bar'; MixLineBar.propTypes = propTypes; export default MixLineBar;

2、 修改 superset-frontend/src/visualizations/presets/MainPreset.js

配置

// 开头导入
import MixLineBarChartPlugin from '../MixLineBar/MixLineBarChartPlugin'

// 末尾添加
new MixLineBarChartPlugin().configure({ key: 'mix_line_bar' }),


3、 修改 superset-frontend/src/explore/components/controls/VizTypeControl.jsx

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

4、新增 superset-frontend/src/explore/controlPanels/MixLineBar.js

前端页面布局

/**
 *   https://www.echartsjs.com/examples/zh/editor.html?c=mix-line-bar
 *   mix line bar
 */
import { t } from '@superset-ui/translation';


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'],
            ],
        },

    ],
    controlOverrides: {

    },
};

5、修改 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_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'),
  },

6、修改 superset-frontend/src/setup/setupPlugins.ts

// 开头引入
import MixLineBar from '../explore/controlPanels/MixLineBar';

// 末尾注册
.registerValue('mix_line_bar', MixLineBar)

7、修改package.json

// 新增引入 echarts 版本
"echarts": "^4.7.0"

后端 py 修改 superset/viz.py

图表处理的逻辑都在这个文件中

1、修改地方 找到 METRIC_KEYS 数组后 添加2个字符串(自定义的组件)

"line_metrics", "bar_metrics",

2、修改地方,新增新图表后端逻辑

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

echarts mix-line-bar 图表字段理解

官方 构建新图表option 例子

https://www.echartsjs.com/examples/zh/editor.html?c=mix-line-bar

echarts 配置手册 options 参数

https://www.echartsjs.com/zh/option.html#series-bar

echarts api文档

https://www.echartsjs.com/zh/api.html#echarts

结束语

本人不太会前端js,所以js文件 写的有点丑,有大佬可以帮忙改进一下

这篇文章主要是写了如何接入echarts 的柱状折线图,基本echarts 其他图也类似

后续
1、36 版本 源码搭建superset
2、superset 怎么渲染图表的 从后端到前端
3、superset 权限是怎么控制的,以及如何把dashboard 提权
4、会写原生table组件加载数据过多会比较慢,考虑用antd 的table 代替

你可能感兴趣的:(python,superset)