数据平台整合matabase图表,调用matabase已有接口使用echarts实现图表展示
目标
将各类型图形独立封装为组件
将多个组件整体封装成一个组件
使用时只需传入组件名和对应数据即可
展示
数据格式
ECharts中dataset配置
公共组件
示例饼图 pie-chart
pie-chart common.js
let commonOption = {
title: {
text: '标题',
},
legend: {},
tooltip: {
trigger: 'item',
},
toolbox: {
show: true,
feature: {
saveAsImage: { show: true },
},
},
dataset: {
dimensions: [],
source: [],
},
// grid: {
// top:"0",
// bottom: '0',
// containLabel: true,
// },
series: [
{
type: 'pie',
radius: ['40%', '70%'],
// center: ['50%', '25%'],
// label: {
// formatter: '{b}: {@2013} ({d}%)',
// },
encode: {
itemName: 'product',
value: '2013',
tooltip: [0, 1],
},
},
],
}
export { commonOption }
pie-chart options.js
import { commonOption } from './common'
import { deepCopy } from '@utils/index'
function getOption(requestData, cardData) {
console.log('饼图CbnPieChart')
console.log('requestData', requestData)
console.log('cardData', cardData)
let option = deepCopy(commonOption)
// 数组数据对应字段名
option.dataset.dimensions = requestData.cols
// 二维数组数据
option.dataset.source = requestData.rows
// 左上角标题
option.title.text = cardData.name
// 维度
let itemName = cardData.visualization_settings['pie.dimension']
// 衡量标准
let value = cardData.visualization_settings['pie.metric']
// 设置series
option.series = [
{
type: 'pie',
radius: ['40%', '70%'],
encode: {
itemName: itemName,
value: value,
tooltip: Array.from(Array(requestData.cols.length), (v, k) => k),
},
},
]
return option
}
export { getOption }
pie-chart index.vue
<template>
<div class="chart-box">
<div class="chart" ref="chart"></div>
</div>
</template>
<script>
import { getOption } from './option'
import ChartShow from '@mixins/chart-show'
export default {
name: 'CbnPieChart',
mixins: [ChartShow(getOption)],
}
</script>
<style lang="scss" scoped>
.chart-box {
width: 100%;
height: 100%;
.chart {
width: 100%;
height: 100%;
}
}
</style>
展示饼图
<div style="width: 100%; height: 800px">
<cbn-chart :myComponent="myComponent8"></cbn-chart>
</div>
import CbnChart from '@components/cbn-chart'
import { data as data8, cardData as cardData8 } from './pieData'
components: {
CbnChart,
},
myComponent8: {
componentName: 'CbnPieChart',
requestData: data8.data,
cardData: cardData8,
},
pieData.js
let data = {
data: {
rows: [
[42, 'Doohickey'],
[51, 'Gizmo'],
[53, 'Gadget'],
[54, 'Widget'],
],
cols: [
{
display_name: 'NUM',
source: 'native',
field_ref: ['field', 'NUM', { 'base-type': 'type/BigInteger' }],
name: 'NUM',
base_type: 'type/BigInteger',
effective_type: 'type/BigInteger',
},
{
display_name: 'CATEGORY',
source: 'native',
field_ref: ['field', 'CATEGORY', { 'base-type': 'type/Text' }],
name: 'CATEGORY',
base_type: 'type/Text',
effective_type: 'type/Text',
},
],
native_form: {
query: 'select count(id) num, category from products group by category',
params: null,
},
results_timezone: 'Asia/Shanghai',
results_metadata: {
columns: [
{
display_name: 'NUM',
field_ref: ['field', 'NUM', { 'base-type': 'type/BigInteger' }],
name: 'NUM',
base_type: 'type/BigInteger',
effective_type: 'type/BigInteger',
semantic_type: null,
fingerprint: {
global: { 'distinct-count': 4, 'nil%': 0.0 },
type: {
'type/Number': {
min: 42.0,
q1: 46.5,
q3: 53.5,
max: 54.0,
sd: 5.477225575051661,
avg: 50.0,
},
},
},
},
{
display_name: 'CATEGORY',
field_ref: ['field', 'CATEGORY', { 'base-type': 'type/Text' }],
name: 'CATEGORY',
base_type: 'type/Text',
effective_type: 'type/Text',
semantic_type: null,
fingerprint: {
global: { 'distinct-count': 4, 'nil%': 0.0 },
type: {
'type/Text': {
'percent-json': 0.0,
'percent-url': 0.0,
'percent-email': 0.0,
'percent-state': 0.0,
'average-length': 6.5,
},
},
},
},
],
},
insights: null,
},
database_id: 1,
started_at: '2023-01-14T14:53:22.235+08:00',
json_query: {
constraints: { 'max-results': 10000, 'max-results-bare-rows': 2000 },
type: 'native',
middleware: { 'js-int-to-string?': true, 'ignore-cached-results?': false },
native: {
query: 'select count(id) num, category from products group by category',
'template-tags': {},
},
database: 1,
'async?': true,
'cache-ttl': null,
},
average_execution_time: null,
status: 'completed',
context: 'dashboard',
row_count: 4,
running_time: 5,
}
let cardData = {
description: null,
archived: false,
collection_position: null,
table_id: null,
result_metadata: [
{
display_name: 'CATEGORY',
field_ref: [
'field',
'CATEGORY',
{
'base-type': 'type/Text',
},
],
name: 'CATEGORY',
base_type: 'type/Text',
effective_type: 'type/Text',
semantic_type: null,
fingerprint: {
global: {
'distinct-count': 4,
'nil%': 0.0,
},
type: {
'type/Text': {
'percent-json': 0.0,
'percent-url': 0.0,
'percent-email': 0.0,
'percent-state': 0.0,
'average-length': 6.5,
},
},
},
},
{
display_name: 'NUM',
field_ref: [
'field',
'NUM',
{
'base-type': 'type/BigInteger',
},
],
name: 'NUM',
base_type: 'type/BigInteger',
effective_type: 'type/BigInteger',
semantic_type: null,
fingerprint: {
global: {
'distinct-count': 4,
'nil%': 0.0,
},
type: {
'type/Number': {
min: 42.0,
q1: 46.5,
q3: 53.5,
max: 54.0,
sd: 5.477225575051661,
avg: 50.0,
},
},
},
},
],
database_id: 1,
enable_embedding: false,
collection_id: null,
query_type: 'native',
name: 'product-bing',
query_average_duration: 3,
creator_id: 5,
moderation_reviews: [],
updated_at: '2023-01-14T17:26:07.853',
made_public_by_id: null,
embedding_params: null,
cache_ttl: null,
dataset_query: {
type: 'native',
native: {
query: 'select category ,count(id) num from products group by category',
'template-tags': {},
},
database: 1,
},
id: 37,
display: 'pie',
visualization_settings: {
'pie.show_legend_perecent': true,
'pie.colors': {
Doohickey: '#509EE3',
Gadget: '#F9D45C',
Gizmo: '#A989C5',
Widget: '#F2A86F',
},
'pie.dimension': 'CATEGORY',
'pie.metric': 'NUM',
'table.pivot_column': 'CATEGORY',
'table.cell_column': 'NUM',
},
dataset: false,
created_at: '2023-01-14T14:11:42.502',
public_uuid: null,
}
export { data, cardData }
公共index.js
批量获取组件导入整体组件
// 自动加载
const componentsContext = require.context('./', true, /(index\.vue)$/)
let components = {}
componentsContext.keys().forEach((component) => {
const componentConfig = componentsContext(component)
components[componentConfig.default.name] = componentConfig.default
})
export const importComponents = components
整体组件index.vue
<template>
<div class="charts">
<component v-if="myComponent" :is="myComponent.componentName" v-bind="myComponent" />
</div>
</template>
<script>
import { importComponents } from './components/index'
export default {
name: 'Chart',
components: {
...importComponents,
},
props: {
myComponent: {
type: Object,
default: function () {
return null
},
},
},
}
</script>
<style lang="scss" scoped>
.charts {
width: 100%;
height: 100%;
}
</style>
mixins写一个混入方法,将公共代码提取出来 chart-show.js
import '@components/cbn-chart/theme-walden.js'
export default function (getOption) {
return {
props: {
requestData: {
type: Object,
default: () => {
return {}
},
},
cardData: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
myChart: null,
option: null,
}
},
mounted() {
this.reloadChart()
window.addEventListener('resize', () => {
this.reloadChart()
})
},
beforeDestroy() {
this.disposeChart()
},
methods: {
drawChart() {
this.option = getOption(this.requestData, this.cardData)
let chartDom = this.$refs.chart
this.$nextTick(() => {
this.myChart = this.$echarts.init(chartDom, 'walden')
this.myChart.setOption(this.option)
this.myChart.resize()
})
},
// 重新加载图表
reloadChart() {
this.disposeChart()
this.drawChart()
},
// 销毁图表以及重置各个数据
disposeChart() {
if (this.myChart) {
this.myChart.dispose()
}
},
},
}
}
相关代码
链接:https://pan.baidu.com/s/1Ca34Xzp7jtE3nbzpJyNliw
提取码:ozbk