使用Vue和ECharts创建交互式图表的代码示例

 更新时间:2024年11月18日 08:29:52   作者:漆黑的莫莫  
在现代 Web 应用中,数据可视化是一个重要的组成部分,它不仅能够帮助用户更好地理解复杂的数据,还能提升用户体验,本文给大家使用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

代码说明

  1. 图表容器

    • 使用 ref 获取图表容器的 DOM 元素。
    • 在 onMounted 生命周期钩子中初始化 ECharts 实例并调用 updateChart 方法更新图表配置。
  2. 图表类型选择

    • 使用 v-model 绑定图表类型,并在选择改变时调用 updateChart 方法更新图表。
  3. 数据编辑

    • 提供两个模态对话框,一个用于编辑单个数据点,另一个用于编辑所有数据点。
    • 使用计算属性 selectedXAxisValue 和 selectedSeriesValue 来同步选中的数据点的 X 轴值和系列数据值。
    • 提供 addDataPoint 和 deleteDataPoint 方法来添加和删除数据点,并在操作后调用 updateChart 方法更新图表。
  4. 图表配置

    • 根据不同的图表类型(折线图、柱状图、饼图、散点图),设置不同的图表配置。
    • 使用 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交互式图表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue3中Vuex状态管理学习实战示例详解

    Vue3中Vuex状态管理学习实战示例详解

    这篇文章主要为大家介绍了Vue3中Vuex状态管理学习实战示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 详解如何实现在Vue中导入Excel文件

    详解如何实现在Vue中导入Excel文件

    这篇文章主要介绍了如何在Vue中导入Excel文件,文中的示例代码讲解详细,对我们学习或工作有一定帮助,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-01-01
  • 详解Vue生命周期和MVVM框架

    详解Vue生命周期和MVVM框架

    MVVM是Model-View-ViewModel的缩写,MVVM是一种设计思想,这篇文章主要介绍了Vue生命周期和MVVM框架,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • Vue自定义指令实现点击右键弹出菜单示例详解

    Vue自定义指令实现点击右键弹出菜单示例详解

    这篇文章主要为大家介绍了Vue自定义指令实现点击右键弹出菜单示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • vue实现自定义全局右键菜单

    vue实现自定义全局右键菜单

    这篇文章主要为大家详细介绍了vue实现自定义全局右键菜单,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • vue3使用base64加密的两种方法举例

    vue3使用base64加密的两种方法举例

    这篇文章主要给大家介绍了关于vue3使用base64加密的两种方法,我们在vue项目中有时会使用到Base6464转码,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • 前端token中4个存储位置的优缺点说明

    前端token中4个存储位置的优缺点说明

    这篇文章主要介绍了前端token中4个存储位置的优缺点说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Nuxt.js结合Serverless构建无服务器应用

    Nuxt.js结合Serverless构建无服务器应用

    Nuxt.js是一个基于Vue.js的框架,结合Serverless架构,Nuxt.js可以让你构建高度可扩展、成本效益高的无服务器应用,具有一定的参考价值,感兴趣的可以了解一下
    2024-08-08
  • 使用Vue进行数据可视化实践分享

    使用Vue进行数据可视化实践分享

    在当今的数据驱动时代,数据可视化变得越来越重要,它能够帮助我们更直观地理解数据,从而做出更好的决策,在这篇博客中,我们将探索如何使用 Vue 和一些常见的图表库(如 Chart.js)来制作漂亮的数据可视化效果,需要的朋友可以参考下
    2024-10-10
  • vue生成初始化名字相近的变量并放到数组中的示例代码

    vue生成初始化名字相近的变量并放到数组中的示例代码

    项目上有一个需求,页面上有50、60个数据变量,是依次排序递增的变量,中间有个别变量用不到,不想把这些变量直接定义在data() { }内,这篇文章主要介绍了vue生成初始化名字相近的变量并放到数组中的示例代码,需要的朋友可以参考下
    2024-08-08

最新评论