From e222ed8db0684bfd07ab246cb3ba9b89e7c8b1ac Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 25 Aug 2025 19:13:11 +0800 Subject: [PATCH 1/4] feat: add combination candlestick chart --- .../src/charts/candlestick/index.ts | 2 + .../src/charts/candlestick/util.ts | 5 + .../combination-candlestick-transformer.ts | 425 ++++++++++++++++++ .../combination-candlestick.ts | 45 ++ .../combination-candlestick/constant.ts | 4 + .../charts/combination-candlestick/index.ts | 3 + .../combination-candlestick/interface.ts | 33 ++ packages/vchart-extension/src/index.ts | 1 + packages/vchart/src/index.ts | 1 + .../src/layout/grid-layout/grid-layout.ts | 7 + 10 files changed, 526 insertions(+) create mode 100644 packages/vchart-extension/src/charts/candlestick/util.ts create mode 100644 packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts create mode 100644 packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick.ts create mode 100644 packages/vchart-extension/src/charts/combination-candlestick/constant.ts create mode 100644 packages/vchart-extension/src/charts/combination-candlestick/index.ts create mode 100644 packages/vchart-extension/src/charts/combination-candlestick/interface.ts diff --git a/packages/vchart-extension/src/charts/candlestick/index.ts b/packages/vchart-extension/src/charts/candlestick/index.ts index 96031419c2..860cb28a53 100644 --- a/packages/vchart-extension/src/charts/candlestick/index.ts +++ b/packages/vchart-extension/src/charts/candlestick/index.ts @@ -1,3 +1,5 @@ export * from './candlestick'; export * from './interface'; export * from './candlestick-transformer'; +export * from './series/candlestick'; +export * from './util'; diff --git a/packages/vchart-extension/src/charts/candlestick/util.ts b/packages/vchart-extension/src/charts/candlestick/util.ts new file mode 100644 index 0000000000..c390e90976 --- /dev/null +++ b/packages/vchart-extension/src/charts/candlestick/util.ts @@ -0,0 +1,5 @@ +import type { ICartesianSeriesSpec } from '@visactor/vchart'; +import type { ICandlestickSeriesSpec } from './series/interface'; +export function transformCandlestickSeriesSpec(spec: ICandlestickSeriesSpec) { + (spec as ICartesianSeriesSpec).yField = [spec.openField, spec.highField, spec.lowField, spec.closeField]; +} diff --git a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts new file mode 100644 index 0000000000..3b2c1fa71a --- /dev/null +++ b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts @@ -0,0 +1,425 @@ +import type { ICommonChartSpec, ICartesianSeriesSpec, IDataType, IGridLayoutSpec, IOrientType } from '@visactor/vchart'; +import { CartesianChartSpecTransformer, isPercent } from '@visactor/vchart'; +import type { ICombinationCandlestickChartSpec } from './interface'; +import { merge, array } from '@visactor/vutils'; +import { transformCandlestickSeriesSpec } from '../candlestick'; +import type { ICandlestickSeriesSpec } from '../candlestick/series/interface'; + +/** + * @description 组合蜡烛图 spec a转换 + */ +export class CombinationCandlestickChartSpecTransformer< + T extends ICombinationCandlestickChartSpec +> extends CartesianChartSpecTransformer { + protected needAxes(): boolean { + return true; + } + + transformSpec(spec: T): void { + this.transformRegion(spec); + if (spec.tooltip === undefined) { + spec.tooltip = {}; + } + this.transformSeriesSpec(spec); + this._transformAxisSpec(spec); + this._transformLayout(spec); + } + + transformRegion(spec: T): void { + const commonSpec = spec as unknown as ICommonChartSpec; + commonSpec.region = [spec.candlestickRegion ?? {}]; + if (spec.previewSeries) { + commonSpec.region.push(spec.previewRegion ?? {}); + } + } + + transformSeriesSpec(spec: T): void { + // 单独的系列转换 + spec.series = spec.series ?? []; + const tempCandlestickSeries = spec.series.find(s => s.id === spec.candlestickSeries.id) as ICandlestickSeriesSpec; + let candlestickSpec; + if (tempCandlestickSeries) { + merge(tempCandlestickSeries, spec.candlestickSeries); + candlestickSpec = tempCandlestickSeries; + } else { + spec.series.push(spec.candlestickSeries); + candlestickSpec = spec.candlestickSeries; + } + transformCandlestickSeriesSpec(candlestickSpec); + this._transformSeriesData(spec, candlestickSpec); + candlestickSpec.regionIndex = 0; + + if (spec.previewSeries) { + const tempPreviewSeries = spec.series.find(s => s.id === spec.previewSeries.id); + let previewSeriesSpec; + if (tempPreviewSeries) { + merge(tempPreviewSeries, spec.previewSeries); + previewSeriesSpec = tempPreviewSeries; + } else { + spec.series.push(spec.previewSeries); + previewSeriesSpec = spec.previewSeries; + } + previewSeriesSpec.regionIndex = 1; + this._transformSeriesData(spec, previewSeriesSpec); + } + } + + private _transformSeriesData(spec: T, seriesSpec: ICartesianSeriesSpec) { + if (seriesSpec.data) { + spec.data = array(spec.data ?? []); + array(seriesSpec.data).forEach((d, index) => { + (spec.data as IDataType[]).push(d); + if (index === 0) { + seriesSpec.dataIndex = (spec.data as IDataType[]).length - 1; + if ('id' in d) { + seriesSpec.dataId = d.id; + } else if ('name' in d) { + seriesSpec.dataId = d.name; + } + } + }); + delete seriesSpec.data; + } + } + + protected _transformAxisSpec(spec: T) { + super._transformAxisSpec(spec); + if (!spec.previewSeries) { + return; + } + if (!spec.previewAxes) { + spec.previewAxes = { + type: 'linear', + orient: 'left', + visible: false + }; + } + spec.previewAxes.regionIndex = [1]; + spec.axes.push(spec.previewAxes); + + spec.axes.forEach(axis => { + if (axis === spec.previewAxes) { + return; + } + if (axis.orient === 'left' || axis.orient === 'right') { + axis.regionIndex = [0]; + } + }); + } + + protected _transformDataZoomSpec(spec: T) { + if (spec.dataZoom) { + array(spec.dataZoom).forEach(dataZoom => { + if (dataZoom.orient === 'left' || dataZoom.orient === 'right') { + dataZoom.regionIndex = [0]; + } + }); + } + } + + protected _transformLayout(spec: T) { + const layout: IGridLayoutSpec = { + type: 'grid', + col: 2, + row: 6, + elements: [], + colWidth: [], + rowHeight: [] + }; + // 创建布局的临时计算属性 + let totalRow = 0; + let totalCol = 0; + // 先获取总计的行和列 + // 如果有 title + if (spec.title?.visible !== false) { + totalRow++; + } + // 图例 + if (spec.legends) { + const firstLegend = array(spec.legends)[0]; + if (firstLegend?.visible !== false) { + if (firstLegend.orient === 'top' || firstLegend.orient === 'bottom') { + totalRow++; + } else { + totalCol++; + } + } + } + // datazoom + if (spec.dataZoom) { + array(spec.dataZoom).forEach(dataZoom => { + if (dataZoom?.visible !== false) { + if (dataZoom.orient === 'top' || dataZoom.orient === 'bottom') { + totalRow++; + } else { + totalCol++; + } + } + }); + } + // 轴 + if (spec.axes) { + const hasLayout = { + top: false, + bottom: false, + left: false, + right: false, + z: false + }; + array(spec.axes).forEach(axis => { + if (hasLayout[axis.orient]) { + return; + } + hasLayout[axis.orient] = true; + if (axis.orient === 'top' || axis.orient === 'bottom') { + totalRow++; + } else { + totalCol++; + } + }); + } + // 系列 + totalRow++; + totalCol++; + // 预览系列 + if (spec.previewSeries) { + totalRow++; + } + layout.row = totalRow; + layout.col = totalCol; + + const temp = { + top: 0, + bottom: 0, + left: 0, + right: 0 + }; + // 开始布局 + if (spec.title?.visible !== false) { + this._layoutOrientItem(layout, spec.title, temp, { specKey: 'title', index: 0 }, 'total'); + } + // 图例 + if (spec.legends) { + const firstLegend = array(spec.legends)[0]; + if (firstLegend?.visible !== false) { + this._layoutOrientItem( + layout, + firstLegend as { orient: IOrientType }, + temp, + { specKey: 'legends', index: 0 }, + 'less' + ); + } + } + // datazoom + if (spec.dataZoom) { + array(spec.dataZoom).forEach((dataZoom, index) => { + if (dataZoom?.visible === false) { + return; + } + this._layoutOrientItem( + layout, + dataZoom as { orient: IOrientType }, + temp, + { specKey: 'dataZoom', index }, + 'one' + ); + }); + } + // 轴 先只布局左右 + if (spec.axes) { + const hasLayout = { + left: false, + right: false + }; + spec.axes.forEach((axis, index) => { + if (axis.orient !== 'left' && axis.orient !== 'right') { + return; + } + if (hasLayout[axis.orient]) { + return; + } + hasLayout[axis.orient] = true; + const axisLayout = this._layoutOrientItem( + layout, + axis as { orient: IOrientType }, + temp, + { specKey: 'axes', index }, + 'one' + ); + if (axis?.visible === false) { + if (axis.orient === 'left' || axis.orient === 'right') { + layout.colWidth.push({ + index: axisLayout.col, + size: 0 + }); + } else { + layout.rowHeight.push({ + index: axisLayout.row, + size: 0 + }); + } + } + }); + } + // region + const regionLayout = this._layoutOrientItem( + layout, + { orient: 'top' }, + temp, + { specKey: 'region', index: 0 }, + 'one' + ); + + // 预览图region + if (spec.previewSeries) { + const regionLayout = this._layoutOrientItem( + layout, + { orient: 'bottom' }, + temp, + { specKey: 'region', index: 1 }, + 'one' + ); + const previewAxesIndex = spec.axes.findIndex(axis => axis === spec.previewAxes); + if (previewAxesIndex !== -1) { + const previewAxesLayout = { + col: regionLayout.col - 1, + row: regionLayout.row, + modelKey: 'axes', + modelIndex: previewAxesIndex + }; + layout.elements.push(previewAxesLayout); + } + if (spec.previewAxes.visible === false) { + const colSize = layout.colWidth.find(c => c.index === regionLayout.col - 1); + if (!colSize) { + layout.colWidth.push({ + index: regionLayout.col - 1, + size: 0 + }); + } + } + if (spec.previewHeight) { + if (typeof spec.previewHeight === 'string') { + if (isPercent(spec.previewHeight)) { + const heightStr = spec.previewHeight; + layout.rowHeight.push({ + index: regionLayout.row, + size: (size: number) => (Number(heightStr.substring(0, heightStr.length - 1)) * size) / 100 + }); + } + } else { + layout.rowHeight.push({ + index: regionLayout.row, + size: spec.previewHeight + }); + } + } + } + + // 下轴 + const bottomAxesIndex = spec.axes.findIndex(axis => axis.orient === 'bottom'); + this._layoutOrientItem( + layout, + spec.axes[bottomAxesIndex], + temp, + { specKey: 'axes', index: bottomAxesIndex }, + 'one' + ); + + // 修正axes,datazoom + spec.axes.forEach((axis, index) => { + // 预览图的轴已处理 + if (axis === spec.previewAxes) { + return; + } + const axisLayout = layout.elements.find( + item => 'modelKey' in item && item.modelKey === 'axes' && item.modelIndex === index + ); + if (axisLayout) { + if (axis.orient === 'top' || axis.orient === 'bottom') { + axisLayout.col = regionLayout.col; + } else { + axisLayout.row = regionLayout.row; + } + } + }); + // 修正datazoom + array(spec.dataZoom).forEach((dataZoom, index) => { + const dataZoomLayout = layout.elements.find( + item => 'modelKey' in item && item.modelKey === 'dataZoom' && item.modelIndex === index + ); + if (dataZoomLayout) { + if (dataZoom.orient === 'top' || dataZoom.orient === 'bottom') { + dataZoomLayout.col = regionLayout.col; + } else { + dataZoomLayout.row = regionLayout.row; + } + } + }); + + spec.layout = layout; + } + + private _layoutOrientItem( + layout: IGridLayoutSpec, + modelSpec: { orient?: IOrientType }, + temp: { top: number; bottom: number; left: number; right: number }, + modelInfo: { specKey: string; index: number }, + spanType: 'total' | 'one' | 'less' + ) { + const modelLayout: IGridLayoutSpec['elements'][number] = { + modelKey: modelInfo.specKey, + modelIndex: modelInfo.index, + col: 0, + row: 0 + }; + const orient = modelSpec.orient ?? 'top'; + const { span, index } = this._getLayoutElementCommon(layout, temp, spanType, orient); + layout.elements.push(modelLayout); + if (orient === 'top') { + modelLayout.row = temp.top; + modelLayout.rowSpan = 1; + modelLayout.colSpan = span; + modelLayout.col = index; + temp.top++; + } else if (orient === 'bottom') { + modelLayout.row = layout.row - temp.bottom - 1; + modelLayout.rowSpan = 1; + modelLayout.colSpan = span; + modelLayout.col = index; + temp.bottom++; + } else if (orient === 'left') { + modelLayout.col = temp.left; + modelLayout.colSpan = 1; + modelLayout.rowSpan = span; + modelLayout.row = index; + temp.left++; + } else if (orient === 'right') { + modelLayout.col = layout.col - temp.right - 1; + modelLayout.colSpan = 1; + modelLayout.rowSpan = span; + modelLayout.row = index; + temp.right++; + } + return modelLayout; + } + + private _getLayoutElementCommon( + layout: IGridLayoutSpec, + temp: { top: number; bottom: number; left: number; right: number }, + span: 'total' | 'one' | 'less', + orient: IOrientType + ) { + if (orient === 'top' || orient === 'bottom') { + return { + span: span === 'total' ? layout.col : span === 'one' ? 1 : layout.col - temp.left - temp.right, + index: span === 'total' ? 0 : temp.left + }; + } + return { + span: span === 'total' ? layout.row : span === 'one' ? 1 : layout.row - temp.top - temp.bottom, + index: span === 'total' ? 0 : temp.top + }; + } +} diff --git a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick.ts b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick.ts new file mode 100644 index 0000000000..fb65e3ee19 --- /dev/null +++ b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick.ts @@ -0,0 +1,45 @@ +import type { IChart, ILayoutPoint } from '@visactor/vchart'; +import { + BaseChart, + Factory, + StackChartMixin, + getCartesianCrosshairRect, + getCartesianDimensionInfo, + getDimensionInfoByValue +} from '@visactor/vchart'; +import type { ICombinationCandlestickChartSpec } from './interface'; +import { CombinationCandlestickChart_TYPE } from './constant'; +import { registerCandlestickSeries } from '../candlestick'; +import { CombinationCandlestickChartSpecTransformer } from './combination-candlestick-transformer'; +import { mixin } from '@visactor/vutils'; + +/** + * @description 组合蜡烛图 + */ +export class CombinationCandlestickChart extends BaseChart { + static readonly type: string = CombinationCandlestickChart_TYPE; + readonly type: string = CombinationCandlestickChart_TYPE; + + declare _spec: ICombinationCandlestickChartSpec; + + static readonly transformerConstructor = CombinationCandlestickChartSpecTransformer; + readonly transformerConstructor = CombinationCandlestickChartSpecTransformer; + + protected _setModelOption() { + this._modelOption.getDimensionInfo = (chart: IChart | undefined, point: ILayoutPoint, isTooltip?: boolean) => { + return [...(getCartesianDimensionInfo(chart, point, isTooltip) ?? [])]; + }; + this._modelOption.getDimensionInfoByValue = getDimensionInfoByValue; + this._modelOption.getRectByDimensionData = getCartesianCrosshairRect; + } +} + +mixin(CombinationCandlestickChart, StackChartMixin); + +/** + * @description 注册组合蜡烛图 + */ +export const registerCombinationCandlestickChart = () => { + registerCandlestickSeries(); + Factory.registerChart(CombinationCandlestickChart.type, CombinationCandlestickChart); +}; diff --git a/packages/vchart-extension/src/charts/combination-candlestick/constant.ts b/packages/vchart-extension/src/charts/combination-candlestick/constant.ts new file mode 100644 index 0000000000..725f2a78ba --- /dev/null +++ b/packages/vchart-extension/src/charts/combination-candlestick/constant.ts @@ -0,0 +1,4 @@ +/** + * @description 常量 + */ +export const CombinationCandlestickChart_TYPE = 'combinationCandlestick'; diff --git a/packages/vchart-extension/src/charts/combination-candlestick/index.ts b/packages/vchart-extension/src/charts/combination-candlestick/index.ts new file mode 100644 index 0000000000..58786148a5 --- /dev/null +++ b/packages/vchart-extension/src/charts/combination-candlestick/index.ts @@ -0,0 +1,3 @@ +export * from './combination-candlestick'; +export * from './interface'; +export * from './constant'; diff --git a/packages/vchart-extension/src/charts/combination-candlestick/interface.ts b/packages/vchart-extension/src/charts/combination-candlestick/interface.ts new file mode 100644 index 0000000000..698a3526d4 --- /dev/null +++ b/packages/vchart-extension/src/charts/combination-candlestick/interface.ts @@ -0,0 +1,33 @@ +import type { ICandlestickSeriesSpec } from './../candlestick/series/interface'; +import type { + ISeriesSpec, + IAreaSeriesSpec, + IBarSeriesSpec, + ILineSeriesSpec, + IRegionSpec, + ICartesianLinearAxisSpec, + ICartesianChartSpec +} from '@visactor/vchart'; + +/** + * @description 组合蜡烛图规约 + */ +export interface ICombinationCandlestickChartSpec extends Omit { + type: 'combinationCandlestick'; + + /** 与蜡烛图使用同region的系列配置 */ + series?: ISeriesSpec[]; + + // 蜡烛系列 必须配置数据 不可以为数组 + candlestickSeries: ICandlestickSeriesSpec; + candlestickRegion?: IRegionSpec; + + // 预览系列 必须配置数据 不可以为数组 + previewSeries?: IBarSeriesSpec | IAreaSeriesSpec | ILineSeriesSpec; + previewRegion?: IRegionSpec; + // 预览图高度 可以是数字,返回数字的函数,或者百分百字符串 '30%' + previewHeight?: number | ((maxSize: number) => number) | string; + + /** 预览系列的数值轴, y轴 */ + previewAxes?: ICartesianLinearAxisSpec; +} diff --git a/packages/vchart-extension/src/index.ts b/packages/vchart-extension/src/index.ts index 24e6715dce..27617b4164 100644 --- a/packages/vchart-extension/src/index.ts +++ b/packages/vchart-extension/src/index.ts @@ -9,6 +9,7 @@ export * from './charts/bar-3d'; export * from './charts/funnel-3d'; export * from './charts/histogram-3d'; export * from './charts/pie-3d'; +export * from './charts/combination-candlestick'; export * from './charts/word-cloud-3d'; export * from './charts/axis-3d'; export * from './charts/range-column-3d'; diff --git a/packages/vchart/src/index.ts b/packages/vchart/src/index.ts index 67948da7ff..69a3c01a29 100644 --- a/packages/vchart/src/index.ts +++ b/packages/vchart/src/index.ts @@ -9,6 +9,7 @@ export * from './chart'; export * from './chart/base'; export * from './chart/cartesian'; export * from './chart/common'; +export * from './chart/stack'; export * from './series'; export * from './mark'; export * from './component'; diff --git a/packages/vchart/src/layout/grid-layout/grid-layout.ts b/packages/vchart/src/layout/grid-layout/grid-layout.ts index 8d4a90986e..952dcaa0a9 100644 --- a/packages/vchart/src/layout/grid-layout/grid-layout.ts +++ b/packages/vchart/src/layout/grid-layout/grid-layout.ts @@ -112,6 +112,13 @@ export class GridLayout implements IBaseLayout { } protected clearLayoutSize() { + // 先对用户设置的宽高进行设置 + this._gridInfo.colWidth && + this.setSizeFromUserSetting(this._gridInfo.colWidth, this._colSize, this._col, this._chartLayoutRect.width); + + this._gridInfo.rowHeight && + this.setSizeFromUserSetting(this._gridInfo.rowHeight, this._rowSize, this._row, this._chartLayoutRect.height); + // 其余位置默认填充0 this._colSize.forEach(c => { c.isLayoutSetting = false; From ebe5a9eb4376e3cb450092feffdeba8fbaa76fc6 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 25 Aug 2025 20:42:30 +0800 Subject: [PATCH 2/4] feat: add combination candlestick chart --- .../combination-candlestick-transformer.ts | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts index 3b2c1fa71a..7a3671de28 100644 --- a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts +++ b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts @@ -36,6 +36,10 @@ export class CombinationCandlestickChartSpecTransformer< transformSeriesSpec(spec: T): void { // 单独的系列转换 spec.series = spec.series ?? []; + spec.series.forEach(s => { + this._transformSeriesData(spec, s); + s.regionIndex = 0; + }); const tempCandlestickSeries = spec.series.find(s => s.id === spec.candlestickSeries.id) as ICandlestickSeriesSpec; let candlestickSpec; if (tempCandlestickSeries) { @@ -240,26 +244,7 @@ export class CombinationCandlestickChartSpecTransformer< return; } hasLayout[axis.orient] = true; - const axisLayout = this._layoutOrientItem( - layout, - axis as { orient: IOrientType }, - temp, - { specKey: 'axes', index }, - 'one' - ); - if (axis?.visible === false) { - if (axis.orient === 'left' || axis.orient === 'right') { - layout.colWidth.push({ - index: axisLayout.col, - size: 0 - }); - } else { - layout.rowHeight.push({ - index: axisLayout.row, - size: 0 - }); - } - } + this._layoutOrientItem(layout, axis as { orient: IOrientType }, temp, { specKey: 'axes', index }, 'one'); }); } // region @@ -290,15 +275,6 @@ export class CombinationCandlestickChartSpecTransformer< }; layout.elements.push(previewAxesLayout); } - if (spec.previewAxes.visible === false) { - const colSize = layout.colWidth.find(c => c.index === regionLayout.col - 1); - if (!colSize) { - layout.colWidth.push({ - index: regionLayout.col - 1, - size: 0 - }); - } - } if (spec.previewHeight) { if (typeof spec.previewHeight === 'string') { if (isPercent(spec.previewHeight)) { From d064375ee9d21ea396720741c54afcd548ac9129 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 8 Sep 2025 16:06:11 +0800 Subject: [PATCH 3/4] feat: support multi previewSeries --- .../combination-candlestick-transformer.ts | 24 ++++++++++--------- .../combination-candlestick/interface.ts | 6 +++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts index 7a3671de28..aece2cd047 100644 --- a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts +++ b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts @@ -54,17 +54,19 @@ export class CombinationCandlestickChartSpecTransformer< candlestickSpec.regionIndex = 0; if (spec.previewSeries) { - const tempPreviewSeries = spec.series.find(s => s.id === spec.previewSeries.id); - let previewSeriesSpec; - if (tempPreviewSeries) { - merge(tempPreviewSeries, spec.previewSeries); - previewSeriesSpec = tempPreviewSeries; - } else { - spec.series.push(spec.previewSeries); - previewSeriesSpec = spec.previewSeries; - } - previewSeriesSpec.regionIndex = 1; - this._transformSeriesData(spec, previewSeriesSpec); + array(spec.previewSeries).forEach(previewSeries => { + const tempPreviewSeries = spec.series.find(s => s.id === previewSeries.id); + let previewSeriesSpec; + if (tempPreviewSeries) { + merge(tempPreviewSeries, previewSeries); + previewSeriesSpec = tempPreviewSeries; + } else { + spec.series.push(previewSeries); + previewSeriesSpec = previewSeries; + } + previewSeriesSpec.regionIndex = 1; + this._transformSeriesData(spec, previewSeriesSpec); + }); } } diff --git a/packages/vchart-extension/src/charts/combination-candlestick/interface.ts b/packages/vchart-extension/src/charts/combination-candlestick/interface.ts index 698a3526d4..62cf3d458f 100644 --- a/packages/vchart-extension/src/charts/combination-candlestick/interface.ts +++ b/packages/vchart-extension/src/charts/combination-candlestick/interface.ts @@ -9,6 +9,8 @@ import type { ICartesianChartSpec } from '@visactor/vchart'; +export type ICombinationCandlestickPreviewSeriesSpec = IBarSeriesSpec | IAreaSeriesSpec | ILineSeriesSpec; + /** * @description 组合蜡烛图规约 */ @@ -16,14 +18,14 @@ export interface ICombinationCandlestickChartSpec extends Omit number) | string; From c0d525a757e0d99fb4fdffcc2ddab8b53d4e6e67 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Wed, 10 Sep 2025 17:35:50 +0800 Subject: [PATCH 4/4] fix: fix the bug of no title --- .../combination-candlestick-transformer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts index aece2cd047..12017ddcea 100644 --- a/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts +++ b/packages/vchart-extension/src/charts/combination-candlestick/combination-candlestick-transformer.ts @@ -137,7 +137,7 @@ export class CombinationCandlestickChartSpecTransformer< let totalCol = 0; // 先获取总计的行和列 // 如果有 title - if (spec.title?.visible !== false) { + if (spec.title && spec.title?.visible !== false) { totalRow++; } // 图例 @@ -201,7 +201,7 @@ export class CombinationCandlestickChartSpecTransformer< right: 0 }; // 开始布局 - if (spec.title?.visible !== false) { + if (spec.title && spec.title?.visible !== false) { this._layoutOrientItem(layout, spec.title, temp, { specKey: 'title', index: 0 }, 'total'); } // 图例