| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /**
- * 图表绘制模块
- * 使用ECharts绘制各种图表
- */
-
- class ChartRenderer {
- constructor() {
- this.charts = new Map(); // 存储图表实例
- }
-
- // 创建图表容器
- createChartContainer(chartId, title) {
- const container = document.createElement('div');
- container.className = 'chart-container';
- container.innerHTML = `
- <h3>${title}</h3>
- <div class="chart" id="chart-${chartId}"></div>
- `;
- 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;
|