feat(图表): 柱条图、柱线组合图增加顶部圆角选项

This commit is contained in:
jianneng-fit2cloud
2025-04-15 22:22:25 +08:00
committed by jianneng-fit2cloud
parent 0309a54849
commit 5cc6c80a57
14 changed files with 202 additions and 85 deletions

View File

@@ -1943,6 +1943,7 @@ export default {
radiusColumnBar: 'Column',
rightAngle: 'Right angle',
roundAngle: 'Rounded angle',
topRoundAngle: 'Top rounded angle',
table_layout_mode: 'Display form',
table_layout_grid: 'Tile display',
table_layout_tree: 'Tree display',

View File

@@ -1892,6 +1892,7 @@ export default {
radiusColumnBar: '柱形',
rightAngle: '直角',
roundAngle: '圓角',
topRoundAngle: '頂部圓角',
table_layout_mode: '展示形式',
table_layout_grid: '平鋪展示',
table_layout_tree: '樹形展示',

View File

@@ -1897,6 +1897,7 @@ export default {
radiusColumnBar: '柱形',
rightAngle: '直角',
roundAngle: '圆角',
topRoundAngle: '顶部圆角',
table_layout_mode: '展示形式',
table_layout_grid: '平铺展示',
table_layout_tree: '树形展示',

View File

@@ -182,9 +182,9 @@ declare interface ChartBasicStyle {
*/
barWidth: number
/**
* 柱子形状:直角|圆角
* 柱子形状:直角|圆角|顶部圆角
*/
radiusColumnBar?: 'rightAngle' | 'roundAngle'
radiusColumnBar?: 'rightAngle' | 'roundAngle' | 'topRoundAngle'
/**
* 圆角柱倒角
*/

View File

@@ -490,9 +490,11 @@ onMounted(() => {
:effect="themes"
v-model="state.basicStyleForm.radiusColumnBar"
@change="changeBasicStyle('radiusColumnBar')"
class="radius-class"
>
<el-radio label="rightAngle" :effect="themes">{{ t('chart.rightAngle') }}</el-radio>
<el-radio label="roundAngle" :effect="themes">{{ t('chart.roundAngle') }}</el-radio>
<el-radio label="topRoundAngle" :effect="themes">{{ t('chart.topRoundAngle') }}</el-radio>
</el-radio-group>
</el-form-item>
@@ -1741,4 +1743,7 @@ onMounted(() => {
flex-direction: row;
align-items: center;
}
.radius-class {
flex-wrap: nowrap !important;
}
</style>

View File

@@ -224,9 +224,13 @@ onMounted(() => {
:effect="themes"
v-model="state.basicStyleForm.radiusColumnBar"
@change="changeBasicStyle('radiusColumnBar')"
class="radius-class"
>
<el-radio label="rightAngle" :effect="themes">{{ t('chart.rightAngle') }}</el-radio>
<el-radio label="roundAngle" :effect="themes">{{ t('chart.roundAngle') }}</el-radio>
<el-radio label="topRoundAngle" :effect="themes">{{
t('chart.topRoundAngle')
}}</el-radio>
</el-radio-group>
</el-form-item>
<div class="alpha-setting" v-if="showProperty('columnWidthRatio')">
@@ -552,4 +556,10 @@ onMounted(() => {
border-top: none !important;
}
}
.radius-class {
flex-wrap: nowrap !important;
:deep(.ed-radio) {
margin-right: 30px !important;
}
}
</style>

View File

@@ -7,7 +7,6 @@ import {
import {
flow,
hexColorToRGBA,
hexToRgba,
parseJson,
setUpGroupSeriesColor,
setUpStackSeriesColor
@@ -21,6 +20,7 @@ import {
} from '@/views/chart/components/js/panel/charts/bar/common'
import {
configPlotTooltipEvent,
configRadius,
getLabel,
getPadding,
getTooltipContainer,
@@ -181,19 +181,9 @@ export class Bar extends G2PlotChartView<ColumnOptions, Column> {
color
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const columnStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
columnStyle
}
options = {
...options,
...configRadius(options.data, basicStyle, 'columnStyle', options.xField, options.seriesField)
}
let columnWidthRatio
const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio

View File

@@ -12,8 +12,7 @@ import {
getYAxis,
getYAxisExt,
setGradientColor,
TOOLTIP_TPL,
addConditionsStyleColorToData
TOOLTIP_TPL
} from '@/views/chart/components/js/panel/common/common_antv'
import type {
BidirectionalBar as G2BidirectionalBar,
@@ -213,18 +212,16 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
...options,
layout: basicStyle.layout
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
if (['roundAngle', 'topRoundAngle'].includes(basicStyle.radiusColumnBar)) {
const valueField = basicStyle.layout === 'vertical' ? 'valueExt' : 'value'
const radius = Array(basicStyle.radiusColumnBar === 'roundAngle' ? 4 : 2).fill(
basicStyle.columnBarRightAngleRadius
)
options = {
...options,
barStyle
barStyle: datum => ({
radius: datum[valueField] && radius.length === 2 ? [0, 0, ...radius] : radius
})
}
}
return options

View File

@@ -140,23 +140,42 @@ export class BulletGraph extends G2PlotChartView<G2BulletOptions, G2Bullet> {
const { radiusColumnBar, columnBarRightAngleRadius, layout } = basicStyle
let radiusValue = 0
let rangeLength = 1
if (radiusColumnBar === 'roundAngle') {
if (radiusColumnBar === 'roundAngle' || radiusColumnBar === 'topRoundAngle') {
radiusValue = columnBarRightAngleRadius
rangeLength = options.data[0]?.ranges?.length
}
const barRadiusStyle = { radius: Array(4).fill(radiusValue) }
const barRadiusStyle = { radius: Array(2).fill(radiusValue) }
const baseRadius = [...barRadiusStyle.radius, ...barRadiusStyle.radius]
options = {
...options,
bulletStyle: {
range: datum => {
if (!datum.rKey) return { fill: 'rgba(0, 0, 0, 0)' }
if (rangeLength === 1) return barRadiusStyle
if (rangeLength > 1 && datum.rKey === 'ranges_0')
return { radius: [0, 0, radiusValue, radiusValue] }
if (rangeLength > 1 && datum.rKey === 'ranges_' + (rangeLength - 1))
return { radius: [radiusValue, radiusValue, 0, 0] }
if (rangeLength === 1) {
return {
radius:
radiusColumnBar === 'topRoundAngle' ? [...barRadiusStyle.radius, 0, 0] : baseRadius
}
}
if (rangeLength > 1 && datum.rKey === 'ranges_0') {
return {
radius: radiusColumnBar === 'topRoundAngle' ? [] : [0, 0, ...barRadiusStyle.radius]
}
}
if (rangeLength > 1 && datum.rKey === 'ranges_' + (rangeLength - 1)) {
return { radius: [...barRadiusStyle.radius, 0, 0] }
}
},
measure: datum => {
if (datum.measures) {
return {
radius:
radiusColumnBar === 'topRoundAngle' ? [...barRadiusStyle.radius, 0, 0] : baseRadius
}
} else {
return undefined
}
},
measure: datum => (datum.measures ? barRadiusStyle : undefined),
target: datum => (datum.tKey === 'target' ? { lineWidth: 2 } : undefined)
}
}

View File

@@ -6,6 +6,7 @@ import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar'
import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
configRadius,
getPadding,
getTooltipContainer,
setGradientColor,
@@ -171,19 +172,16 @@ export class HorizontalBar extends G2PlotChartView<BarOptions, Bar> {
color
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
options = {
...options,
barStyle
}
options = {
...options,
...configRadius(
options.data,
basicStyle,
'barStyle',
options.yField,
options.seriesField,
true
)
}
let barWidthRatio

View File

@@ -4,7 +4,6 @@ import {
configAxisLabelLengthLimit,
configPlotTooltipEvent,
getTooltipContainer,
getTooltipItemConditionColor,
setGradientColor,
TOOLTIP_TPL
} from '../../common/common_antv'
@@ -184,18 +183,13 @@ export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
}
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
if (['roundAngle', 'topRoundAngle'].includes(basicStyle.radiusColumnBar)) {
const radius = Array(basicStyle.radiusColumnBar === 'roundAngle' ? 4 : 2).fill(
basicStyle.columnBarRightAngleRadius
)
options = {
...options,
barStyle
barStyle: { radius }
}
}

View File

@@ -321,18 +321,24 @@ export class RangeBar extends G2PlotChartView<BarOptions, Bar> {
}
}
}
if (basicStyle.radiusColumnBar === 'roundAngle') {
const barStyle = {
radius: [
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius,
basicStyle.columnBarRightAngleRadius
]
}
if (['roundAngle', 'topRoundAngle'].includes(basicStyle.radiusColumnBar)) {
const radius = Array(2).fill(basicStyle.columnBarRightAngleRadius)
options = {
...options,
barStyle
barStyle: datum => {
const isTopRound = basicStyle.radiusColumnBar === 'topRoundAngle'
const baseRadius = [...radius, ...radius]
return {
radius:
datum.values[0] < datum.values[1]
? isTopRound
? [...radius, 0, 0]
: baseRadius
: isTopRound
? [0, 0, ...radius]
: baseRadius
}
}
}
}
let barWidthRatio

View File

@@ -120,10 +120,27 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
valueExt: d.value
}
})
// column数据分组
const groupedByField = data1.reduce((acc, item) => {
const groupField = isGroup ? `${item.field}-${item.category}` : item.field
if (!acc[groupField]) {
acc[groupField] = []
}
acc[groupField].push(item)
return acc
}, {})
// 遍历每个分组,添加 isFirst 和 isLast 属性
Object.values(groupedByField).forEach(group => {
const firstItem = group[0]
const lastItem = group[group.length - 1]
firstItem.isFirst = true
lastItem.isLast = true
})
// 将分组后的数据重新展开为一个数组
const result = Object.values(groupedByField).flat()
// options
const initOptions: DualAxesOptions = {
data: [data1, data2],
data: [result, data2],
xField: 'field',
yField: ['value', 'valueExt'], //这里不能设置成一样的
appendPadding: getPadding(chart),
@@ -134,7 +151,8 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
color: [],
isGroup: isGroup,
isStack: isStack,
seriesField: seriesField
seriesField: seriesField,
rawFields: ['isFirst', 'isLast']
},
{
geometry: data2Type,
@@ -298,17 +316,20 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
tempOption.geometryOptions[1].point = point
tempOption.geometryOptions[1].lineStyle = lineStyle
if (s.radiusColumnBar === 'roundAngle') {
const columnStyle = {
radius: [
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius,
s.columnBarRightAngleRadius
]
if (['roundAngle', 'topRoundAngle'].includes(s.radiusColumnBar)) {
const radius = Array(2).fill(s.columnBarRightAngleRadius)
const isTopRound = s.radiusColumnBar === 'topRoundAngle'
tempOption.geometryOptions[0].columnStyle = datum => {
if (isTopRound && datum.isFirst && datum.isLast) {
return { radius }
}
if (!isTopRound && datum.isFirst && datum.isLast) {
return { radius: [...radius, ...radius] }
}
if (datum.isFirst || (!isTopRound && datum.isLast)) {
return { radius: datum.isLast ? [0, 0, ...radius] : radius }
}
}
tempOption.geometryOptions[0].columnStyle = columnStyle
tempOption.geometryOptions[1].columnStyle = columnStyle
}
}

View File

@@ -33,7 +33,7 @@ import { PositionType } from '@antv/l7-core'
import { centroid } from '@turf/centroid'
import type { Plot } from '@antv/g2plot'
import type { PickOptions } from '@antv/g2plot/lib/core/plot'
import { defaults, find } from 'lodash-es'
import { defaults, find, groupBy, map, uniq } from 'lodash-es'
import { useI18n } from '@/hooks/web/useI18n'
import { isMobile } from '@/utils/utils'
import { GaodeMap, TMap, TencentMap } from '@antv/l7-maps'
@@ -2205,7 +2205,7 @@ const getColorByConditions = (quotaList: [], values: number | number[], chart) =
* @param chart
* @param options
*/
export function handleConditionsStyle(chart: Chart, options: O) {
export function handleConditionsStyle(chart: Chart, options) {
const { threshold } = parseJson(chart.senior)
if (!threshold.enable) return options
const { basicStyle } = parseJson(chart.customAttr)
@@ -2394,3 +2394,77 @@ export const numberToChineseUnderHundred = (num: number): string => {
// 处理其他两位数
return tens === 1 ? '十' + digits[ones] : digits[tens] + '十' + digits[ones]
}
/**
* 配置柱条图的圆角
* @param data
* @param basicStyle
* @param styleName
* @param xField
* @param seriesField
* @param isHorizontalBar 条形图
*/
export const configRadius = (
data: Record<string, any>[],
basicStyle: DeepPartial<ChartBasicStyle>,
styleName: string,
xField: string,
seriesField: string,
isHorizontalBar?: boolean
) => {
if (['roundAngle', 'topRoundAngle'].includes(basicStyle.radiusColumnBar)) {
// 根据维度分组
const grouped = groupBy(data, xField)
// 分组下的堆叠项
const result = map(grouped, (items, field) => ({
field,
seriesFields: uniq(map(items, seriesField))
}))
const radius = Array(2).fill(basicStyle.columnBarRightAngleRadius)
// 分组下堆叠项索引
const groupIndex = new Map()
// 配置柱条样式
const style = datum => {
const group = result.find(item => item.field === datum[xField])
// 未找到分组直接返回
if (!group) return { radius }
// 分组下堆叠项索引,每遇到一个索引+1
if (groupIndex.has(group.field)) {
groupIndex.set(group.field, groupIndex.get(group.field) + 1)
} else {
groupIndex.set(group.field, 0)
}
// 分组下堆叠项长度
const seriesFieldLength = group.seriesFields.length
// 只有一个柱子时
if (seriesFieldLength === 1) {
return {
radius: [
...radius,
...(seriesFieldLength === 1 && basicStyle.radiusColumnBar === 'topRoundAngle'
? [0, 0]
: radius)
]
}
}
// 获取当前分组的索引
const groupIdx = groupIndex.get(group.field)
// 判断是否为第一个或最后一个堆叠项
const isStart = isHorizontalBar ? groupIdx === seriesFieldLength - 1 : groupIdx === 0
const isEnd = isHorizontalBar ? groupIdx === 0 : groupIdx === seriesFieldLength - 1
return {
radius: isStart
? [...radius, 0, 0]
: isEnd
? basicStyle.radiusColumnBar === 'topRoundAngle'
? []
: [0, 0, ...radius]
: []
}
}
return {
[styleName]: style
}
}
return {}
}