Merge pull request #12160 from ulleo/dev-v2

feat(图表): 支持双轴折线图
This commit is contained in:
ulleo 2024-09-11 18:16:35 +08:00 committed by GitHub
commit 18be831a7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 274 additions and 40 deletions

View File

@ -0,0 +1,11 @@
package io.dataease.chart.charts.impl.mix;
import lombok.Getter;
import org.springframework.stereotype.Component;
@Component
public class DualLineMixHandler extends GroupMixHandler {
@Getter
private final String type = "chart-mix-dual-line";
}

View File

@ -1,13 +1,10 @@
package io.dataease.chart.charts.impl.mix;
import io.dataease.chart.utils.ChartDataBuild;
import io.dataease.extensions.view.dto.*;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class GroupMixHandler extends MixHandler {

View File

@ -7,7 +7,6 @@ import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class StackMixHandler extends MixHandler {

View File

@ -1027,6 +1027,7 @@ export default {
chart_mix: '柱线组合图',
chart_mix_group_column: '分组柱线组合图',
chart_mix_stack_column: '堆叠柱线组合图',
chart_mix_dual_line: '双线组合图',
axis_value: '轴值',
axis_value_min: '最小值',
axis_value_max: '最大值',

View File

@ -141,20 +141,22 @@ onMounted(() => {
/>
</template>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('gradient')"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.basicStyleForm.gradient"
@change="changeBasicStyle('gradient')"
<template v-if="chart.type !== 'chart-mix-dual-line'">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('gradient')"
>
{{ $t('chart.gradient') }}{{ $t('chart.color') }}
</el-checkbox>
</el-form-item>
<el-checkbox
size="small"
:effect="themes"
v-model="state.basicStyleForm.gradient"
@change="changeBasicStyle('gradient')"
>
{{ $t('chart.gradient') }}{{ $t('chart.color') }}
</el-checkbox>
</el-form-item>
</template>
<div class="alpha-setting" v-if="showProperty('alpha')">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
@ -189,22 +191,100 @@ onMounted(() => {
</el-row>
</div>
<el-form-item
class="form-item"
v-if="showProperty('radiusColumnBar')"
:label="t('chart.radiusColumnBar')"
:class="'form-item-' + themes"
>
<el-radio-group
size="small"
:effect="themes"
v-model="state.basicStyleForm.radiusColumnBar"
@change="changeBasicStyle('radiusColumnBar')"
<template v-if="chart.type !== 'chart-mix-dual-line'">
<el-form-item
class="form-item"
v-if="showProperty('radiusColumnBar')"
:label="t('chart.radiusColumnBar')"
:class="'form-item-' + themes"
>
<el-radio label="rightAngle" :effect="themes">{{ t('chart.rightAngle') }}</el-radio>
<el-radio label="roundAngle" :effect="themes">{{ t('chart.roundAngle') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-radio-group
size="small"
:effect="themes"
v-model="state.basicStyleForm.radiusColumnBar"
@change="changeBasicStyle('radiusColumnBar')"
>
<el-radio label="rightAngle" :effect="themes">{{ t('chart.rightAngle') }}</el-radio>
<el-radio label="roundAngle" :effect="themes">{{ t('chart.roundAngle') }}</el-radio>
</el-radio-group>
</el-form-item>
</template>
<tempalte v-else>
<el-row :gutter="8">
<el-col :span="12">
<el-form-item
:label="t('chart.line_width')"
class="form-item form-item-slider"
:class="'form-item-' + themes"
v-if="showProperty('lineWidth')"
>
<el-input-number
:effect="themes"
v-model="state.basicStyleForm.leftLineWidth"
:min="0"
:max="10"
controls-position="right"
@change="changeBasicStyle('lineWidth')"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="8">
<el-col :span="12">
<el-form-item
:label="t('chart.line_symbol')"
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('lineSymbol')"
>
<el-select
:effect="themes"
v-model="state.basicStyleForm.leftLineSymbol"
:placeholder="t('chart.line_symbol')"
@change="changeBasicStyle('lineSymbol')"
>
<el-option
v-for="item in symbolOptions"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="t('chart.line_symbol_size')"
class="form-item form-item-slider"
:class="'form-item-' + themes"
v-if="showProperty('lineSymbolSize')"
>
<el-input-number
:effect="themes"
v-model="state.basicStyleForm.leftLineSymbolSize"
:min="0"
:max="20"
controls-position="right"
@change="changeBasicStyle('lineSymbolSize')"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('lineSmooth')"
>
<el-checkbox
size="small"
:effect="themes"
v-model="state.basicStyleForm.leftLineSmooth"
@change="changeBasicStyle('lineSmooth')"
>
{{ t('chart.line_smooth') }}
</el-checkbox>
</el-form-item>
</tempalte>
</el-tab-pane>
<el-tab-pane :label="t('chart.yAxisRight')" name="right">
<template v-if="showProperty('colors')">

View File

@ -1482,6 +1482,13 @@ export const CHART_TYPE_CONFIGS = [
value: 'chart-mix-stack',
title: t('chart.chart_mix_stack_column'),
icon: 'chart-mix-stack'
},
{
render: 'antv',
category: 'dual_axes',
value: 'chart-mix-dual-line',
title: t('chart.chart_mix_dual_line'),
icon: 'line'
}
]
},

View File

@ -91,7 +91,11 @@ export const CHART_MIX_DEFAULT_BASIC_STYLE = {
'#00ccdf',
'#00c039',
'#ff7701'
]
],
leftLineWidth: 2,
leftLineSymbol: 'circle',
leftLineSymbolSize: 4,
leftLineSmooth: true
}
export interface MixChartBasicStyle extends ChartBasicStyle {

View File

@ -66,6 +66,14 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
type: 'q'
}
}
protected getLeftType(): string {
return 'column'
}
protected getRightType(): string {
return 'line'
}
async drawChart(drawOptions: G2PlotDrawOptions<DualAxes>): Promise<DualAxes> {
const { chart, action, container } = drawOptions
if (!chart.data?.left?.data?.length && !chart.data?.right?.data?.length) {
@ -76,8 +84,8 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
// const data1Type = (left[0]?.type === 'bar' ? 'column' : left[0]?.type) ?? 'column'
// const data2Type = (right[0]?.type === 'bar' ? 'column' : right[0]?.type) ?? 'column'
const data1Type = 'column'
const data2Type = 'line'
const data1Type = this.getLeftType()
const data2Type = this.getRightType()
const isGroup = this.name === 'chart-mix-group' && chart.xAxisExt?.length > 0
const isStack = this.name === 'chart-mix-stack' && chart.extStack?.length > 0
@ -225,7 +233,10 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
protected configBasicStyle(chart: Chart, options: DualAxesOptions): DualAxesOptions {
// size
const customAttr: DeepPartial<ChartAttr> = parseJson(chart.customAttr)
const s = JSON.parse(JSON.stringify(customAttr.basicStyle))
const s = defaultsDeep(
JSON.parse(JSON.stringify(customAttr.basicStyle)),
CHART_MIX_DEFAULT_BASIC_STYLE
)
const smooth = s.lineSmooth
const point = {
size: s.lineSymbolSize,
@ -234,6 +245,14 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
const lineStyle = {
lineWidth: s.lineWidth
}
const leftSmooth = s.leftLineSmooth
const leftPoint = {
size: s.leftLineSymbolSize,
shape: s.leftLineSymbol
}
const leftLineStyle = {
lineWidth: s.leftLineWidth
}
const tempOption = {
...options,
smooth,
@ -241,9 +260,9 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
lineStyle
}
if (tempOption.geometryOptions) {
tempOption.geometryOptions[0].smooth = smooth
tempOption.geometryOptions[0].point = point
tempOption.geometryOptions[0].lineStyle = lineStyle
tempOption.geometryOptions[0].smooth = leftSmooth
tempOption.geometryOptions[0].point = leftPoint
tempOption.geometryOptions[0].lineStyle = leftLineStyle
tempOption.geometryOptions[1].smooth = smooth
tempOption.geometryOptions[1].point = point
@ -815,3 +834,119 @@ export class StackColumnLineMix extends ColumnLineMix {
super(name)
}
}
export class DualLineMix extends ColumnLineMix {
axis: AxisType[] = [...this['axis'], 'xAxisExt']
propertyInner = {
...CHART_MIX_EDITOR_PROPERTY_INNER,
'label-selector': ['seriesLabelFormatter'],
'tooltip-selector': [
...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'],
'seriesTooltipFormatter'
]
}
axisConfig = {
...this['axisConfig'],
xAxisExt: {
name: `${t('chart.drag_block_type_axis_left')} / ${t('chart.dimension')}`,
type: 'd',
limit: 1
}
}
protected getLeftType(): string {
return 'line'
}
protected configCustomColors(chart: Chart, options: DualAxesOptions): DualAxesOptions {
const tempOption = {
...options
}
const basicStyle = parseJson(chart.customAttr).basicStyle as MixChartBasicStyle
const { seriesColor } = basicStyle
if (seriesColor?.length) {
const seriesMap = seriesColor.reduce((p, n) => {
p[n.id] = n
return p
}, {})
const { yAxis, extStack } = chart
const { data } = options as unknown as Options
if (extStack?.length) {
const seriesSet = new Set()
data[0]?.forEach(d => d.category !== null && seriesSet.add(d.category))
const tmp = [...seriesSet]
tmp.forEach((c, i) => {
const curAxisColor = seriesMap[c as string]
if (curAxisColor) {
if (i + 1 > basicStyle.colors.length) {
basicStyle.colors.push(curAxisColor.color)
} else {
basicStyle.colors[i] = curAxisColor.color
}
}
})
} else {
yAxis?.forEach((axis, index) => {
const curAxisColor = seriesMap[axis.id]
if (curAxisColor) {
if (index + 1 > basicStyle.colors.length) {
basicStyle.colors.push(curAxisColor.color)
} else {
basicStyle.colors[index] = curAxisColor.color
}
}
})
}
}
//左轴
const color = basicStyle.colors.map(ele => {
const tmp = hexColorToRGBA(ele, basicStyle.alpha)
if (basicStyle.gradient) {
return setGradientColor(tmp, true, 270)
} else {
return tmp
}
})
tempOption.geometryOptions[0].color = color
return tempOption
}
public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
const result: ChartBasicStyle['seriesColor'] = []
const seriesSet = new Set<string>()
const colors = chart.customAttr.basicStyle.colors
const { yAxis, extStack } = chart
if (extStack?.length) {
data?.forEach(d => {
if (d.value === null || d.category === null || seriesSet.has(d.category)) {
return
}
seriesSet.add(d.category)
result.push({
id: d.category,
name: d.category,
color: colors[(seriesSet.size - 1) % colors.length]
})
})
} else {
yAxis?.forEach(axis => {
if (seriesSet.has(axis.id)) {
return
}
seriesSet.add(axis.id)
result.push({
id: axis.id,
name: axis.chartShowName ?? axis.name,
color: colors[(seriesSet.size - 1) % colors.length]
})
})
}
return result
}
constructor(name = 'chart-mix-dual-line') {
super(name)
}
}

@ -1 +1 @@
Subproject commit 8ec01af68b38419a065d57f80f915b903ddc00bb
Subproject commit b6142ad74ae10509990650cc6148277983b7e827