环形占比图

This commit is contained in:
jingna 2025-07-15 09:43:31 +08:00
parent ad1fa0ee92
commit 3f7f96bf2c
16 changed files with 210 additions and 11 deletions

View File

@ -76,6 +76,7 @@ const iconChartMap = {
'percentage-bar-stack': percentageBarStack, 'percentage-bar-stack': percentageBarStack,
'pie-donut-rose': pieDonutRose, 'pie-donut-rose': pieDonutRose,
'pie-donut': pieDonut, 'pie-donut': pieDonut,
'pie-proportion': pieDonut,
'pie-rose': pieRose, 'pie-rose': pieRose,
pie: pie, pie: pie,
'progress-bar': progressBar, 'progress-bar': progressBar,

View File

@ -1440,6 +1440,7 @@ export default {
chart_area_stack: 'Stacked line chart', chart_area_stack: 'Stacked line chart',
chart_pie: 'Pie chart', chart_pie: 'Pie chart',
chart_pie_donut: 'Ring chart', chart_pie_donut: 'Ring chart',
chart_pie_proportion: 'Ring proportion chart',
chart_pie_rose: 'Rose chart', chart_pie_rose: 'Rose chart',
chart_pie_donut_rose: 'Rose ring chart', chart_pie_donut_rose: 'Rose ring chart',
chart_funnel: 'Funnel chart', chart_funnel: 'Funnel chart',

View File

@ -1402,6 +1402,7 @@ export default {
chart_area_stack: '堆疊折線圖', chart_area_stack: '堆疊折線圖',
chart_pie: '餅圖', chart_pie: '餅圖',
chart_pie_donut: '環形圖', chart_pie_donut: '環形圖',
chart_pie_proportion: '环形占比图',
chart_pie_rose: '玫瑰圖', chart_pie_rose: '玫瑰圖',
chart_pie_donut_rose: '玫瑰環形圖', chart_pie_donut_rose: '玫瑰環形圖',
chart_funnel: '漏斗圖', chart_funnel: '漏斗圖',

View File

@ -1407,6 +1407,7 @@ export default {
chart_area_stack: '堆叠折线图', chart_area_stack: '堆叠折线图',
chart_pie: '饼图', chart_pie: '饼图',
chart_pie_donut: '环形图', chart_pie_donut: '环形图',
chart_pie_proportion: '环形占比图',
chart_pie_rose: '玫瑰图', chart_pie_rose: '玫瑰图',
chart_pie_donut_rose: '玫瑰环形图', chart_pie_donut_rose: '玫瑰环形图',
chart_funnel: '漏斗图', chart_funnel: '漏斗图',

View File

@ -92,7 +92,7 @@ init()
<template> <template>
<el-form ref="colorForm" :model="state.colorForm" label-width="80px" size="small"> <el-form ref="colorForm" :model="state.colorForm" label-width="80px" size="small">
<div> <div>11
<el-form-item :label="t('chart.color_case')" class="form-item"> <el-form-item :label="t('chart.color_case')" class="form-item">
<el-popover placement="bottom" width="400" trigger="click"> <el-popover placement="bottom" width="400" trigger="click">
<template #reference> <template #reference>

View File

@ -160,6 +160,7 @@ const switchSeriesColor = (seriesColor, index) => {
} }
const changeSeriesColor = () => { const changeSeriesColor = () => {
// seriesColorState.curSeriesColor.color
let changed = false let changed = false
seriesColorState.seriesColor.forEach(c => { seriesColorState.seriesColor.forEach(c => {
if ( if (
@ -419,7 +420,6 @@ const colorItemBorderColor = (index, state) => {
{{ t('chart.reset') }} {{ t('chart.reset') }}
</span> </span>
</div> </div>
<div <div
v-if="!((!sub && showProperty('seriesColor')) || (sub && showProperty('subSeriesColor')))" v-if="!((!sub && showProperty('seriesColor')) || (sub && showProperty('subSeriesColor')))"
class="custom-color-extend-setting colors" class="custom-color-extend-setting colors"

View File

@ -322,8 +322,6 @@ const chartStyleShow = computed(() => {
}) })
const chartViewInstance = computed(() => { const chartViewInstance = computed(() => {
// console.log(chartViewManager.getChartView(view.value.render, view.value.type))
// debugger
return chartViewManager.getChartView(view.value.render, view.value.type) return chartViewManager.getChartView(view.value.render, view.value.type)
}) })
const showAxis = (axis: AxisType) => chartViewInstance.value?.axis?.includes(axis) const showAxis = (axis: AxisType) => chartViewInstance.value?.axis?.includes(axis)

View File

@ -1460,6 +1460,13 @@ export const CHART_TYPE_CONFIGS = [
title: t('chart.chart_pie_donut'), title: t('chart.chart_pie_donut'),
icon: 'pie-donut' icon: 'pie-donut'
}, },
{
render: 'antv',
category: 'distribute',
value: 'pie-proportion',
title: t('chart.chart_pie_proportion'),
icon: 'pie-proportion'
},
{ {
render: 'antv', render: 'antv',
category: 'distribute', category: 'distribute',

View File

@ -11,7 +11,7 @@ const CHART_CATEGORY = {
COLUMN: ['bar', 'bar-stack', 'bar-group', 'bar-group-stack', 'percentage-bar-stack'], COLUMN: ['bar', 'bar-stack', 'bar-group', 'bar-group-stack', 'percentage-bar-stack'],
LINE: ['line', 'area', 'area-stack'], LINE: ['line', 'area', 'area-stack'],
MIX: ['chart-mix', 'chart-mix-group', 'chart-mix-stack', 'chart-mix-dual-line'], MIX: ['chart-mix', 'chart-mix-group', 'chart-mix-stack', 'chart-mix-dual-line'],
PIE: ['pie', 'pie-donut'] PIE: ['pie', 'pie-proportion', 'pie-donut']
} }
/** /**
@ -464,7 +464,6 @@ class ChartCarouselTooltip {
mouseX <= rect.right - 10 && mouseX <= rect.right - 10 &&
mouseY >= rect.top + 10 && mouseY >= rect.top + 10 &&
mouseY <= rect.bottom - 10 mouseY <= rect.bottom - 10
console.log(isInside)
if (!isInside) { if (!isInside) {
this.paused() this.paused()
this.resume() this.resume()

View File

@ -0,0 +1,141 @@
// G2Pie.ts
import { Chart } from '@antv/g2'
import {
G2ChartView
} from '@/views/chart/components/js/panel/types/impl/g2'
import {
parseJson,
hexColorToRGBA,
setUpSingleDimensionSeriesColor
} from '@/views/chart/components/js/util'
import { getPadding } from '@/views/chart/components/js/panel/common/common_antv'
import { cloneDeep } from 'lodash-es'
import {
PIE_AXIS_CONFIG,
PIE_AXIS_TYPE,
PIE_EDITOR_PROPERTY,
PIE_EDITOR_PROPERTY_INNER
} from "@/views/chart/components/js/panel/charts/pie/common";
/**
* G2 Chart 封装器支持统一销毁和渲染
*/
class ChartWrapper {
chartInstance: Chart
constructor(chartInstance: Chart) {
this.chartInstance = chartInstance
}
destroy() {
this.chartInstance?.destroy?.()
}
render() {
this.chartInstance?.render?.()
}
}
export class G2Pie extends G2ChartView {
axis: AxisType[] = PIE_AXIS_TYPE
properties = PIE_EDITOR_PROPERTY
propertyInner: EditorPropertyInner = {
...PIE_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['colors', 'alpha', 'radius','innerRadius', 'seriesColor'],
'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel']
}
axisConfig = PIE_AXIS_CONFIG
constructor() {
super()
this.name = 'pie-proportion'
}
async drawChart({ container, chart }: { container: HTMLElement; chart: ChartObj }) {
const customAttr = parseJson(chart.customAttr)
const innerRadius = customAttr.basicStyle.innerRadius ?? 60
const data = cloneDeep(chart.data?.data || [])
const { radius = 0.75, alpha = 1, colors = [] } = customAttr.basicStyle
const colorList = customAttr.basicStyle.colors.map(i =>
hexColorToRGBA(i, customAttr.basicStyle.alpha)
)
const g2 = new Chart({
container,
autoFit: true,
height: 300
})
g2.coordinate('theta', {
radius: customAttr.basicStyle.radius / 100,
innerRadius: innerRadius / 100
})
g2.data(data)
g2.legend(false)
g2.tooltip({
showMarkers: false
})
g2.facet('rect', {
fields: ['name'],
padding: 20,
showTitle: false,
eachView: (view, facet) => {
const data:any = facet.data
let color
color = colorList[0];
data.push({ name: '其他', value: 100 - data[0].value })
view.data(data)
view.coordinate('theta', {
radius: customAttr.basicStyle.radius / 100,
innerRadius: customAttr.basicStyle.innerRadius / 100
})
view
.interval()
.adjust('stack')
.position('value')
.color('name', (name) => {
return name === '其他' ? colorList[1] : colorList[0];
})
.style({
opacity: 1,
});
view.annotation().text({
position: [ '50%', '50%' ],
content: data[0].name,
style: {
fontSize: 12,
fill: '#8c8c8c',
fontWeight: 300,
textBaseline: 'bottom',
textAlign: 'center'
},
offsetY: -12,
})
view.annotation().text({
position: ['50%', '50%'],
content: data[0].value,
style: {
fontSize: 18,
fill: '#fff',
fontWeight: 500,
textAlign: 'center'
},
offsetY: 10,
})
}
})
g2.render()
return new ChartWrapper(g2)
}
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
return setUpSingleDimensionSeriesColor(chart, data)
}
}

View File

@ -1792,7 +1792,7 @@ export function configPlotTooltipEvent<O extends PickOptions, P extends Plot<O>>
chart.customAttr?.tooltip?.carousel && chart.customAttr?.tooltip?.carousel &&
(!event || // 事件触发时使用event的client坐标 (!event || // 事件触发时使用event的client坐标
['plot:leave', 'plot:mouseleave'].includes(event?.type) || //鼠标离开时使用tooltipCtl.point ['plot:leave', 'plot:mouseleave'].includes(event?.type) || //鼠标离开时使用tooltipCtl.point
['pie', 'pie-rose', 'pie-donut'].includes(chart.type)) // 饼图时使用tooltipCtl.point ['pie', 'pie-proportion', 'pie-rose', 'pie-donut'].includes(chart.type)) // 饼图时使用tooltipCtl.point
plot.options.tooltip.showMarkers = isCarousel ? true : false plot.options.tooltip.showMarkers = isCarousel ? true : false
const wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER) const wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER)
wrapperDom.style.zIndex = isCarousel && wrapperDom ? carousel_zIndex : '9999' wrapperDom.style.zIndex = isCarousel && wrapperDom ? carousel_zIndex : '9999'

View File

@ -0,0 +1,22 @@
// G2ChartView.ts
import { Chart } from '@antv/g2'
import {
AntVAbstractChartView,
ChartLibraryType,
ChartWrapper
} from '@/views/chart/components/js/panel/types'
export abstract class G2ChartView extends AntVAbstractChartView {
constructor(name: string, defaultData: any[] = []) {
super(ChartLibraryType.G2, name, defaultData)
}
public abstract drawChart(drawOptions: {
container: HTMLElement
chart: ChartObj
}): Promise<ChartWrapper<Chart> | void>
public destroyChart(chart: Chart) {
chart?.destroy()
}
}

View File

@ -9,6 +9,7 @@ export enum ChartRenderType {
} }
export enum ChartLibraryType { export enum ChartLibraryType {
G2 = 'g2',
G2_PLOT = 'g2plot', G2_PLOT = 'g2plot',
L7_PLOT = 'l7plot', L7_PLOT = 'l7plot',
L7 = 'l7', L7 = 'l7',

View File

@ -246,6 +246,7 @@ const mapChartTypes = ['bubble-map', 'flow-map', 'heat-map', 'map', 'symbolic-ma
const distributionChartTypes = [ const distributionChartTypes = [
'pie', 'pie',
'pie-donut', 'pie-donut',
'pie-proportion',
'pie-rose', 'pie-rose',
'pie-donut-rose', 'pie-donut-rose',
'radar', 'radar',

View File

@ -12,6 +12,7 @@ import {
import { getData } from '@/api/chart' import { getData } from '@/api/chart'
import { ChartLibraryType } from '@/views/chart/components/js/panel/types' import { ChartLibraryType } from '@/views/chart/components/js/panel/types'
import { G2PlotChartView } from '@/views/chart/components/js/panel/types/impl/g2plot' import { G2PlotChartView } from '@/views/chart/components/js/panel/types/impl/g2plot'
import { G2ChartView } from '@/views/chart/components/js/panel/types/impl/g2'
import { L7PlotChartView } from '@/views/chart/components/js/panel/types/impl/l7plot' import { L7PlotChartView } from '@/views/chart/components/js/panel/types/impl/l7plot'
import chartViewManager from '@/views/chart/components/js/panel' import chartViewManager from '@/views/chart/components/js/panel'
import { useAppStoreWithOut } from '@/store/modules/app' import { useAppStoreWithOut } from '@/store/modules/app'
@ -239,25 +240,47 @@ const calcData = async (view, callback) => {
callback?.() callback?.()
} }
} }
let g2VTimer: any
const renderG2 = async (chart, chartView: G2ChartView<any, any>) => {
g2VTimer && clearTimeout(g2VTimer)
g2VTimer = setTimeout(async () => {
try {
myChart?.destroy?.()
myChart = await chartView.drawChart({
chartObj: myChart,
container: containerId,
chart,
scale: 1,
action,
quadrantDefaultBaseline
})
// G2 Chart render
myChart?.render?.()
} catch (e) {
console.error('renderG2 error', e)
}
}, 300)
}
let curView let curView
const renderChart = async (view, callback?) => { const renderChart = async (view, callback?) => {
console.log('renderChart',view.type, callback) // console.log('renderChart',view, callback)
if (!view) { if (!view) {
return return
} }
curView = view curView = view
// view view.data // view view.data
// 便 // 便
console.log(view,998779)
const chart = deepCopy({ const chart = deepCopy({
...defaultsDeep(view, cloneDeep(BASE_VIEW_CONFIG)), ...defaultsDeep(view, cloneDeep(BASE_VIEW_CONFIG)),
data: chartData.value, data: chartData.value,
...(props.fontFamily && props.fontFamily !== 'inherit' ? { fontFamily: props.fontFamily } : {}) ...(props.fontFamily && props.fontFamily !== 'inherit' ? { fontFamily: props.fontFamily } : {})
}) })
console.log('chart1', chart.customAttr.basicStyle.colors)
const chartView = chartViewManager.getChartView(view.render, view.type) const chartView = chartViewManager.getChartView(view.render, view.type)
recursionTransObj(customAttrTrans, chart.customAttr, scale.value, terminal.value) recursionTransObj(customAttrTrans, chart.customAttr, scale.value, terminal.value)
recursionTransObj(customStyleTrans, chart.customStyle, scale.value, terminal.value) recursionTransObj(customStyleTrans, chart.customStyle, scale.value, terminal.value)
console.log('chart', chart) console.log('chart', chart)
console.log( chartView)
switch (chartView.library) { switch (chartView.library) {
case ChartLibraryType.L7_PLOT: case ChartLibraryType.L7_PLOT:
await renderL7Plot(chart, chartView as L7PlotChartView<any, any>, callback) await renderL7Plot(chart, chartView as L7PlotChartView<any, any>, callback)
@ -269,6 +292,10 @@ const renderChart = async (view, callback?) => {
await renderG2Plot(chart, chartView as G2PlotChartView<any, any>) await renderG2Plot(chart, chartView as G2PlotChartView<any, any>)
callback?.() callback?.()
break break
case ChartLibraryType.G2:
await renderG2(chart, chartView as G2ChartView<any, any>)
callback?.()
break
default: default:
break break
} }
@ -336,7 +363,6 @@ const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any
}, 500) }, 500)
} }
// let satelliteLayer: any = null; // let satelliteLayer: any = null;
class SatelliteControl extends Control { class SatelliteControl extends Control {
protected onAdd() { protected onAdd() {
const btn = document.createElement('button'); const btn = document.createElement('button');

View File

@ -1231,7 +1231,7 @@ const clearG2Tooltip = () => {
:font-family="fontFamily" :font-family="fontFamily"
:active="active" :active="active"
v-else-if=" v-else-if="
showChartView(ChartLibraryType.G2_PLOT, ChartLibraryType.L7_PLOT, ChartLibraryType.L7) || view.type == 'three-map' showChartView(ChartLibraryType.G2,ChartLibraryType.G2_PLOT, ChartLibraryType.L7_PLOT, ChartLibraryType.L7) || view.type == 'three-map'
" "
ref="chartComponent" ref="chartComponent"
@onChartClick="chartClick" @onChartClick="chartClick"