/**
* 图表绘制模块
* 使用ECharts绘制各种图表
*/
class ChartRenderer {
constructor() {
this.charts = new Map(); // 存储图表实例
}
// 创建图表容器
createChartContainer(chartId, title) {
const container = document.createElement('div');
container.className = 'chart-container';
container.innerHTML = `
${title}
`;
return container;
}
// 初始化图表
initChart(chartId, options = {}) {
const chartDom = document.getElementById(`chart-${chartId}`);
if (!chartDom) {
console.error(`图表容器 ${chartId} 不存在`);
return null;
}
// 清除之前的图表实例
if (this.charts.has(chartId)) {
this.charts.get(chartId).dispose();
}
const chart = echarts.init(chartDom);
this.charts.set(chartId, chart);
// 应用配置
chart.setOption(options);
// 响应式处理
const resizeHandler = () => {
chart.resize();
};
window.addEventListener('resize', resizeHandler);
// 保存resize处理函数以便后续清理
chartDom._resizeHandler = resizeHandler;
return chart;
}
// 销毁图表
destroyChart(chartId) {
const chart = this.charts.get(chartId);
if (chart) {
chart.dispose();
this.charts.delete(chartId);
// 移除resize监听器
const chartDom = document.getElementById(`chart-${chartId}`);
if (chartDom && chartDom._resizeHandler) {
window.removeEventListener('resize', chartDom._resizeHandler);
}
}
}
// 绘制折线图
renderLineChart(chartId, data, options = {}) {
const defaultOptions = {
title: {
text: options.title || '趋势图',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: options.legendData || ['数据'],
top: 'bottom'
},
xAxis: {
type: 'category',
data: data.map(item => item[options.xAxis || 'timestamp']),
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: options.yAxisName || '数值'
},
series: options.seriesData || [{
name: '数据',
type: 'line',
data: data.map(item => item.value),
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
width: 2
},
areaStyle: {
opacity: 0.1
}
}],
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
}
};
const mergedOptions = this.mergeOptions(defaultOptions, options);
this.initChart(chartId, mergedOptions);
}
// 绘制柱状图
renderBarChart(chartId, data, options = {}) {
const defaultOptions = {
title: {
text: options.title || '柱状图',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: data.map(item => item[options.xAxis || 'category']),
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: options.yAxisName || '数值'
},
series: [{
name: options.seriesName || '数据',
type: 'bar',
data: data.map(item => item.value),
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2378f7' },
{ offset: 0.7, color: '#2378f7' },
{ offset: 1, color: '#83bff6' }
])
}
}
}],
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
}
};
const mergedOptions = this.mergeOptions(defaultOptions, options);
this.initChart(chartId, mergedOptions);
}
// 绘制饼图
renderPieChart(chartId, data, options = {}) {
const defaultOptions = {
title: {
text: options.title || '饼图',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
top: 'middle'
},
series: [{
name: options.seriesName || '数据',
type: 'pie',
radius: options.radius || ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '20',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: data.map(item => ({
name: item.name,
value: item.value
}))
}]
};
const mergedOptions = this.mergeOptions(defaultOptions, options);
this.initChart(chartId, mergedOptions);
}
// 绘制散点图
renderScatterChart(chartId, data, options = {}) {
const defaultOptions = {
title: {
text: options.title || '散点图',
left: 'center'
},
tooltip: {
trigger: 'item'
},
xAxis: {
type: 'value',
name: options.xAxisName || 'X轴'
},
yAxis: {
type: 'value',
name: options.yAxisName || 'Y轴'
},
series: [{
name: options.seriesName || '数据',
type: 'scatter',
data: data.map(item => [item.x, item.y]),
symbolSize: function (data) {
return Math.sqrt(data[2]) || 10;
},
itemStyle: {
color: new echarts.graphic.RadialGradient(0.5, 0.5, 1, [
{ offset: 0, color: 'rgba(58,77,233,0.8)' },
{ offset: 1, color: 'rgba(58,77,233,0.1)' }
])
}
}]
};
const mergedOptions = this.mergeOptions(defaultOptions, options);
this.initChart(chartId, mergedOptions);
}
// 绘制面积图
renderAreaChart(chartId, data, options = {}) {
const defaultOptions = {
title: {
text: options.title || '面积图',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: options.legendData || ['数据'],
top: 'bottom'
},
xAxis: {
type: 'category',
data: data.map(item => item[options.xAxis || 'timestamp']),
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: options.yAxisName || '数值'
},
series: [{
name: options.seriesName || '数据',
type: 'line',
data: data.map(item => item.value),
smooth: true,
symbol: 'none',
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(128, 255, 165, 0.3)' },
{ offset: 1, color: 'rgba(1, 191, 236, 0.1)' }
])
}
}],
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
}
};
const mergedOptions = this.mergeOptions(defaultOptions, options);
this.initChart(chartId, mergedOptions);
}
// 绘制仪表盘
renderGaugeChart(chartId, data, options = {}) {
const defaultOptions = {
title: {
text: options.title || '仪表盘',
left: 'center'
},
tooltip: {
formatter: '{b}: {c}%'
},
series: [{
name: options.seriesName || '数据',
type: 'gauge',
min: options.min || 0,
max: options.max || 100,
splitNumber: options.splitNumber || 10,
radius: options.radius || '80%',
axisLine: {
lineStyle: {
width: 10,
color: [
[0.3, '#ff6e76'],
[0.7, '#fddd60'],
[1, '#7cffb2']
]
}
},
pointer: {
itemStyle: {
color: 'auto'
}
},
axisTick: {
distance: -15,
length: 8,
lineStyle: {
color: '#999',
width: 2
}
},
splitLine: {
distance: -20,
length: 15,
lineStyle: {
color: '#999',
width: 3
}
},
axisLabel: {
color: '#999',
distance: 30,
fontSize: 12
},
detail: {
valueAnimation: true,
fontSize: 20,
offsetCenter: [0, '60%']
},
data: [{
value: data.value || 0,
name: options.name || '数据'
}]
}]
};
const mergedOptions = this.mergeOptions(defaultOptions, options);
this.initChart(chartId, mergedOptions);
}
// 绘制表格
renderTableChart(chartId, data, options = {}) {
const defaultOptions = {
title: {
text: options.title || '表格',
left: 'center'
},
tooltip: {
show: false
},
grid: {
top: '10%',
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: data.map(item => item[options.xAxis || 'name']),
axisLabel: {
interval: 0,
rotate: 45
}
},
yAxis: {
type: 'value'
},
series: [{
name: options.seriesName || '数据',
type: 'bar',
data: data.map(item => item.value),
itemStyle: {
color: '#5470c6'
}
}]
};
const mergedOptions = this.mergeOptions(defaultOptions, options);
this.initChart(chartId, defaultOptions); // 表格不需要复杂配置
}
// 合并配置选项
mergeOptions(defaultOptions, customOptions) {
return {
...defaultOptions,
...customOptions,
series: customOptions.series || defaultOptions.series
};
}
// 根据图表类型渲染图表
renderChartByType(chartId, chartData) {
const { chart_type: chartType, data, options = {} } = chartData;
switch (chartType) {
case 'line':
this.renderLineChart(chartId, data, options);
break;
case 'bar':
this.renderBarChart(chartId, data, options);
break;
case 'pie':
this.renderPieChart(chartId, data, options);
break;
case 'scatter':
this.renderScatterChart(chartId, data, options);
break;
case 'area':
this.renderAreaChart(chartId, data, options);
break;
case 'gauge':
this.renderGaugeChart(chartId, data, options);
break;
case 'table':
this.renderTableChart(chartId, data, options);
break;
default:
// 默认使用折线图
this.renderLineChart(chartId, data, options);
}
}
// 批量渲染图表
renderCharts(chartsData) {
chartsData.forEach(chartData => {
const { chart_id: chartId, chart_name: chartName, chart_type: chartType, data, options = {} } = chartData;
const chartContainer = this.createChartContainer(chartId, chartName);
document.getElementById('chartsContainer').appendChild(chartContainer);
this.renderChartByType(chartId, {
chart_type: chartType,
data,
options: {
...options,
title: chartName
}
});
});
}
// 清理所有图表
clearAllCharts() {
this.charts.forEach((chart, chartId) => {
this.destroyChart(chartId);
});
const chartsContainer = document.getElementById('chartsContainer');
if (chartsContainer) {
chartsContainer.innerHTML = '';
}
}
}
// 创建全局图表渲染器实例
const chartRenderer = new ChartRenderer();
export default chartRenderer;