feat(图表): 堆叠柱状图支持显示总计 #10895

This commit is contained in:
wisonic 2024-09-09 19:41:57 +08:00
parent c212b39e97
commit bc16a24d72
5 changed files with 233 additions and 9 deletions

View File

@ -844,6 +844,22 @@ declare interface ChartLabelAttr {
* 转化率标签
*/
conversionTag: ConversionTagAtt
/**
* 堆叠柱状图显示总计
*/
showTotal: boolean
/**
* 总计标签字体大小
*/
totalFontSize: number
/**
* 总计标签字体颜色
*/
totalColor: string
/**
* 总计标签格式化设置
*/
totalFormatter: BaseFormatter
}
/**
* 提示设置

View File

@ -220,12 +220,13 @@ const COMPUTED_DEFAULT_LABEL = computed(() => {
return DEFAULT_LABEL
})
const state = reactive<{ labelForm: ChartLabelAttr | any }>({
const state = reactive<{ labelForm: DeepPartial<ChartLabelAttr> }>({
labelForm: {
quotaLabelFormatter: DEFAULT_LABEL.quotaLabelFormatter,
seriesLabelFormatter: [],
labelFormatter: DEFAULT_LABEL.labelFormatter,
conversionTag: DEFAULT_LABEL.conversionTag
conversionTag: DEFAULT_LABEL.conversionTag,
totalFormatter: DEFAULT_LABEL.totalFormatter
}
})
@ -662,6 +663,149 @@ const conversionPrecision = [
/>
</el-form-item>
</template>
<el-form-item v-if="showProperty('showTotal')" class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.labelForm.showTotal"
@change="changeLabelAttr('showTotal')"
:label="t('chart.total_show')"
/>
</el-form-item>
<template v-if="false && showProperty('totalFormatter')">
<el-divider class="m-divider" :class="{ 'divider-dark': themes === 'dark' }" />
<div v-show="state.labelForm.showTotal">
<el-space>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('totalColor')"
:label="t('chart.text')"
>
<el-color-picker
:effect="themes"
v-model="state.labelForm.totalColor"
class="color-picker-style"
:predefine="COLOR_PANEL"
@change="changeLabelAttr('totalColor')"
is-custom
/>
</el-form-item>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('totalFontSize')"
>
<template #label>&nbsp;</template>
<el-tooltip content="字号" :effect="toolTip" placement="top">
<el-select
size="small"
style="width: 108px"
:effect="themes"
v-model.number="state.labelForm.totalFontSize"
:placeholder="t('chart.text_fontsize')"
@change="changeLabelAttr('totalFontSize')"
>
<el-option
v-for="option in fontSizeList"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-tooltip>
</el-form-item>
</el-space>
<el-form-item
:label="$t('chart.value_formatter_type')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
size="small"
:effect="themes"
v-model="state.labelForm.totalFormatter.type"
@change="changeLabelAttr('totalFormatter.type')"
>
<el-option
v-for="type in formatterType"
:key="type.value"
:label="$t('chart.' + type.name)"
:value="type.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="state.labelForm.totalFormatter && state.labelForm.totalFormatter.type !== 'auto'"
:label="$t('chart.value_formatter_decimal_count')"
class="form-item"
:class="'form-item-' + themes"
>
<el-input-number
controls-position="right"
:effect="themes"
v-model="state.labelForm.totalFormatter.decimalCount"
:precision="0"
:min="0"
:max="10"
@change="changeLabelAttr('totalFormatter.decimalCount')"
/>
</el-form-item>
<el-row
:gutter="8"
v-if="state.labelForm.totalFormatter && state.labelForm.totalFormatter.type !== 'percent'"
>
<el-col :span="12">
<el-form-item
:label="$t('chart.value_formatter_unit')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
size="small"
:effect="themes"
v-model="state.labelForm.totalFormatter.unit"
:placeholder="$t('chart.pls_select_field')"
@change="changeLabelAttr('totalFormatter.unit')"
>
<el-option
v-for="item in unitType"
:key="item.value"
:label="$t('chart.' + item.name)"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="$t('chart.value_formatter_suffix')"
class="form-item"
:class="'form-item-' + themes"
>
<el-input
:effect="themes"
v-model="state.labelForm.totalFormatter.suffix"
clearable
:placeholder="$t('commons.input_content')"
@change="changeLabelAttr('totalFormatter.suffix')"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.labelForm.totalFormatter.thousandSeparator"
@change="changeLabelAttr('totalFormatter.thousandSeparator')"
:label="t('chart.value_formatter_thousand_separator')"
/>
</el-form-item>
</div>
</template>
<el-form-item
class="form-item"

View File

@ -335,7 +335,11 @@ export const DEFAULT_LABEL: ChartLabelAttr = {
show: false,
precision: 2,
text: '转化率'
}
},
showTotal: false,
totalFontSize: 12,
totalColor: '#FFF',
totalFormatter: formatterItem
}
export const DEFAULT_TOOLTIP: ChartTooltipAttr = {
show: true,

View File

@ -1,5 +1,5 @@
import type { Column, ColumnOptions } from '@antv/g2plot/esm/plots/column'
import { cloneDeep, isEmpty } from 'lodash-es'
import { cloneDeep, each, groupBy, isEmpty } from 'lodash-es'
import {
G2PlotChartView,
G2PlotDrawOptions
@ -12,7 +12,7 @@ import {
setUpStackSeriesColor
} from '@/views/chart/components/js/util'
import type { Datum } from '@antv/g2plot'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter'
import {
BAR_AXIS_TYPE,
BAR_EDITOR_PROPERTY,
@ -264,7 +264,14 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
export class StackBar extends Bar {
propertyInner = {
...this['propertyInner'],
'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition'],
'label-selector': [
...BAR_EDITOR_PROPERTY_INNER['label-selector'],
'vPosition',
'showTotal',
'totalColor',
'totalFontSize',
'totalFormatter'
],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show']
}
protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
@ -340,13 +347,58 @@ export class StackBar extends Bar {
return options
}
protected configTotalLabel(chart: Chart, options: ColumnOptions): ColumnOptions {
if (!options.label) {
return options
}
const { label } = parseJson(chart.customAttr)
if (label.showTotal) {
const formatterCfg = label.labelFormatter ?? formatterItem
each(groupBy(options.data, 'field'), (values, key) => {
const total = values.reduce((a, b) => a + b.value, 0)
const value = valueFormatter(total, formatterCfg)
if (!options.annotations) {
options = {
...options,
annotations: []
}
}
options.annotations.push({
type: 'text',
position: [key, total],
content: `${value}`,
style: {
textAlign: 'center',
fontSize: label.fontSize,
fill: label.color
},
offsetY: -(parseInt(label.fontSize as unknown as string) / 2)
})
})
}
return options
}
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
return setUpStackSeriesColor(chart, data)
}
protected setupOptions(chart: Chart, options: ColumnOptions): ColumnOptions {
const tmp = super.setupOptions(chart, options)
return flow(this.configData)(chart, tmp, {}, this)
return flow(
this.configTheme,
this.configEmptyDataStrategy,
this.configColor,
this.configBasicStyle,
this.configLabel,
this.configTooltip,
this.configLegend,
this.configXAxis,
this.configYAxis,
this.configSlider,
this.configAnalyse,
this.configData,
this.configTotalLabel
)(chart, options, {}, this)
}
constructor(name = 'bar-stack') {
@ -439,6 +491,10 @@ export class GroupBar extends StackBar {
* 分组堆叠柱状图
*/
export class GroupStackBar extends StackBar {
propertyInner = {
...this['propertyInner'],
'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition']
}
protected configTheme(chart: Chart, options: ColumnOptions): ColumnOptions {
const baseOptions = super.configTheme(chart, options)
const baseTheme = baseOptions.theme as object

View File

@ -27,6 +27,7 @@ import {
handleEmptyDataStrategy,
setupSeriesColor
} from '../../../util'
import { Options } from '@antv/g2plot'
export interface G2PlotDrawOptions<O> extends AntVDrawOptions<O> {
/**
@ -127,7 +128,10 @@ export abstract class G2PlotChartView<
protected configAnalyse(chart: Chart, options: O): O {
const annotations = getAnalyse(chart)
return { ...options, annotations }
return {
...options,
annotations: [...annotations, ...((options as unknown as Options).annotations || [])]
}
}
protected configAnalyseHorizontal(chart: Chart, options: O): O {