SupersetBI添加自定义Echarts图像、控件

开发环境部署:

SupersetBI二次开发环境部署_健胃消食片-的博客-CSDN博客

superset版本:2.0.0

echarts版本:5.3.1


前言:

参考文章:

superset 二次开发 添加新图例 | 出行应用文档中心 (qq.com)


目录

一、添加自定义图表:

1、\superset-frontend\src\visualizations文件夹下新建Gauge文件夹

新建images文件夹

新建GaugeChartPlugin.js

新建Gauge.jsx前端渲染图表主要逻辑

新建transformProps.js前端后端数据转换

2、\superset-frontend\src\visualizations\presets\MainPreset.js修改文件

3、\superset-frontend\src\explore\controlPanels新增文件Gauge.js

4、\superset-frontend\src\explore\controls.jsx新增自定义组件

5、\superset-frontend\src\setup\setupPlugins.ts修改文件

7、\superset\viz.py修改后端代码

8、终端执行

最终结果:

 二、添加自定义控件

1、superset-frontend/src/explore/controlPanels/controls.jsx添加自定义组件

2、superset-frontend/packages/superset-ui-charts-contrlos/src/shared-controls/index.tsx

3、 superset/viz.py注册自定义组件

4、superset-frontend/src/explore/controlPanels/xxx.js

 三、添加图表所需api脚本

入口文件superset-templates-tail_js_custom_extra.html

 四、发布Superset服务-gunicorn


一、添加自定义图表:

1、\superset-frontend\src\visualizations文件夹下新建Gauge文件夹

  • 新建images文件夹

  • 新建GaugeChartPlugin.js
import { t } from '@superset-ui/core';
import { 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,
});

export default class MixLineBarChartPlugin extends ChartPlugin {
  constructor() {
    super({
      metadata,
      transformProps,
      loadChart: () => import('./ReactMixLineBar.js'), // 前端渲染逻辑
    });
  }
}
  • 新建Gauge.jsx前端渲染图表主要逻辑
import React from 'react'
import * as echarts from 'echarts'
import PropTypes from 'prop-types'

const propTypes = {
  data: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
  width: PropTypes.number,
  height: PropTypes.number,
  chartId: PropTypes.number,
  groupby: PropTypes.arrayOf(PropTypes.string),
}
console.log("propTypes", propTypes)

class Gauge extends React.Component {
  constructor(props) {
    // 
    console.log("props", props)
    super(props)
    // 这里的props为 `superset/assets/src/visualizations/Gauge/transformProps.js` 传递过来的数据
    this.state = {
      chartIns: null
    }
  }
  setOptions() {
    const { data, groupby } = this.props;

    if (data && data.length > 0) {
      const total = data.reduce((acc, item) => acc + item['SUM(sum__num)'], 0);

      // 创建仪表盘配置选项
      const option = {
        tooltip: {
          formatter: '{a} <br/>{b} : {c}%'
        },
        series: data.map((item, index) => {
          // 根据groupby的值构建customTitle
          const customTitle = groupby.map(field => `${field}: ${item[field]}`).join(', ');

          return {
            name: customTitle || `Gauge ${index + 1}`,
            type: 'gauge',
            detail: {
              formatter: '{value}%',
              offsetCenter: [0, `${15 + index * 20}%`], // 调整每个指针的位置
            },
            data: [{ value: ((item['SUM(sum__num)'] / total) * 100).toFixed(1), name: customTitle || `Gauge ${index + 1}` }],
            axisLabel: {
              formatter: '{value}%', // 格式化刻度标签
            },
            // axisLine: {
            //   lineStyle: {
            //     color: [[0.2, 'red'], [0.8, 'orange'], [1, 'green']],
            //     width: 8, // 调整仪表盘刻度线的宽度
            //   },
            // },
            // pointer: {
            //   width: 4, // 调整指针的宽度
            // },
            // splitLine: {
            //   length: 12, // 调整分隔线的长度
            // },
          };
        }),
      };

      this.state.chartIns.setOption(option,true);
    }
  }


  componentDidMount() {
    const { data, chartId = 123, customTitle } = this.props
    const myChart = echarts.init(
      document.getElementById(`gauge_${chartId}`)
    )
    this.setState({
      chartIns: myChart
    }, () => {
      this.setOptions()
    })
  }
  componentDidUpdate() {
    this.setOptions()
    this.state.chartIns.resize()
  }
  render() {
    const { width, height, chartId = 123 } = this.props
    const style = {
      width,
      height
    }
    return <div id={`gauge_${chartId}`} style={style} />
  }
}

