fix: ensure complete StyleConfig defaults in ECharts builder
Root cause: backend returns minimal style object without title/legend/axis sub-objects, causing TypeError 'Cannot read properties of undefined'. Fix: normalize style with full defaults at the builder entry point, protecting ALL chart type builders uniformly. Also fix antd deprecation warnings (destroyOnClose -> destroyOnHidden). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b7edc7f86e
commit
36c5b42f53
|
|
@ -1,5 +1,6 @@
|
|||
import { type IChartRenderer } from '@/application/ports/output/IChartRenderer';
|
||||
import { type ChartInstance } from '@/domain/entities/ChartInstance';
|
||||
import { type StyleConfig } from '@/domain/entities/StyleConfig';
|
||||
import { buildBarOption } from './barOptionBuilder';
|
||||
import { buildLineOption } from './lineOptionBuilder';
|
||||
import { buildPieOption } from './pieOptionBuilder';
|
||||
|
|
@ -10,44 +11,64 @@ import { buildMapOption } from './mapOptionBuilder';
|
|||
import { buildWordcloudOption } from './wordcloudOptionBuilder';
|
||||
import { buildComboOption } from './comboOptionBuilder';
|
||||
|
||||
const DEFAULT_STYLE: StyleConfig = {
|
||||
title: { text: '', visible: false, fontSize: 16, fontWeight: 'bold', color: '#333', align: 'left' },
|
||||
colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4'],
|
||||
legend: { visible: true, position: 'top', orient: 'horizontal', fontSize: 12, color: '#666' },
|
||||
xAxis: { visible: true, labelVisible: true, labelFontSize: 12, labelColor: '#666', labelRotation: 0, titleVisible: false, titleText: '', gridVisible: true, gridColor: '#eee' },
|
||||
yAxis: { visible: true, labelVisible: true, labelFontSize: 12, labelColor: '#666', labelRotation: 0, titleVisible: false, titleText: '', gridVisible: true, gridColor: '#eee' },
|
||||
dataLabel: { visible: false, fontSize: 12, color: '#333', position: 'outside', format: 'value' },
|
||||
background: { color: 'transparent', opacity: 1, borderRadius: 4 },
|
||||
border: { visible: false, color: '#ddd', width: 1, style: 'solid' },
|
||||
animation: { enabled: true, duration: 500, easing: 'ease-out' },
|
||||
};
|
||||
|
||||
function ensureStyle(style: any): StyleConfig {
|
||||
if (!style || typeof style !== 'object') return DEFAULT_STYLE;
|
||||
return {
|
||||
title: { ...DEFAULT_STYLE.title, ...(style.title ?? {}) },
|
||||
colors: style.colors ?? DEFAULT_STYLE.colors,
|
||||
legend: { ...DEFAULT_STYLE.legend, ...(style.legend ?? {}) },
|
||||
xAxis: { ...DEFAULT_STYLE.xAxis, ...(style.xAxis ?? {}) },
|
||||
yAxis: { ...DEFAULT_STYLE.yAxis, ...(style.yAxis ?? {}) },
|
||||
dataLabel: { ...DEFAULT_STYLE.dataLabel, ...(style.dataLabel ?? {}) },
|
||||
background: { ...DEFAULT_STYLE.background, ...(style.background ?? {}) },
|
||||
border: { ...DEFAULT_STYLE.border, ...(style.border ?? {}) },
|
||||
animation: { ...DEFAULT_STYLE.animation, ...(style.animation ?? {}) },
|
||||
};
|
||||
}
|
||||
|
||||
export class EChartsOptionBuilder implements IChartRenderer {
|
||||
build(chart: ChartInstance, data: Record<string, any>[]): Record<string, any> {
|
||||
switch (chart.type) {
|
||||
const safeChart = { ...chart, style: ensureStyle(chart.style) };
|
||||
|
||||
switch (safeChart.type) {
|
||||
case 'bar':
|
||||
case 'grouped-bar':
|
||||
case 'stacked-bar':
|
||||
case 'horizontal-bar':
|
||||
return buildBarOption(chart, data);
|
||||
|
||||
return buildBarOption(safeChart, data);
|
||||
case 'line':
|
||||
case 'area':
|
||||
return buildLineOption(chart, data);
|
||||
|
||||
return buildLineOption(safeChart, data);
|
||||
case 'pie':
|
||||
case 'donut':
|
||||
return buildPieOption(chart, data);
|
||||
|
||||
return buildPieOption(safeChart, data);
|
||||
case 'scatter':
|
||||
case 'boston-matrix':
|
||||
return buildScatterOption(chart, data);
|
||||
|
||||
return buildScatterOption(safeChart, data);
|
||||
case 'radar':
|
||||
return buildRadarOption(chart, data);
|
||||
|
||||
return buildRadarOption(safeChart, data);
|
||||
case 'heatmap':
|
||||
return buildHeatmapOption(chart, data);
|
||||
|
||||
return buildHeatmapOption(safeChart, data);
|
||||
case 'map':
|
||||
return buildMapOption(chart, data);
|
||||
|
||||
return buildMapOption(safeChart, data);
|
||||
case 'wordcloud':
|
||||
return buildWordcloudOption(chart, data);
|
||||
|
||||
return buildWordcloudOption(safeChart, data);
|
||||
case 'combo':
|
||||
return buildComboOption(chart, data);
|
||||
|
||||
return buildComboOption(safeChart, data);
|
||||
default:
|
||||
throw new Error(`Unsupported chart type: ${chart.type}`);
|
||||
return { title: { text: 'Unsupported chart type' } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export default function ImportModal() {
|
|||
cancelText="取消"
|
||||
width={currentStep === 1 ? 960 : 600}
|
||||
okButtonProps={{ disabled: currentStep === 0 }}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ export const ExportDialog: React.FC = () => {
|
|||
</Button>
|
||||
</Space>
|
||||
}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
>
|
||||
<Spin spinning={loading} tip="正在导出...">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export const TemplateModal: React.FC = () => {
|
|||
okText={activeTab === 'save' ? '保存模板' : '确定'}
|
||||
cancelText="关闭"
|
||||
width={720}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
>
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
|
|
|
|||
Loading…
Reference in New Issue