feat(图表): 新增进度条

This commit is contained in:
jianneng-fit2cloud 2024-05-15 17:27:03 +08:00
parent 7c76496719
commit dc2c85d7a5
5 changed files with 308 additions and 4 deletions

View File

@ -121,7 +121,8 @@ public class ChartDataManage {
List<ChartViewFieldDTO> yAxis = new ArrayList<>(view.getYAxis());
if (StringUtils.equalsIgnoreCase(view.getType(), "chart-mix")
|| StringUtils.equalsIgnoreCase(view.getType(), "bidirectional-bar")
|| StringUtils.equalsIgnoreCase(view.getType(), "quadrant")) {
|| StringUtils.equalsIgnoreCase(view.getType(), "quadrant")
|| StringUtils.containsIgnoreCase(view.getType(), "progress-bar")) {
List<ChartViewFieldDTO> yAxisExt = new ArrayList<>(view.getYAxisExt());
yAxis.addAll(yAxisExt);
}
@ -767,7 +768,8 @@ public class ChartDataManage {
|| StringUtils.equalsIgnoreCase("liquid", view.getType())) {
mapChart = ChartDataBuild.transNormalChartData(xAxis, yAxis, view, data, isDrill);
} else if (StringUtils.containsIgnoreCase(view.getType(), "chart-mix")
|| StringUtils.containsIgnoreCase(view.getType(), "bidirectional-bar")) {
|| StringUtils.containsIgnoreCase(view.getType(), "bidirectional-bar")
|| StringUtils.containsIgnoreCase(view.getType(), "progress-bar")) {
mapChart = ChartDataBuild.transMixChartDataAntV(xAxis, yAxis, view, data, isDrill);
} else if (StringUtils.containsIgnoreCase(view.getType(), "label")) {
mapChart = ChartDataBuild.transLabelChartData(xAxis, yAxis, view, data, isDrill);

View File

@ -682,6 +682,7 @@ export default {
chart_percentage_bar_stack_horizontal: '横向百分比柱状图',
chart_bar_range: '区间条形图',
chart_bidirectional_bar: '对称柱状图',
chart_progress_bar: '进度条',
chart_line: '基础折线图',
chart_area_stack: '堆叠折线图',
chart_pie: '饼图',
@ -1126,7 +1127,9 @@ export default {
top_n_desc: '合并数据',
top_n_input_1: '显示 Top',
top_n_input_2: ', 其余合并至其他',
top_n_label: '其他项名称'
top_n_label: '其他项名称',
progress_target: '目标值',
progress_current: '实际值',
},
dataset: {
scope_edit: '仅编辑时生效',

View File

@ -1212,6 +1212,13 @@ export const CHART_TYPE_CONFIGS = [
value: 'bidirectional-bar',
title: t('chart.chart_bidirectional_bar'),
icon: 'percentage-bar-stack-horizontal'
},
{
render: 'antv',
category: 'compare',
value: 'progress-bar',
title: t('chart.chart_progress_bar'),
icon: 'percentage-bar-stack-horizontal'
}
]
},

View File

@ -365,7 +365,7 @@ export class BidirectionalHorizontalBar extends G2PlotChartView<
if (l.show) {
label = {
position: l.position,
layout: [{ type: 'fixed-overlap' }],
layout: [{ type: 'limit-in-canvas' }],
style: {
fill: l.color,
fontSize: l.fontSize

View File

@ -0,0 +1,292 @@
import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot'
import { flow, hexColorToRGBA, parseJson } from '../../../util'
import { setGradientColor } from '../../common/common_antv'
import { useI18n } from '@/hooks/web/useI18n'
import { Bar as G2Progress, BarOptions } from '@antv/g2plot/esm/plots/bar'
import {
BAR_AXIS_TYPE,
BAR_EDITOR_PROPERTY_INNER
} from '@/views/chart/components/js/panel/charts/bar/common'
import { cloneDeep, defaultTo } from 'lodash-es'
import { valueFormatter } from '@/views/chart/components/js/formatter'
const { t } = useI18n()
export class ProgressBar extends G2PlotChartView<BarOptions, G2Progress> {
axisConfig = {
...this['axisConfig'],
xAxis: {
name: `${t('chart.form_type')} / ${t('chart.dimension')}`,
type: 'd',
limit: 1
},
yAxis: {
name: `${t('chart.progress_target')} / ${t('chart.quota')}`,
type: 'q',
limit: 1
},
yAxisExt: {
name: `${t('chart.progress_current')} / ${t('chart.quota')}`,
type: 'q',
limit: 1
}
}
properties: EditorProperty[] = [
'background-overall-component',
'basic-style-selector',
'label-selector',
'tooltip-selector',
'y-axis-selector',
'title-selector',
'jump-set',
'linkage'
]
propertyInner = {
...BAR_EDITOR_PROPERTY_INNER,
'legend-selector': null,
'background-overall-component': ['all'],
'basic-style-selector': ['colors', 'gradient'],
'label-selector': ['hPosition', 'color', 'fontSize'],
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter'],
'y-axis-selector': ['name', 'color', 'fontSize', 'axisForm', 'axisLabel', 'position']
}
axis: AxisType[] = [...BAR_AXIS_TYPE, 'yAxisExt']
protected baseOptions: BarOptions = {
data: [],
xField: 'progress',
yField: 'title',
seriesField: 'type',
isGroup: false,
isPercent: true,
isStack: true,
xAxis: false
}
drawChart(drawOptions: G2PlotDrawOptions<G2Progress>): G2Progress {
const { chart, container, action } = drawOptions
if (!chart.data?.data?.length) {
return
}
const getCompletionRate = (target: number, current: number) => {
if (target === 0) {
return 100
}
// 目标为正 当前为负
if (target > 0 && current < 0) {
return 0
}
// 目标为负 当前为正 正向
if ((target < 0 && current > 0) || (target < 0 && current === 0)) {
return (2 - current / target) * 100
}
// 目标与当前都为正
if (target > 0 && current > 0) {
return (current / target) * 100
}
// 目标与当前都为负 负向小于0为0
if (target < 0 && current < 0) {
const completionRate = (2 - current / target) * 100
return Math.max(completionRate, 0)
}
return 0
}
// data
const sourceData: Array<any> = cloneDeep(chart.data.data)
const data1 = defaultTo(sourceData[0]?.data, [])
const data2 = defaultTo(sourceData[1]?.data, [])
const currentData = data2.map(item => {
return {
...item,
type: 'current',
title: item.field,
id: item.quotaList[0].id,
originalValue: item.value,
progress: getCompletionRate(data1.find(i => i.field === item.field)?.value, item.value)
}
})
const targetData = data1.map(item => {
const progress = 100 - currentData.find(i => i.title === item.field)?.progress
return {
...item,
type: 'target',
title: item.field,
id: item.quotaList[0].id,
originalValue: item.value,
progress: progress
}
})
// options
const initOptions: BarOptions = {
...this.baseOptions,
data: currentData.concat(targetData).flat()
}
const options = this.setupOptions(chart, initOptions)
// 开始渲染
const newChart = new G2Progress(container, options)
newChart.on('interval:click', action)
return newChart
}
protected configBasicStyle(chart: Chart, options: BarOptions): BarOptions {
const basicStyle = parseJson(chart.customAttr).basicStyle
let color1 = basicStyle.colors?.map((ele, index) => {
if (index === 1) {
return hexColorToRGBA(ele, 10)
} else {
return ele
}
})
if (basicStyle.gradient) {
color1 = color1.map((ele, index) => {
if (index === 1) {
return ele
}
const tmp = hexColorToRGBA(ele, basicStyle.alpha)
return setGradientColor(tmp, true, 0)
})
}
options = {
...options,
color: datum => {
if (datum.type === 'target') {
return 'rgba(0, 0, 0, 0)'
}
return color1[0]
},
barBackground: {
style: {
fill: color1[1]
}
}
}
return options
}
protected configTooltip(chart: Chart, options: BarOptions): BarOptions {
const tooltipAttr = parseJson(chart.customAttr).tooltip
if (!tooltipAttr.show) {
return {
...options,
tooltip: {
showContent: false
}
}
}
const yAxis = cloneDeep(chart.yAxis)[0]
const yAxisExt = cloneDeep(chart.yAxisExt)[0]
return {
...options,
tooltip: {
showContent: true,
domStyles: {
'g2-tooltip-marker': null
},
customItems(originalItems) {
const result = []
originalItems.forEach(item => {
if (item.data) {
const value = valueFormatter(item.data.originalValue, tooltipAttr.tooltipFormatter)
if (item.data.id === yAxis.id) {
result.push({
...item,
marker: false,
name: yAxis.chartShowName ? yAxis.chartShowName : yAxis.name,
value: value
})
}
if (item.data.id === yAxisExt.id) {
result.push({
...item,
marker: false,
name: yAxisExt.chartShowName ? yAxisExt.chartShowName : yAxisExt.name,
value: value
})
}
}
})
return result.length == 0 ? originalItems : result
}
}
}
}
protected configLabel(chart: Chart, options: BarOptions): BarOptions {
const baseOptions = super.configLabel(chart, options)
if (!baseOptions.label) {
return baseOptions
}
const { label: labelAttr } = parseJson(chart.customAttr)
baseOptions.label.style.fill = labelAttr.color
const label = {
...baseOptions.label,
content: item => {
if (item.type === 'target') {
return ''
}
return (item.progress * 100).toFixed(2) + '%'
}
}
if (label.position === 'top') {
label.position = 'right'
}
return {
...baseOptions,
label
}
}
protected configYAxis(chart: Chart, options: BarOptions): BarOptions {
const baseOption = super.configYAxis(chart, options)
if (!baseOption.yAxis) {
return baseOption
}
if (baseOption.yAxis.position === 'left') {
baseOption.yAxis.position = 'bottom'
}
if (baseOption.yAxis.position === 'right') {
baseOption.yAxis.position = 'top'
}
return baseOption
}
setupDefaultOptions(chart: ChartObj): ChartObj {
chart.customStyle.yAxis = {
...chart.customStyle.yAxis,
position: 'left',
axisLine: {
show: false,
lineStyle: chart.customStyle.yAxis.axisLine.lineStyle
},
splitLine: {
show: false,
lineStyle: chart.customStyle.yAxis.axisLine.lineStyle
}
}
chart.customStyle.legend.show = false
chart.customAttr.label.show = true
chart.customAttr.label.position = 'right'
return chart
}
protected configLegend(chart: Chart, options: BarOptions): BarOptions {
const o = super.configLegend(chart, options)
return {
...o,
legend: false
}
}
protected setupOptions(chart: Chart, options: BarOptions): BarOptions {
return flow(
this.configTheme,
this.configBasicStyle,
this.configLabel,
this.configTooltip,
this.configLegend,
this.configYAxis
)(chart, options)
}
constructor() {
super('progress-bar', [])
}
}