Gauge.displayName = 'Gauge'
Gauge.propTypes = propTypes

export default Gauge
  • 新建transformProps.js前端后端数据转换
export default function transformProps(chartProps) {
  const {
    width,
    height,
    queriesData,
    formData,
  } = chartProps;
  // 需要传递给 `superset/assets/src/visualizations/Gauge/Gauge.jsx` 的属性
  return {
    data: queriesData[0].data,
    chartId: formData.sliceId,
    // customTitle: formData.customTitle,
    width,
    height,
    groupby:formData.groupby,
  };
}

 2、\superset-frontend\src\visualizations\presets\MainPreset.js修改文件

# 开头添加
import Gauge from '../Gauge/GaugeChartPlugin';

# 末尾添加
new Gauge().configure({ key: 'gauge'}),

 3、\superset-frontend\src\explore\controlPanels新增文件Gauge.js

import { t } from '@superset-ui/core';

export default {
  // label对应的是每个section的标题
  // expand对应的是改section是否可以被展开
  // controlSetRows 定义的是每一行有几个组件,metric,adhoc_filters对应的是组件的key
  // 可以在superset-frontend/src/explore/controls.jsx
  // 中通过这些key找到对应的配置
  label: t('Gauge Chart'),
  // requiresTime: true,
  controlPanelSections: [
    {
      label: t('Time'),
      controlSetRows: [
        ['granularity_sqla'], 
        ['time_range'],
  //    ['time_grain_sqla'], 
      ],
    },
    {
      label: t('Query'),
      expanded: true,
      controlSetRows: [
        ['metrics'],
        ['adhoc_filters'],
        ['groupby'],
        ['columns'],
        ['row_limit'],
        ['contribution'],
      ],
    },
    {
      label: t('Chart Options'),
      expanded: true,
      controlSetRows: [['color_scheme', 'label_colors']],
    },
  ],
  controlOverrides: {
    // groupby: {
    //   label: t('Series'),
    // },
    // columns: {
    //   label: t('Breakdowns'),
    //   description: t('Defines how each series is broken down'),
    // },
  },
};

4、\superset-frontend\src\explore\controls.jsx新增自定义组件

  label_colors: {
    type: 'ColorMapControl',
    label: t('Color Map'),
    default: {},
    renderTrigger: true,
    mapStateToProps: state => ({
      colorNamespace: state.form_data.color_namespace,
      colorScheme: state.form_data.color_scheme,
    }),
  },

5、\superset-frontend\src\setup\setupPlugins.ts修改文件

# 开头导入
import Gauge from '../explore/controlPanels/Gauge'

# 末尾添加
    .registerValue('gauge', Gauge)

7、\superset\viz.py修改后端代码

# 在METRIC_KEYS数组中添加新增自定义组件
"label_colors",
class GaugeViz(BaseViz):
    viz_type = 'gauge' # 当前图例的类型
    verbose_name = 'Gauge'  # 当前图例的别名
    sort_series = False # 是否排序
    is_timeseries = False # 是否查询时间

    def query_obj(self):
        d = super().query_obj()
        metric = self.form_data.get("metric")
        if not metric:
            raise Exception(_("Pick a metric!"))
        d["metrics"] = [self.form_data.get("metric")]
        self.form_data["metric"] = metric
        d["row_limit"] = None
        return d
    def get_data(self, df):
        # 构造未过滤器数据
        d = super().query_obj()
        d['filter'] = []
        res = self.datasource.query(d)
        # df1 为总数 无过滤器过滤 -> 分母
        df1 = res.df.to_dict(orient="records")
        # df2 为过滤后的数据 -> 分子
        df2 = df.to_dict(orient="records")
        df1.extend(df2)
        return df1

 8、终端执行

# 安装echarts
npm install echarts@5.3.1

// superset-frontend目录下执行
npm run dev

// 虚拟环境中conda执行,发布本地服务
set FLASK_APP=superset
superset run -p 8099

// 本机127.0.0.1:8099可进入Superset

最终结果:

添加自定义图表本质是添加一个react页面,添加d3图之类的也和上述步骤一样;


 二、添加自定义控件

1、superset-frontend/src/explore/controlPanels/controls.jsx添加自定义组件

  lon_column: {
    ...metrics, // 继承
    // multi: true, // 多选
    clearable: true, // 是否可调用, true当作sql
    // validators: [], // 是否可以为空
    label: t('Longitude'),
    description: t('Column containing longitude data'),
    default: null
  },

