Commit 937af939 authored by 水玉婷's avatar 水玉婷
Browse files

feat:数据过多时优化折线图,柱图,饼图,表格配置

parent 12b90ac5
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
<div class="chart-render-area"> <div class="chart-render-area">
<!-- 表格渲染 --> <!-- 表格渲染 -->
<div v-if="selectedChartType === CHART_TYPES.TABLE" class="table-render"> <div v-if="selectedChartType === CHART_TYPES.TABLE" class="table-render">
<div v-html="tableHtml" class="message-table"></div> <div v-html="tableHtml" class="message-chart"></div>
</div> </div>
<!-- 柱状图渲染 --> <!-- 柱状图渲染 -->
......
...@@ -140,19 +140,29 @@ const formatData = (data: any) => { ...@@ -140,19 +140,29 @@ const formatData = (data: any) => {
/** /**
* 基础图表配置 * 基础图表配置
*/ */
const getBaseChartConfig = () => ({ const getBaseChartConfig = (xAxisDataLength: number) => {
responsive: true, // 根据数据量动态调整底部边距,为dataZoom留出空间
animation: true, let bottomMargin = '3%';
animationDuration: 500, if (xAxisDataLength > 15) {
animationEasing: 'cubicOut' as const, bottomMargin = '8%'; // 数据项多时增加底部边距
grid: { } else if (xAxisDataLength > 10) {
left: '3%', bottomMargin = '5%';
right: '3%',
bottom: '3%',
top: '70px', // 增加顶部间距,为legend留出更多空间
containLabel: true
} }
});
return {
responsive: true,
animation: true,
animationDuration: 500,
animationEasing: 'cubicOut' as const,
grid: {
left: '3%',
right: '3%',
bottom: bottomMargin,
top: '70px', // 增加顶部间距,为legend留出更多空间
containLabel: true
}
};
};
/** /**
* 基础legend配置 - 始终放在顶部,与折线图保持一致 * 基础legend配置 - 始终放在顶部,与折线图保持一致
...@@ -245,16 +255,74 @@ const getBaseTooltipConfig = () => ({ ...@@ -245,16 +255,74 @@ const getBaseTooltipConfig = () => ({
/** /**
* 基础x轴配置 * 基础x轴配置
*/ */
const getXAxisConfig = (xAxisData: string[]) => ({ const getXAxisConfig = (xAxisData: string[]) => {
type: 'category' as const, // 根据数据量智能设置标签配置
data: xAxisData, const dataLength = xAxisData.length;
axisLabel: {
interval: 0, let rotate = 0;
rotate: xAxisData.length > 6 ? 45 : 0, let margin = 12;
margin: 8, let fontSize = 14;
fontSize: 14 let interval = 0;
if (dataLength > 20) {
// 数据项非常多时,旋转45度,增加间距
rotate = 45;
margin = 15;
fontSize = 12;
interval = 1; // 每2个显示一个标签(每隔一个显示)
} else if (dataLength > 12) {
// 数据项较多时,旋转30度
rotate = 30;
margin = 12;
fontSize = 13;
interval = 1; // 每2个显示一个标签
} else if (dataLength > 6) {
// 数据项适中时,旋转15度
rotate = 15;
margin = 10;
interval = 1; // 每2个显示一个标签
} else {
// 数据项较少时,显示所有标签
interval = 0;
} }
});
return {
type: 'category' as const,
data: xAxisData,
axisLabel: {
interval: interval,
rotate: rotate,
margin: margin,
fontSize: fontSize,
color: '#666',
fontWeight: 'normal',
align: rotate > 0 ? 'right' : 'center',
verticalAlign: rotate > 0 ? 'middle' : 'top',
overflow: 'truncate',
width: rotate > 0 ? 80 : null,
lineHeight: 16
},
axisTick: {
show: true,
alignWithLabel: true,
length: 4,
lineStyle: {
color: '#ccc',
width: 1
}
},
axisLine: {
show: true,
lineStyle: {
color: '#d9d9d9',
width: 1
}
},
splitLine: {
show: false
}
};
};
/** /**
* 基础y轴配置 * 基础y轴配置
...@@ -316,6 +384,105 @@ const getDualYAxisConfig = (yFields: string[]) => [ ...@@ -316,6 +384,105 @@ const getDualYAxisConfig = (yFields: string[]) => [
} }
]; ];
/**
* dataZoom配置
*/
const getDataZoomConfig = (xAxisDataLength: number) => {
// 当数据项超过15个时启用dataZoom
if (xAxisDataLength <= 15) {
return [];
}
// 根据数据量智能设置初始显示范围
let start = 0;
let end = 100;
if (xAxisDataLength > 30) {
// 数据项非常多时,显示约1/3的数据
start = 65;
end = 85;
} else if (xAxisDataLength > 20) {
// 数据项较多时,显示约一半的数据
start = 50;
end = 75;
} else {
// 数据项适中时,显示大部分数据
start = 30;
end = 80;
}
return [
{
show: true,
type: 'slider' as const,
realtime: true,
start: start,
end: end,
height: 25,
bottom: 5,
backgroundColor: '#f5f5f5',
dataBackground: {
lineStyle: {
color: '#ccc'
},
areaStyle: {
color: '#e8e8e8'
}
},
selectedDataBackground: {
lineStyle: {
color: '#1890ff'
},
areaStyle: {
color: '#e6f7ff'
}
},
borderColor: '#d9d9d9',
fillerColor: 'rgba(24, 144, 255, 0.2)',
handleStyle: {
color: '#1890ff',
borderColor: '#1890ff',
borderWidth: 1,
shadowBlur: 2,
shadowColor: 'rgba(0,0,0,0.1)'
},
handleSize: 12,
brushSelect: false,
showDetail: true,
showDataShadow: 'auto',
emphasis: {
handleStyle: {
color: '#096dd9',
borderColor: '#096dd9'
}
},
textStyle: {
fontSize: 11,
color: '#666'
},
alwaysShowContent: true,
label: {
show: true,
position: 'top',
formatter: '{c}%'
},
detailFormatter: (value: number) => {
return `${value}%`;
}
},
{
type: 'inside' as const,
realtime: true,
start: start,
end: end,
zoomOnMouseWheel: 'shift' as const,
moveOnMouseWheel: true,
moveOnMouseMove: true,
preventDefaultMouseMove: true
}
];
};
/** /**
* 基础系列配置 * 基础系列配置
*/ */
...@@ -351,27 +518,29 @@ const createSingleColumnOption = (chartConfig: any): echarts.EChartsOption => { ...@@ -351,27 +518,29 @@ const createSingleColumnOption = (chartConfig: any): echarts.EChartsOption => {
}); });
return { return {
...getBaseChartConfig(), ...getBaseChartConfig(xAxisData.length),
tooltip: { tooltip: {
...getBaseTooltipConfig() ...getBaseTooltipConfig()
}, },
legend: getBaseLegendConfig(groups), legend: getBaseLegendConfig(groups),
xAxis: getXAxisConfig(xAxisData), xAxis: getXAxisConfig(xAxisData),
yAxis: getYAxisConfig(), yAxis: getYAxisConfig(),
series series,
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} else { } else {
const seriesData = chartConfig.data.map((item: any) => item[chartConfig.yField]); const seriesData = chartConfig.data.map((item: any) => item[chartConfig.yField]);
return { return {
...getBaseChartConfig(), ...getBaseChartConfig(xAxisData.length),
tooltip: { tooltip: {
...getBaseTooltipConfig() ...getBaseTooltipConfig()
}, },
legend: { show: false }, legend: { show: false },
xAxis: getXAxisConfig(xAxisData), xAxis: getXAxisConfig(xAxisData),
yAxis: getYAxisConfig(), yAxis: getYAxisConfig(),
series: [getBaseSeriesConfig(chartConfig.yField, seriesData, colors[0], 0, xAxisData.length)] series: [getBaseSeriesConfig(chartConfig.yField, seriesData, colors[0], 0, xAxisData.length)],
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} }
}; };
...@@ -403,21 +572,22 @@ const createDualColumnOption = (chartConfig: any): echarts.EChartsOption => { ...@@ -403,21 +572,22 @@ const createDualColumnOption = (chartConfig: any): echarts.EChartsOption => {
}); });
return { return {
...getBaseChartConfig(), ...getBaseChartConfig(xAxisData.length),
tooltip: { tooltip: {
...getBaseTooltipConfig() ...getBaseTooltipConfig()
}, },
legend: getBaseLegendConfig([...series1, ...series2].map(s => s.name)), legend: getBaseLegendConfig([...series1, ...series2].map(s => s.name)),
xAxis: getXAxisConfig(xAxisData), xAxis: getXAxisConfig(xAxisData),
yAxis: getDualYAxisConfig(chartConfig.yFields), yAxis: getDualYAxisConfig(chartConfig.yFields),
series: [...series1, ...series2] series: [...series1, ...series2],
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} else { } else {
const series1Data = chartConfig.data.map((item: any) => item[chartConfig.yFields[0]]); const series1Data = chartConfig.data.map((item: any) => item[chartConfig.yFields[0]]);
const series2Data = chartConfig.data.map((item: any) => item[chartConfig.yFields[1]]); const series2Data = chartConfig.data.map((item: any) => item[chartConfig.yFields[1]]);
return { return {
...getBaseChartConfig(), ...getBaseChartConfig(xAxisData.length),
tooltip: { tooltip: {
...getBaseTooltipConfig() ...getBaseTooltipConfig()
}, },
...@@ -427,7 +597,8 @@ const createDualColumnOption = (chartConfig: any): echarts.EChartsOption => { ...@@ -427,7 +597,8 @@ const createDualColumnOption = (chartConfig: any): echarts.EChartsOption => {
series: [ series: [
getBaseSeriesConfig(chartConfig.yFields[0], series1Data, colors[0], 0, xAxisData.length), getBaseSeriesConfig(chartConfig.yFields[0], series1Data, colors[0], 0, xAxisData.length),
getBaseSeriesConfig(chartConfig.yFields[1], series2Data, colors[1], 1, xAxisData.length) getBaseSeriesConfig(chartConfig.yFields[1], series2Data, colors[1], 1, xAxisData.length)
] ],
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} }
}; };
......
...@@ -140,20 +140,29 @@ const formatData = (data: any) => { ...@@ -140,20 +140,29 @@ const formatData = (data: any) => {
/** /**
* 基础图表配置 * 基础图表配置
*/ */
const getBaseChartConfig = () => ({ const getBaseChartConfig = (xAxisDataLength: number) => {
responsive: true, // 根据数据量动态调整底部边距,为dataZoom留出空间
animation: true, let bottomMargin = '3%';
animationDuration: 500, if (xAxisDataLength > 15) {
animationEasing: 'cubicOut' as const, bottomMargin = '8%'; // 数据项多时增加底部边距
grid: { } else if (xAxisDataLength > 10) {
left: '2%', bottomMargin = '5%';
right: '2%',
bottom: '3%',
top: '70px', // 增加顶部间距,为legend留出更多空间
containLabel: true
} }
});
return {
responsive: true,
animation: true,
animationDuration: 500,
animationEasing: 'cubicOut' as const,
grid: {
left: '3%',
right: '3%',
bottom: bottomMargin,
top: '70px', // 增加顶部间距,为legend留出更多空间
containLabel: true
}
};
};
/** /**
* 基础legend配置 - 始终放在顶部 * 基础legend配置 - 始终放在顶部
*/ */
...@@ -245,16 +254,74 @@ const getBaseTooltipConfig = () => ({ ...@@ -245,16 +254,74 @@ const getBaseTooltipConfig = () => ({
/** /**
* 基础x轴配置 * 基础x轴配置
*/ */
const getXAxisConfig = (xAxisData: string[]) => ({ const getXAxisConfig = (xAxisData: string[]) => {
type: 'category' as const, // 根据数据量智能设置标签配置
data: xAxisData, const dataLength = xAxisData.length;
axisLabel: {
interval: 0, let rotate = 0;
rotate: xAxisData.length > 6 ? 45 : 0, let margin = 12;
margin: 8, let fontSize = 14;
fontSize: 14 let interval = 0;
if (dataLength > 20) {
// 数据项非常多时,旋转45度,增加间距
rotate = 45;
margin = 15;
fontSize = 12;
interval = 1; // 每2个显示一个标签(每隔一个显示)
} else if (dataLength > 12) {
// 数据项较多时,旋转30度
rotate = 30;
margin = 12;
fontSize = 13;
interval = 1; // 每2个显示一个标签
} else if (dataLength > 6) {
// 数据项适中时,旋转15度
rotate = 15;
margin = 10;
interval = 1; // 每2个显示一个标签
} else {
// 数据项较少时,显示所有标签
interval = 0;
} }
});
return {
type: 'category' as const,
data: xAxisData,
axisLabel: {
interval: interval,
rotate: rotate,
margin: margin,
fontSize: fontSize,
color: '#666',
fontWeight: 'normal',
align: rotate > 0 ? 'right' : 'center',
verticalAlign: rotate > 0 ? 'middle' : 'top',
overflow: 'truncate',
width: rotate > 0 ? 80 : null,
lineHeight: 16
},
axisTick: {
show: true,
alignWithLabel: true,
length: 4,
lineStyle: {
color: '#ccc',
width: 1
}
},
axisLine: {
show: true,
lineStyle: {
color: '#d9d9d9',
width: 1
}
},
splitLine: {
show: false
}
};
};
/** /**
* 基础y轴配置 * 基础y轴配置
...@@ -316,6 +383,105 @@ const getDualYAxisConfig = (yFields: string[]) => [ ...@@ -316,6 +383,105 @@ const getDualYAxisConfig = (yFields: string[]) => [
} }
]; ];
/**
* dataZoom配置
*/
const getDataZoomConfig = (xAxisDataLength: number) => {
// 当数据项超过15个时启用dataZoom
if (xAxisDataLength <= 15) {
return [];
}
// 根据数据量智能设置初始显示范围
let start = 0;
let end = 100;
if (xAxisDataLength > 30) {
// 数据项非常多时,显示约1/3的数据
start = 65;
end = 85;
} else if (xAxisDataLength > 20) {
// 数据项较多时,显示约一半的数据
start = 50;
end = 75;
} else {
// 数据项适中时,显示大部分数据
start = 30;
end = 80;
}
return [
{
show: true,
type: 'slider' as const,
realtime: true,
start: start,
end: end,
height: 25,
bottom: 5,
backgroundColor: '#f5f5f5',
dataBackground: {
lineStyle: {
color: '#ccc'
},
areaStyle: {
color: '#e8e8e8'
}
},
selectedDataBackground: {
lineStyle: {
color: '#1890ff'
},
areaStyle: {
color: '#e6f7ff'
}
},
borderColor: '#d9d9d9',
fillerColor: 'rgba(24, 144, 255, 0.2)',
handleStyle: {
color: '#1890ff',
borderColor: '#1890ff',
borderWidth: 1,
shadowBlur: 2,
shadowColor: 'rgba(0,0,0,0.1)'
},
handleSize: 12,
brushSelect: false,
showDetail: true,
showDataShadow: 'auto',
emphasis: {
handleStyle: {
color: '#096dd9',
borderColor: '#096dd9'
}
},
textStyle: {
fontSize: 11,
color: '#666'
},
alwaysShowContent: true,
label: {
show: true,
position: 'top',
formatter: '{c}%'
},
detailFormatter: (value: number) => {
return `${value}%`;
}
},
{
type: 'inside' as const,
realtime: true,
start: start,
end: end,
zoomOnMouseWheel: 'shift' as const,
moveOnMouseWheel: true,
moveOnMouseMove: true,
preventDefaultMouseMove: true
}
];
};
/** /**
* 基础系列配置 - 折线图专用 * 基础系列配置 - 折线图专用
*/ */
...@@ -364,7 +530,8 @@ const createSingleLineOption = (chartConfig: any): echarts.EChartsOption => { ...@@ -364,7 +530,8 @@ const createSingleLineOption = (chartConfig: any): echarts.EChartsOption => {
legend: getBaseLegendConfig(groups), legend: getBaseLegendConfig(groups),
xAxis: getXAxisConfig(xAxisData), xAxis: getXAxisConfig(xAxisData),
yAxis: getYAxisConfig(), yAxis: getYAxisConfig(),
series series,
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} else { } else {
const seriesData = chartConfig.data.map((item: any) => item[chartConfig.yField]); const seriesData = chartConfig.data.map((item: any) => item[chartConfig.yField]);
...@@ -375,7 +542,8 @@ const createSingleLineOption = (chartConfig: any): echarts.EChartsOption => { ...@@ -375,7 +542,8 @@ const createSingleLineOption = (chartConfig: any): echarts.EChartsOption => {
legend: { show: false }, legend: { show: false },
xAxis: getXAxisConfig(xAxisData), xAxis: getXAxisConfig(xAxisData),
yAxis: getYAxisConfig(), yAxis: getYAxisConfig(),
series: [getBaseSeriesConfig(chartConfig.yField, seriesData, colors[0])] series: [getBaseSeriesConfig(chartConfig.yField, seriesData, colors[0])],
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} }
}; };
...@@ -414,7 +582,8 @@ const createDualLineOption = (chartConfig: any): echarts.EChartsOption => { ...@@ -414,7 +582,8 @@ const createDualLineOption = (chartConfig: any): echarts.EChartsOption => {
legend: getBaseLegendConfig(legendData), legend: getBaseLegendConfig(legendData),
xAxis: getXAxisConfig(xAxisData), xAxis: getXAxisConfig(xAxisData),
yAxis: getDualYAxisConfig(chartConfig.yFields), yAxis: getDualYAxisConfig(chartConfig.yFields),
series: [...series1, ...series2] series: [...series1, ...series2],
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} else { } else {
const series1Data = chartConfig.data.map((item: any) => item[chartConfig.yFields[0]]); const series1Data = chartConfig.data.map((item: any) => item[chartConfig.yFields[0]]);
...@@ -429,7 +598,8 @@ const createDualLineOption = (chartConfig: any): echarts.EChartsOption => { ...@@ -429,7 +598,8 @@ const createDualLineOption = (chartConfig: any): echarts.EChartsOption => {
series: [ series: [
getBaseSeriesConfig(chartConfig.yFields[0], series1Data, colors[0], 0), getBaseSeriesConfig(chartConfig.yFields[0], series1Data, colors[0], 0),
getBaseSeriesConfig(chartConfig.yFields[1], series2Data, colors[1], 1) getBaseSeriesConfig(chartConfig.yFields[1], series2Data, colors[1], 1)
] ],
dataZoom: getDataZoomConfig(xAxisData.length)
}; };
} }
}; };
......
...@@ -368,23 +368,23 @@ const getResponsiveLayout = (isManyItems: boolean) => { ...@@ -368,23 +368,23 @@ const getResponsiveLayout = (isManyItems: boolean) => {
if (screenWidth < 768) { // 小屏幕 if (screenWidth < 768) { // 小屏幕
return { return {
top: isManyItems ? '15%' : '10%', top: isManyItems ? '5%' : '10%',
left: '5%', left: '5%',
right: '5%', right: '5%',
radius: isManyItems ? ['25%', '50%'] : ['35%', '60%'] radius: isManyItems ? ['35%', '80%'] : ['35%', '80%']
}; };
} else if (screenWidth < 1200) { // 中等屏幕 } else if (screenWidth < 1200) { // 中等屏幕
return { return {
top: isManyItems ? '15%' : '10%', top: isManyItems ? '5%' : '10%',
left: '25%', left: '25%',
right: '25%', right: '25%',
radius: isManyItems ? ['30%', '55%'] : ['40%', '65%'] radius: isManyItems ? ['35%', '80%'] : ['40%', '80%']
}; };
} else { // 大屏幕 } else { // 大屏幕
return { return {
left: '30%', left: '30%',
right: '30%', right: '30%',
radius: isManyItems ? ['35%', '60%'] : ['45%', '70%'] radius: isManyItems ? ['35%', '80%'] : ['45%', '80%']
}; };
} }
}; };
......
...@@ -876,7 +876,13 @@ li { ...@@ -876,7 +876,13 @@ li {
} }
.table-title { .table-title {
font-size: 14px; font-size: 16px;
font-weight: bold;
margin-bottom: 8px;
color: #333;
width: 100%;
box-sizing: border-box;
text-align: center;
} }
.table-summary { .table-summary {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment