/** * 图表绘制模块 * 使用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;