fix(图表): 线面图空值处理失效

This commit is contained in:
wisonic-s
2025-09-22 17:58:23 +08:00
committed by wisonic-s
parent 5b4e7730ef
commit bf4c54ef97
5 changed files with 138 additions and 250 deletions

View File

@@ -10,8 +10,8 @@ import { useEmitt } from '@/hooks/web/useEmitt'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import chartViewManager from '../../../js/panel'
import { G2PlotChartView } from '../../../js/panel/types/impl/g2plot'
import { cloneDeep } from 'lodash-es'
import { type G2ChartView } from '../../../js/panel/types/impl/g2'
const { t } = useI18n()
@@ -54,7 +54,7 @@ const seriesColorState = reactive({
seriesColorPickerId: 'body'
})
const instance = ref<G2PlotChartView | undefined>()
const instance = ref<G2ChartView | undefined>()
const colorsName = computed(() => {
return props.sub ? 'subColors' : 'colors'
@@ -85,7 +85,7 @@ const setupSeriesColor = () => {
instance.value = chartViewManager.getChartView(
props.chart.render,
props.chart.type
) as G2PlotChartView
) as G2ChartView
if (!props.sub) {
if (!needSetSeriesColor.value) {

View File

@@ -1,4 +1,10 @@
import { parseJson } from '@/views/chart/components/js/util'
import {
handleBreakLineMultiDimension,
handleIgnoreData,
handleSetZeroMultiDimension,
handleSetZeroSingleDimension,
parseJson
} from '@/views/chart/components/js/util'
import { Chart as G2Chart, G2Spec } from '@antv/g2'
export type ViewSpec = { children?: G2Spec[]; [key: string]: any } & G2Spec
@@ -9,70 +15,30 @@ export type Transform = {
export function handleEmptyDataStrategy<O extends ViewSpec>(chart: Chart, options: O): O {
const { data } = options.children[0]
const isChartMix = chart.type.includes('chart-mix')
if (!data?.length) {
return options
}
const strategy = parseJson(chart.senior).functionCfg.emptyDataStrategy
if (strategy === 'ignoreData') {
if (isChartMix) {
for (let i = 0; i < data.length; i++) {
handleIgnoreData(data[i] as Record<string, any>[])
}
} else {
handleIgnoreData(data)
}
handleIgnoreData(data)
return options
}
const { yAxis, xAxisExt, extStack, extBubble } = chart
const { yAxis, xAxisExt, extStack } = chart
const multiDimension = yAxis?.length >= 2 || xAxisExt?.length > 0 || extStack?.length > 0
switch (strategy) {
case 'breakLine': {
if (isChartMix) {
if (data[0]) {
if (xAxisExt?.length > 0 || extStack?.length > 0) {
handleBreakLineMultiDimension(data[0] as Record<string, any>[])
}
}
if (data[1]) {
if (extBubble?.length > 0) {
handleBreakLineMultiDimension(data[1] as Record<string, any>[])
}
}
} else {
if (multiDimension) {
handleBreakLineMultiDimension(data)
}
}
return {
...options,
connectNulls: false
if (multiDimension) {
handleBreakLineMultiDimension(data)
}
break
}
case 'setZero': {
if (isChartMix) {
if (data[0]) {
if (xAxisExt?.length > 0 || extStack?.length > 0) {
handleSetZeroMultiDimension(data[0] as Record<string, any>[])
} else {
handleSetZeroSingleDimension(data[0] as Record<string, any>[])
}
}
if (data[1]) {
if (extBubble?.length > 0) {
handleSetZeroMultiDimension(data[1] as Record<string, any>[], true)
} else {
handleSetZeroSingleDimension(data[1] as Record<string, any>[], true)
}
}
if (multiDimension) {
// 多维度置0
handleSetZeroMultiDimension(data)
} else {
if (multiDimension) {
// 多维度置0
handleSetZeroMultiDimension(data)
} else {
// 单维度置0
handleSetZeroSingleDimension(data)
}
// 单维度置0
handleSetZeroSingleDimension(data)
}
break
}
@@ -80,109 +46,6 @@ export function handleEmptyDataStrategy<O extends ViewSpec>(chart: Chart, option
return options
}
function handleBreakLineMultiDimension(data) {
const dimensionInfoMap = new Map()
const subDimensionSet = new Set()
const quotaMap = new Map<string, { id: string }[]>()
for (let i = 0; i < data.length; i++) {
const item = data[i]
const dimensionInfo = dimensionInfoMap.get(item.field)
if (dimensionInfo) {
dimensionInfo.set.add(item.category)
} else {
dimensionInfoMap.set(item.field, { set: new Set([item.category]), index: i })
}
subDimensionSet.add(item.category)
quotaMap.set(item.category, item.quotaList)
}
// Map 是按照插入顺序排序的,所以插入索引往后推
let insertCount = 0
dimensionInfoMap.forEach((dimensionInfo, field) => {
if (dimensionInfo.set.size < subDimensionSet.size) {
let subInsertIndex = 0
subDimensionSet.forEach(dimension => {
if (!dimensionInfo.set.has(dimension)) {
data.splice(dimensionInfo.index + insertCount + subInsertIndex, 0, {
field,
value: null,
category: dimension,
quotaList: quotaMap.get(dimension as string)
})
}
subInsertIndex++
})
insertCount += subDimensionSet.size - dimensionInfo.set.size
}
})
}
function handleSetZeroMultiDimension(data: Record<string, any>[], isExt = false) {
const dimensionInfoMap = new Map()
const subDimensionSet = new Set()
const quotaMap = new Map<string, { id: string }[]>()
for (let i = 0; i < data.length; i++) {
const item = data[i]
if (item.value === null) {
item.value = 0
if (isExt) {
item.valueExt = 0
}
}
const dimensionInfo = dimensionInfoMap.get(item.field)
if (dimensionInfo) {
dimensionInfo.set.add(item.category)
} else {
dimensionInfoMap.set(item.field, { set: new Set([item.category]), index: i })
}
subDimensionSet.add(item.category)
quotaMap.set(item.category, item.quotaList)
}
let insertCount = 0
dimensionInfoMap.forEach((dimensionInfo, field) => {
if (dimensionInfo.set.size < subDimensionSet.size) {
let subInsertIndex = 0
subDimensionSet.forEach(dimension => {
if (!dimensionInfo.set.has(dimension)) {
const _temp = {
field,
value: 0,
category: dimension,
quotaList: quotaMap.get(dimension as string)
} as any
if (isExt) {
_temp.valueExt = 0
}
data.splice(dimensionInfo.index + insertCount + subInsertIndex, 0, _temp)
}
subInsertIndex++
})
insertCount += subDimensionSet.size - dimensionInfo.set.size
}
})
}
function handleSetZeroSingleDimension(data: Record<string, any>[], isExt = false) {
data.forEach(item => {
if (item.value === null) {
if (!isExt) {
item.value = 0
} else {
item.valueExt = 0
}
}
})
}
function handleIgnoreData(data: Record<string, any>[]) {
for (let i = data.length - 1; i >= 0; i--) {
const item = data[i]
if (item.value === null) {
data.splice(i, 1)
}
}
}
export function tooltipWrapperId(container: string) {
return 'G2-TOOLTIP-WRAPPER-' + container
}

View File

@@ -3,6 +3,10 @@ import {
flow,
getLineConditions,
getLineLabelColorByCondition,
handleBreakLineMultiDimension,
handleIgnoreData,
handleSetZeroMultiDimension,
handleSetZeroSingleDimension,
hexColorToRGBA,
parseJson,
randomString,
@@ -652,9 +656,43 @@ export class Area extends G2ChartView {
return options
}
protected configEmptyDataStrategy(chart: Chart, options: G2Spec): G2Spec {
const { functionCfg } = parseJson(chart.senior)
const { emptyDataStrategy } = functionCfg
const [areaMark, lineMark] = options.children
const data = options.data.value
const multiDimension = chart.yAxis?.length > 1
switch (emptyDataStrategy) {
case 'breakLine': {
if (multiDimension) {
handleBreakLineMultiDimension(data)
}
merge(areaMark, { style: { connect: false } })
merge(lineMark, { style: { connect: false } })
break
}
case 'ignoreData': {
handleIgnoreData(data)
break
}
case 'setZero': {
if (multiDimension) {
// 多维度置0
handleSetZeroMultiDimension(data)
} else {
// 单维度置0
handleSetZeroSingleDimension(data)
}
break
}
}
return options
}
protected setupOptions(chart: Chart, options: G2Spec, context: Record<string, any>): G2Spec {
return flow(
this.configTheme,
this.configEmptyDataStrategy,
this.configColor,
this.configLabel,
this.configBasicStyle,
@@ -866,11 +904,44 @@ export class StackArea extends Area {
return setUpStackSeriesColor(chart, data)
}
protected configEmptyDataStrategy(chart: Chart, options: G2Spec): G2Spec {
const { functionCfg } = parseJson(chart.senior)
const { emptyDataStrategy } = functionCfg
const [areaMark, lineMark] = options.children
const data = options.data.value
const multiDimension = chart.yAxis?.length > 1 || chart.extStack?.length > 0
switch (emptyDataStrategy) {
case 'breakLine': {
if (multiDimension) {
handleBreakLineMultiDimension(data)
}
merge(areaMark, { style: { connect: false } })
merge(lineMark, { style: { connect: false } })
break
}
case 'ignoreData': {
handleIgnoreData(data)
break
}
case 'setZero': {
if (multiDimension) {
// 多维度置0
handleSetZeroMultiDimension(data)
} else {
// 单维度置0
handleSetZeroSingleDimension(data)
}
break
}
}
return options
}
constructor() {
super('area-stack')
this.baseOptions = {
...this.baseOptions,
transform: [{ type: 'stackY' }]
transform: [{ type: 'stackY', orderBy: 'series' }]
}
delete this.propertyInner.threshold
this.properties = this.properties.filter(item => item !== 'threshold')

View File

@@ -3,6 +3,10 @@ import {
flow,
getLineConditions,
getLineLabelColorByCondition,
handleBreakLineMultiDimension,
handleIgnoreData,
handleSetZeroMultiDimension,
handleSetZeroSingleDimension,
hexColorToRGBA,
parseJson,
randomString,
@@ -726,9 +730,42 @@ export class Line extends G2ChartView {
return options
}
protected configEmptyDataStrategy(chart: Chart, options: G2Spec): G2Spec {
const { functionCfg } = parseJson(chart.senior)
const { emptyDataStrategy } = functionCfg
const [lineMark] = options.children
const data = options.data.value
const multiDimension = chart.yAxis?.length > 1 || chart.xAxisExt?.length > 0
switch (emptyDataStrategy) {
case 'breakLine': {
if (multiDimension) {
handleBreakLineMultiDimension(data)
}
merge(lineMark, { style: { connect: false } })
break
}
case 'ignoreData': {
handleIgnoreData(data)
break
}
case 'setZero': {
if (multiDimension) {
// 多维度置0
handleSetZeroMultiDimension(data)
} else {
// 单维度置0
handleSetZeroSingleDimension(data)
}
break
}
}
return options
}
protected setupOptions(chart: Chart, options: G2Spec): G2Spec {
return flow(
this.configTheme,
this.configEmptyDataStrategy,
this.configColor,
this.configLabel,
this.configBasicStyle,

View File

@@ -267,80 +267,7 @@ export const notSupportAccumulateViews = [
'stock-line'
]
export function handleEmptyDataStrategy<O extends PickOptions>(chart: Chart, options: O): O {
const { data } = options as unknown as Options
const isChartMix = chart.type.includes('chart-mix')
if (!data?.length) {
return options
}
const strategy = parseJson(chart.senior).functionCfg.emptyDataStrategy
if (strategy === 'ignoreData') {
if (isChartMix) {
for (let i = 0; i < data.length; i++) {
handleIgnoreData(data[i] as Record<string, any>[])
}
} else {
handleIgnoreData(data)
}
return options
}
const { yAxis, xAxisExt, extStack, extBubble } = chart
const multiDimension = yAxis?.length >= 2 || xAxisExt?.length > 0 || extStack?.length > 0
switch (strategy) {
case 'breakLine': {
if (isChartMix) {
if (data[0]) {
if (xAxisExt?.length > 0 || extStack?.length > 0) {
handleBreakLineMultiDimension(data[0] as Record<string, any>[])
}
}
if (data[1]) {
if (extBubble?.length > 0) {
handleBreakLineMultiDimension(data[1] as Record<string, any>[])
}
}
} else {
if (multiDimension) {
handleBreakLineMultiDimension(data)
}
}
return {
...options,
connectNulls: false
}
}
case 'setZero': {
if (isChartMix) {
if (data[0]) {
if (xAxisExt?.length > 0 || extStack?.length > 0) {
handleSetZeroMultiDimension(data[0] as Record<string, any>[])
} else {
handleSetZeroSingleDimension(data[0] as Record<string, any>[])
}
}
if (data[1]) {
if (extBubble?.length > 0) {
handleSetZeroMultiDimension(data[1] as Record<string, any>[], true)
} else {
handleSetZeroSingleDimension(data[1] as Record<string, any>[], true)
}
}
} else {
if (multiDimension) {
// 多维度置0
handleSetZeroMultiDimension(data)
} else {
// 单维度置0
handleSetZeroSingleDimension(data)
}
}
break
}
}
return options
}
function handleBreakLineMultiDimension(data) {
export function handleBreakLineMultiDimension(data) {
const dimensionInfoMap = new Map()
const subDimensionSet = new Set()
const quotaMap = new Map<string, { id: string }[]>()
@@ -376,17 +303,14 @@ function handleBreakLineMultiDimension(data) {
})
}
function handleSetZeroMultiDimension(data: Record<string, any>[], isExt = false) {
export function handleSetZeroMultiDimension(data: Record<string, any>[], valueProp = 'value') {
const dimensionInfoMap = new Map()
const subDimensionSet = new Set()
const quotaMap = new Map<string, { id: string }[]>()
for (let i = 0; i < data.length; i++) {
const item = data[i]
if (item.value === null) {
item.value = 0
if (isExt) {
item.valueExt = 0
}
if (item[valueProp] === null) {
item[valueProp] = 0
}
const dimensionInfo = dimensionInfoMap.get(item.field)
if (dimensionInfo) {
@@ -405,13 +329,10 @@ function handleSetZeroMultiDimension(data: Record<string, any>[], isExt = false)
if (!dimensionInfo.set.has(dimension)) {
const _temp = {
field,
value: 0,
[valueProp]: 0,
category: dimension,
quotaList: quotaMap.get(dimension as string)
} as any
if (isExt) {
_temp.valueExt = 0
}
data.splice(dimensionInfo.index + insertCount + subInsertIndex, 0, _temp)
}
@@ -422,19 +343,15 @@ function handleSetZeroMultiDimension(data: Record<string, any>[], isExt = false)
})
}
function handleSetZeroSingleDimension(data: Record<string, any>[], isExt = false) {
export function handleSetZeroSingleDimension(data: Record<string, any>[], valueProp = 'value') {
data.forEach(item => {
if (item.value === null) {
if (!isExt) {
item.value = 0
} else {
item.valueExt = 0
}
if (item[valueProp] === null) {
item[valueProp] = 0
}
})
}
function handleIgnoreData(data: Record<string, any>[]) {
export function handleIgnoreData(data: Record<string, any>[]) {
for (let i = data.length - 1; i >= 0; i--) {
const item = data[i]
if (item.value === null) {