diff --git a/packages/vchart/src/chart/venn/venn-transformer.ts b/packages/vchart/src/chart/venn/venn-transformer.ts index e5224777a5..adba174fb4 100644 --- a/packages/vchart/src/chart/venn/venn-transformer.ts +++ b/packages/vchart/src/chart/venn/venn-transformer.ts @@ -9,6 +9,7 @@ export class VennChartSpecTransformer const series: any = super._getDefaultSeriesSpec(spec, [ 'categoryField', 'valueField', + 'emptySetKey', 'circle', 'overlap', 'overlapLabel' diff --git a/packages/vchart/src/series/venn/interface.ts b/packages/vchart/src/series/venn/interface.ts index 718c9d024e..9f10c98db4 100644 --- a/packages/vchart/src/series/venn/interface.ts +++ b/packages/vchart/src/series/venn/interface.ts @@ -17,7 +17,10 @@ export interface IVennSeriesSpec extends ISeriesSpec, IAnimationSpec { - return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]]); + return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]], this.series.getSpec().emptySetKey); }; markTooltipKeyCallback = (datum: any) => { - return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]]); + return getVennSeriesDataKey(datum?.[this.series.getDimensionField()[0]], this.series.getSpec().emptySetKey); }; } diff --git a/packages/vchart/src/series/venn/util.ts b/packages/vchart/src/series/venn/util.ts index 25fd2ee3fa..0e6e99ef61 100644 --- a/packages/vchart/src/series/venn/util.ts +++ b/packages/vchart/src/series/venn/util.ts @@ -1,5 +1,8 @@ import { array } from '@visactor/vutils'; -export const getVennSeriesDataKey = (sets: string | string[]) => { +export const getVennSeriesDataKey = (sets: string | string[], emptysetKey?: string) => { + if (!sets || (Array.isArray(sets) && sets.length === 0)) { + return emptysetKey || 'others'; + } return array(sets).join(','); }; diff --git a/packages/vchart/src/series/venn/venn.ts b/packages/vchart/src/series/venn/venn.ts index 363238bff1..4d21bbaed5 100644 --- a/packages/vchart/src/series/venn/venn.ts +++ b/packages/vchart/src/series/venn/venn.ts @@ -36,7 +36,6 @@ export class VennSeries extends Bas static readonly mark: SeriesMarkMap = vennSeriesMark; static readonly builtInTheme = { venn }; - static readonly transformerConstructor = VennSeriesSpecTransformer; readonly transformerConstructor = VennSeriesSpecTransformer; @@ -67,11 +66,21 @@ export class VennSeries extends Bas return this._valueField; } + protected _emptySetKey!: string; + getEmptySetKey() { + return this._emptySetKey; + } + setEmptySetKey(f: string): string { + this._emptySetKey = f; + return this._emptySetKey; + } + setAttrFromSpec(): void { super.setAttrFromSpec(); this.setCategoryField(this._spec.categoryField ?? 'sets'); this.setValueField(this._spec.valueField ?? 'size'); this.setSeriesField(this._spec.seriesField ?? DEFAULT_DATA_KEY); + this.setEmptySetKey(this._spec.emptySetKey ?? 'others'); } initData() { @@ -210,7 +219,13 @@ export class VennSeries extends Bas { x: datum => (datum as IVennCircleDatum).labelX, y: datum => (datum as IVennCircleDatum).labelY, - text: datum => getVennSeriesDataKey((datum as IVennCircleDatum).sets), + text: datum => { + const sets = (datum as IVennOverlapDatum).sets; + if (!sets || (Array.isArray(sets) && sets.length === 0)) { + return ''; + } + return getVennSeriesDataKey(sets); + }, maxLineWidth: (datum: any) => { const { x, radius, labelX } = datum as IVennCircleDatum; const circleX0 = x - radius; @@ -234,7 +249,10 @@ export class VennSeries extends Bas { x: datum => (datum as IVennOverlapDatum).labelX, y: datum => (datum as IVennOverlapDatum).labelY, - text: datum => getVennSeriesDataKey((datum as IVennOverlapDatum).sets) + text: datum => { + const sets = (datum as IVennOverlapDatum).sets; + return getVennSeriesDataKey(sets); + } }, STATE_VALUE_ENUM.STATE_NORMAL, AttributeLevel.Series @@ -310,7 +328,7 @@ export class VennSeries extends Bas protected _getSeriesInfo(field: string, keys: string[]) { const defaultShapeType = this.getDefaultShapeType(); return keys.map(originalKey => { - const dataKey = getVennSeriesDataKey(originalKey); + const dataKey = getVennSeriesDataKey(originalKey, this._emptySetKey); return { key: dataKey, originalKey, @@ -324,7 +342,7 @@ export class VennSeries extends Bas getSeriesFieldValue(datum: Datum, seriesField?: string) { const value = super.getSeriesFieldValue(datum, seriesField); - return getVennSeriesDataKey(value); + return getVennSeriesDataKey(value, this._emptySetKey); } legendSelectedFilter(component: ILegend, selectedKeys: StringOrNumber[]) { @@ -339,27 +357,38 @@ export class VennSeries extends Bas if (selectedKeys.length === originalLegendKeys.length) { return selectedKeys; } - - // 找到缺失的项 - const selectedFilter = {}; - selectedKeys.forEach(s => { - selectedFilter[s] = true; - }); - const disableKeys = originalLegendKeys.filter(key => !selectedFilter[getVennSeriesDataKey(key)]); - - // 找到缺失的项的派生项(如 “A&B” 的派生项 “A&B&C”) - const derivedDisableKeys = originalLegendKeys.filter(key => { - if (disableKeys.includes(key)) { - return false; - } - return disableKeys.some(disableKey => array(disableKey).every(k => key.includes(k))); - }); - - // 将派生项从 selectedKeys 中移除 - selectedKeys = selectedKeys.slice(); - derivedDisableKeys.forEach(key => { - selectedKeys.splice(selectedKeys.indexOf(getVennSeriesDataKey(key)), 1); - }); + const emptyKey = this._emptySetKey; + + const hasEmpty = selectedKeys.includes(emptyKey); + const nonEmpty = selectedKeys.filter(key => key !== emptyKey); + + if (nonEmpty.length > 0) { + // 过滤出非空的原始图例键 + const validKeys = originalLegendKeys.filter(key => getVennSeriesDataKey(key, this._emptySetKey) !== emptyKey); + // 找到缺失的项 + const selectedFilter: Record = {}; + selectedKeys.forEach(s => { + selectedFilter[s] = true; + }); + const disableKeys = validKeys.filter(key => !selectedFilter[getVennSeriesDataKey(key, this._emptySetKey)]); + + // 找到缺失的项的派生项(如 “A&B” 的派生项 “A&B&C”) + const derivedDisableKeys = validKeys.filter(key => { + if (disableKeys.includes(key)) { + return false; + } + return disableKeys.some(disableKey => array(disableKey).every(k => key.includes(k))); + }); + + // 将派生项从 nonEmpty 中移除 + selectedKeys = nonEmpty.slice(); + derivedDisableKeys.forEach(key => { + selectedKeys.splice(selectedKeys.indexOf(getVennSeriesDataKey(key, this._emptySetKey)), 1); + }); + } + if (hasEmpty) { + selectedKeys.push(emptyKey); + } } return selectedKeys; }