使用Vue和ECharts创建交互式图表的代码示例
引言
在现代 Web 应用中,数据可视化是一个重要的组成部分。它不仅能够帮助用户更好地理解复杂的数据,还能提升用户体验。
技术背景
Vue.js
Vue.js 是一个渐进式 JavaScript 框架,用于构建用户界面。它易于上手,同时提供了强大的功能来构建复杂的单页应用。Vue 的响应式系统使得数据绑定变得简单高效。
ECharts
ECharts 是一个基于 JavaScript 的开源可视化库,由百度前端技术部开发。它提供了丰富的图表类型和高度可定制的配置选项,适用于各种数据可视化需求。
项目搭建
首先,需要创建一个新的 Vue 项目。如果还没有安装 Vue CLI,可以通过以下命令进行安装:
npm install -g @vue/cli
然后,创建一个新的 Vue 项目:
vue create my-chart-app cd my-chart-app
接下来,安装 ECharts:
npm install echarts
代码说明
图表容器:
- 使用
ref
获取图表容器的 DOM 元素。 - 在
onMounted
生命周期钩子中初始化 ECharts 实例并调用updateChart
方法更新图表配置。
- 使用
图表类型选择:
- 使用
v-model
绑定图表类型,并在选择改变时调用updateChart
方法更新图表。
- 使用
数据编辑:
- 提供两个模态对话框,一个用于编辑单个数据点,另一个用于编辑所有数据点。
- 使用计算属性
selectedXAxisValue
和selectedSeriesValue
来同步选中的数据点的 X 轴值和系列数据值。 - 提供
addDataPoint
和deleteDataPoint
方法来添加和删除数据点,并在操作后调用updateChart
方法更新图表。
图表配置:
- 根据不同的图表类型(折线图、柱状图、饼图、散点图),设置不同的图表配置。
- 使用
label
属性常驻显示数值标签,并在饼图中使用labelLine
属性设置连接线的样式。
代码实现
<script setup lang="ts"> import { defineComponent, onMounted, ref, computed } from 'vue' import * as echarts from 'echarts' // 定义图表容器引用 const chartRef = ref<HTMLDivElement | null>(null) let chartInstance: echarts.ECharts | null = null // 定义图表数据 const xAxisData = ref(["初始阶段", "开发阶段", "完成阶段"]) const seriesData = ref([10, 50, 80]) const chartType = ref('line') // 初始化图表 const initChart = () => { if (!chartRef.value) return chartInstance = echarts.init(chartRef.value) updateChart() } // 更新图表配置 const updateChart = () => { if (!chartInstance) return let option; switch (chartType.value) { case 'line': case 'bar': option = { tooltip: { trigger: 'axis', formatter: '{b}: {c}' }, legend: { orient: 'vertical', left: 'left', textStyle: { color: '#666' } }, xAxis: { show: true, type: 'category', data: xAxisData.value, axisLine: { lineStyle: { color: '#999' } }, axisLabel: { color: '#666' } }, yAxis: { show: true, type: 'value', axisLine: { lineStyle: { color: '#999' } }, splitLine: { lineStyle: { color: ['#eaeaea'], width: 1, type: 'dashed' } }, axisLabel: { color: '#666' } }, series: [ { data: seriesData.value, type: chartType.value, itemStyle: { color: '#5470c6' }, label: { // 常驻显示数值标签 show: true, position: 'top', // 标签位置 color: '#666' }, ...(chartType.value === 'line' ? { areaStyle: { color: 'rgba(84, 112, 198, 0.3)' } } : {}) } ], grid: { left: '5%', right: '5%', bottom: '10%' } }; break; case 'pie': option = { tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' }, legend: { orient: 'vertical', left: 'left', textStyle: { color: '#666' } }, xAxis: { show: false // 明确禁用 X 轴 }, yAxis: { show: false // 明确禁用 Y 轴 }, series: [ { name: '数据', type: 'pie', radius: ['40%', '70%'], avoidLabelOverlap: false, label: { show: true, // 常驻显示数值标签 position: 'outside', // 标签位置 formatter: '{b}: {c} ({d}%)', // 自定义标签格式 color: '#666' }, emphasis: { label: { show: true, fontSize: '20', fontWeight: 'bold' } }, data: xAxisData.value.map((name, index) => ({ name, value: seriesData.value[index], itemStyle: { color: ['#5470c6', '#91cc75', '#fac858'][index % 3] } })) } ] }; break; case 'scatter': option = { tooltip: { trigger: 'item', formatter: '{b}: {c}' }, legend: { orient: 'vertical', left: 'left', textStyle: { color: '#666' } }, xAxis: { show: true, type: 'category', data: xAxisData.value, axisLine: { lineStyle: { color: '#999' } }, axisLabel: { color: '#666' } }, yAxis: { show: true, type: 'value', axisLine: { lineStyle: { color: '#999' } }, splitLine: { lineStyle: { color: ['#eaeaea'], width: 1, type: 'dashed' } }, axisLabel: { color: '#666' } }, series: [ { symbolSize: 20, data: xAxisData.value.map((name, index) => [index, seriesData.value[index]]), type: 'scatter', label: { // 常驻显示数值标签 show: true, position: 'top', // 标签位置 color: '#666' }, itemStyle: { color: '#5470c6' } } ] }; break; default: option = {}; } chartInstance.setOption(option) console.log('option',option) } // 监听图表点击事件 onMounted(() => { initChart() chartInstance?.on('click', (params) => { showModalSingle.value = true; selectedDataIndex.value = params.dataIndex ?? -1; }); }) // 处理 X 轴数据变化 const handleXAxisChange = (index: number, value: string) => { xAxisData.value[index] = value updateChart() } // 处理系列数据变化 const handleSeriesChange = (index: number, value: string) => { seriesData.value[index] = parseFloat(value) updateChart() } // 模态对话框状态 const showModalSingle = ref(false); const showModalAll = ref(false); const selectedDataIndex = ref(-1); // 计算属性:获取选中的 X 轴值 const selectedXAxisValue = computed({ get: () => xAxisData.value[selectedDataIndex.value], set: (newValue) => handleXAxisChange(selectedDataIndex.value, newValue) }); // 计算属性:获取选中的系列数据值 const selectedSeriesValue = computed({ get: () => seriesData.value[selectedDataIndex.value].toString(), set: (newValue) => handleSeriesChange(selectedDataIndex.value, newValue) }); // 添加数据点 const addDataPoint = () => { xAxisData.value.push(`新数据点 ${xAxisData.value.length + 1}`); seriesData.value.push(0); updateChart(); // 更新图表以反映新增的数据点 }; // 删除数据点 const deleteDataPoint = (index: number) => { xAxisData.value.splice(index, 1); seriesData.value.splice(index, 1); updateChart(); }; </script> <template> <!-- 图表容器 --> <div ref="chartRef" :style="{ width: '100%', height: '400px', backgroundColor: '#fff', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)' }"></div> <!-- 图表类型选择 --> <select v-model="chartType" @change="updateChart" style="margin-top: 20px; padding: 8px; border: 1px solid #ccc; border-radius: 4px;"> <option value="line">折线图</option> <option value="bar">柱状图</option> <option value="pie">饼图</option> <option value="scatter">散点图</option> </select> <!-- 编辑所有数据按钮 --> <button @click="showModalAll = true" style="margin-top: 20px; margin-left: 10px; padding: 8px 16px; background-color: #5470c6; color: #fff; border: none; border-radius: 4px; cursor: pointer;"> 编辑所有数据 </button> <!-- 单个数据点模态对话框 --> <div v-if="showModalSingle" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center;"> <div style="background-color: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"> <h3>编辑数据点 {{ selectedDataIndex + 1 }}</h3> <div> <label>X轴数据:</label> <input :value="selectedXAxisValue" @input="selectedXAxisValue = ($event.target as HTMLInputElement).value" style="width: 100%; padding: 8px; margin-top: 5px; border: 1px solid #ccc; border-radius: 4px;" /> </div> <div> <label>系列数据:</label> <input :value="selectedSeriesValue" @input="selectedSeriesValue = ($event.target as HTMLInputElement).value" style="width: 100%; padding: 8px; margin-top: 5px; border: 1px solid #ccc; border-radius: 4px;" /> </div> <button @click="showModalSingle = false" style="margin-top: 10px; padding: 8px 16px; background-color: #5470c6; color: #fff; border: none; border-radius: 4px; cursor: pointer;"> 关闭 </button> </div> </div> <!-- 所有数据模态对话框 --> <div v-if="showModalAll" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center;"> <div style="background-color: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); width: 80%; max-width: 600px;"> <h3>编辑所有数据</h3> <table style="width: 100%; border-collapse: collapse;"> <thead> <tr> <th style="padding: 8px; text-align: left; background-color: #f2f2f2; color: #333;">序号</th> <th style="padding: 8px; text-align: left; background-color: #f2f2f2; color: #333;">X轴数据</th> <th style="padding: 8px; text-align: left; background-color: #f2f2f2; color: #333;">系列数据</th> <th style="padding: 8px; text-align: left; background-color: #f2f2f2; color: #333;">操作</th> </tr> </thead> <tbody> <tr v-for="(item, index) in xAxisData" :key="index"> <td style="border-bottom: 1px solid #ddd; padding: 8px;">{{ index + 1 }}</td> <td style="border-bottom: 1px solid #ddd; padding: 8px;"> <input :value="xAxisData[index]" @input="handleXAxisChange(index, ($event.target as HTMLInputElement).value)" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;" /> </td> <td style="border-bottom: 1px solid #ddd; padding: 8px;"> <input :value="seriesData[index]" @input="handleSeriesChange(index, ($event.target as HTMLInputElement).value)" style="width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px;" /> </td> <td style="border-bottom: 1px solid #ddd; padding: 8px;"> <button @click="deleteDataPoint(index)" style="padding: 8px 16px; background-color: #ff4d4f; color: #fff; border: none; border-radius: 4px; cursor: pointer;"> 删除 </button> </td> </tr> </tbody> </table> <button @click="addDataPoint" style="margin-top: 10px; padding: 8px 16px; background-color: #5470c6; color: #fff; border: none; border-radius: 4px; cursor: pointer;"> 添加数据点 </button> <button @click="showModalAll = false" style="margin-top: 10px; padding: 8px 16px; background-color: #5470c6; color: #fff; border: none; border-radius: 4px; cursor: pointer;"> 关闭 </button> </div> </div> </template>
到此这篇关于使用Vue和ECharts创建交互式图表的代码示例的文章就介绍到这了,更多相关Vue ECharts交互式图表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论