2、superset-frontend/packages/superset-ui-charts-contrlos/src/shared-controls/index.tsx

const lon_column: SharedControlConfig<'MetricsControl'> = {
  ...metric,
  label: t('Longitude'),
  default: null,
  description: t('Column containing longitude data'),
};

//    sharedControls    中添加自定义组件名称

3、 superset/viz.py注册自定义组件

//    METRIC_KEYS    注册自定义组件

4、superset-frontend/src/explore/controlPanels/xxx.js

export default {  
  label: t('Heatmap_On_Map Chart'),
  controlPanelSections: [
    {
      label: t('Query'),
      expanded: true,
      controlSetRows: [
        ['lon_column']
      ]
    }
  ]
};

  // label对应的是每个section的标题
  // expanded对应的是改section是否可以被展开
  // controlSetRows 定义的是每一行有几个组件,metric,adhoc_filters对应的是组件的key
  // 可以在superset-frontend/src/explore/controls.jsx中通过这些key找到对应的配置

 三、添加图表所需api脚本

图表入口文件superset-templates-tail_js_custom_extra.html

// 添加百度地图api为例
<script type="text/javascript" src="https://api.map.baidu.com/api?v=2.0&ak=YOUR_KEY"></script>

四、打包Superset

依靠主目录下的setup.py文件进行打包成dist文件

// 安装Pyinstaller
pip install pyinstaller

// 主目录下进行打包
pyinstaller setup.py

五、发布Superset服务-gunicorn

该服务是发布在Liunx系统下,使用gunicorn进行发布,连接同一个局域网下的设备都可进行访问;参考代码中发布端口为9119

  •  在superset目录下新建superset.sh脚本
superset_status(){
    result=`ps -ef | awk '/gunicorn/ && !/awk/{print $2}' | wc -l`
    if [[ $result -eq 0 ]]; then
        return 0
    else
        return 1
    fi
}
superset_start(){
        # 该段内容取自~/.bashrc,所用是进行conda初始化
        # >>> conda initialize >>>
        # !! Contents within this block are managed by 'conda init' !!
        __conda_setup="$('/root/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
        if [ $? -eq 0 ]; then
            eval "$__conda_setup"
        else
            if [ -f "/root/anaconda3/etc/profile.d/conda.sh" ]; then
                . "/root/anaconda3/etc/profile.d/conda.sh"
            else
                export PATH="/root/anaconda3/bin:$PATH"
            fi
        fi
        unset __conda_setup
        # <<< conda initialize <<<
        superset_status >/dev/null 2>&1
        if [[ $? -eq 0 ]]; then
            conda activate superset ; gunicorn --workers 5 --timeout 120 --bind 0.0.0.0:9119 --daemon 'superset.app:create_app()'
        else
            echo "superset正在运行"
        fi

}

superset_stop(){
    superset_status >/dev/null 2>&1
    if [[ $? -eq 0 ]]; then
        echo "superset未在运行"
    else
        ps -ef | awk '/gunicorn/ && !/awk/{print $2}' | xargs kill -9
    fi
}


case $1 in
    start )
        echo "启动Superset"
        superset_start
    ;;
    stop )
        echo "停止Superset"
        superset_stop
    ;;
    restart )
        echo "重启Superset"
        superset_stop
        superset_start
    ;;
    status )
        superset_status >/dev/null 2>&1
        if [[ $? -eq 0 ]]; then
            echo "superset未在运行"
        else
            echo "superset正在运行"
  • 启动/暂停脚本
// 启动脚本
/anaconda3/envs/superset/lib/sites-package/superset/superset.sh

// 启动服务
superset.sh start

// 暂停服务
superset.sh stop

六、一些零碎问题的解决

报错:WebGL发生渲染错误,渲染已经停止,请刷新页面。 SecurityError: Failed to construct 'Worker': Script at 'file:///E:/BI/superset-2.0-test/superset-frontend/node_modules/mars3d-cesium/Build/Cesium/Workers/createVerticesFromHeightmap.js' cannot be accessed from origin 'http://127.0.0.1:8099'.

解决: 安装react-dom,不然会报跨域问题的错误

错误:mars3d-cesium中访问资源路径错误

 解决:参考Mars3d官网教程(修改配置文件)——在已有项目中集成Mars3D | Mars3D开发教程

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值