forked from github/dataease
Merge pull request #12106 from dataease/pr@dev-v2@feat_stack_bar_total_label
feat(图表): 堆叠柱状图支持显示总计 #10895
This commit is contained in:
commit
7c804fbd53
@ -844,6 +844,22 @@ declare interface ChartLabelAttr {
|
||||
* 转化率标签
|
||||
*/
|
||||
conversionTag: ConversionTagAtt
|
||||
/**
|
||||
* 堆叠柱状图显示总计
|
||||
*/
|
||||
showTotal: boolean
|
||||
/**
|
||||
* 总计标签字体大小
|
||||
*/
|
||||
totalFontSize: number
|
||||
/**
|
||||
* 总计标签字体颜色
|
||||
*/
|
||||
totalColor: string
|
||||
/**
|
||||
* 总计标签格式化设置
|
||||
*/
|
||||
totalFormatter: BaseFormatter
|
||||
}
|
||||
/**
|
||||
* 提示设置
|
||||
|
@ -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> </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"
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user