文章目錄
- 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
- echarts mix-line-bar 圖表字段理解
- 結束語
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,
主要新加的文件目錄
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 id='+ sliceId + ' style="height:' + height + 'px; width:' + width + 'px;"></div>';
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'),
},
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 代替