使用Vue和ECharts創(chuàng)建交互式圖表的代碼示例
引言
在現(xiàn)代 Web 應(yīng)用中,數(shù)據(jù)可視化是一個(gè)重要的組成部分。它不僅能夠幫助用戶更好地理解復(fù)雜的數(shù)據(jù),還能提升用戶體驗(yàn)。
技術(shù)背景
Vue.js
Vue.js 是一個(gè)漸進(jìn)式 JavaScript 框架,用于構(gòu)建用戶界面。它易于上手,同時(shí)提供了強(qiáng)大的功能來構(gòu)建復(fù)雜的單頁應(yīng)用。Vue 的響應(yīng)式系統(tǒng)使得數(shù)據(jù)綁定變得簡單高效。
ECharts
ECharts 是一個(gè)基于 JavaScript 的開源可視化庫,由百度前端技術(shù)部開發(fā)。它提供了豐富的圖表類型和高度可定制的配置選項(xiàng),適用于各種數(shù)據(jù)可視化需求。
項(xiàng)目搭建
首先,需要?jiǎng)?chuàng)建一個(gè)新的 Vue 項(xiàng)目。如果還沒有安裝 Vue CLI,可以通過以下命令進(jìn)行安裝:
npm install -g @vue/cli
然后,創(chuàng)建一個(gè)新的 Vue 項(xiàng)目:
vue create my-chart-app cd my-chart-app
接下來,安裝 ECharts:
npm install echarts
代碼說明
圖表容器:
- 使用
ref
獲取圖表容器的 DOM 元素。 - 在
onMounted
生命周期鉤子中初始化 ECharts 實(shí)例并調(diào)用updateChart
方法更新圖表配置。
- 使用
圖表類型選擇:
- 使用
v-model
綁定圖表類型,并在選擇改變時(shí)調(diào)用updateChart
方法更新圖表。
- 使用
數(shù)據(jù)編輯:
- 提供兩個(gè)模態(tài)對話框,一個(gè)用于編輯單個(gè)數(shù)據(jù)點(diǎn),另一個(gè)用于編輯所有數(shù)據(jù)點(diǎn)。
- 使用計(jì)算屬性
selectedXAxisValue
和selectedSeriesValue
來同步選中的數(shù)據(jù)點(diǎn)的 X 軸值和系列數(shù)據(jù)值。 - 提供
addDataPoint
和deleteDataPoint
方法來添加和刪除數(shù)據(jù)點(diǎn),并在操作后調(diào)用updateChart
方法更新圖表。
圖表配置:
- 根據(jù)不同的圖表類型(折線圖、柱狀圖、餅圖、散點(diǎn)圖),設(shè)置不同的圖表配置。
- 使用
label
屬性常駐顯示數(shù)值標(biāo)簽,并在餅圖中使用labelLine
屬性設(shè)置連接線的樣式。
代碼實(shí)現(xiàn)
<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 // 定義圖表數(shù)據(jù) const xAxisData = ref(["初始階段", "開發(fā)階段", "完成階段"]) 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: ': {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: { // 常駐顯示數(shù)值標(biāo)簽 show: true, position: 'top', // 標(biāo)簽位置 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/>: {c} (vvxyksv9kd%)' }, legend: { orient: 'vertical', left: 'left', textStyle: { color: '#666' } }, xAxis: { show: false // 明確禁用 X 軸 }, yAxis: { show: false // 明確禁用 Y 軸 }, series: [ { name: '數(shù)據(jù)', type: 'pie', radius: ['40%', '70%'], avoidLabelOverlap: false, label: { show: true, // 常駐顯示數(shù)值標(biāo)簽 position: 'outside', // 標(biāo)簽位置 formatter: ': {c} (vvxyksv9kd%)', // 自定義標(biāo)簽格式 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: ': {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: { // 常駐顯示數(shù)值標(biāo)簽 show: true, position: 'top', // 標(biāo)簽位置 color: '#666' }, itemStyle: { color: '#5470c6' } } ] }; break; default: option = {}; } chartInstance.setOption(option) console.log('option',option) } // 監(jiān)聽圖表點(diǎn)擊事件 onMounted(() => { initChart() chartInstance?.on('click', (params) => { showModalSingle.value = true; selectedDataIndex.value = params.dataIndex ?? -1; }); }) // 處理 X 軸數(shù)據(jù)變化 const handleXAxisChange = (index: number, value: string) => { xAxisData.value[index] = value updateChart() } // 處理系列數(shù)據(jù)變化 const handleSeriesChange = (index: number, value: string) => { seriesData.value[index] = parseFloat(value) updateChart() } // 模態(tài)對話框狀態(tài) const showModalSingle = ref(false); const showModalAll = ref(false); const selectedDataIndex = ref(-1); // 計(jì)算屬性:獲取選中的 X 軸值 const selectedXAxisValue = computed({ get: () => xAxisData.value[selectedDataIndex.value], set: (newValue) => handleXAxisChange(selectedDataIndex.value, newValue) }); // 計(jì)算屬性:獲取選中的系列數(shù)據(jù)值 const selectedSeriesValue = computed({ get: () => seriesData.value[selectedDataIndex.value].toString(), set: (newValue) => handleSeriesChange(selectedDataIndex.value, newValue) }); // 添加數(shù)據(jù)點(diǎn) const addDataPoint = () => { xAxisData.value.push(`新數(shù)據(jù)點(diǎn) ${xAxisData.value.length + 1}`); seriesData.value.push(0); updateChart(); // 更新圖表以反映新增的數(shù)據(jù)點(diǎn) }; // 刪除數(shù)據(jù)點(diǎn) 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">散點(diǎn)圖</option> </select> <!-- 編輯所有數(shù)據(jù)按鈕 --> <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;"> 編輯所有數(shù)據(jù) </button> <!-- 單個(gè)數(shù)據(jù)點(diǎn)模態(tài)對話框 --> <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>編輯數(shù)據(jù)點(diǎn) {{ selectedDataIndex + 1 }}</h3> <div> <label>X軸數(shù)據(jù):</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>系列數(shù)據(jù):</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;"> 關(guān)閉 </button> </div> </div> <!-- 所有數(shù)據(jù)模態(tài)對話框 --> <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>編輯所有數(shù)據(jù)</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軸數(shù)據(jù)</th> <th style="padding: 8px; text-align: left; background-color: #f2f2f2; color: #333;">系列數(shù)據(jù)</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;"> 添加數(shù)據(jù)點(diǎn) </button> <button @click="showModalAll = false" style="margin-top: 10px; padding: 8px 16px; background-color: #5470c6; color: #fff; border: none; border-radius: 4px; cursor: pointer;"> 關(guān)閉 </button> </div> </div> </template>
到此這篇關(guān)于使用Vue和ECharts創(chuàng)建交互式圖表的代碼示例的文章就介紹到這了,更多相關(guān)Vue ECharts交互式圖表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue監(jiān)聽使用方法和過濾器實(shí)現(xiàn)
這篇文章主要介紹了Vue監(jiān)聽使用方法和過濾器實(shí)現(xiàn),過濾器為頁面中數(shù)據(jù)進(jìn)行強(qiáng)化,具有局部過濾器和全局過濾器2022-06-06vue實(shí)現(xiàn)滾動(dòng)條始終懸浮在頁面最下方
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)滾動(dòng)條始終懸浮在頁面最下方,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)
這篇文章主要介紹了vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)的方法,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-11-11Vue3 ref構(gòu)建響應(yīng)式變量失效問題及解決
這篇文章主要介紹了Vue3 ref構(gòu)建響應(yīng)式變量失效問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04el-form-item中表單項(xiàng)label和表單項(xiàng)內(nèi)容換行實(shí)現(xiàn)方法
這篇文章主要給大家介紹了el-form-item中表單項(xiàng)label和表單項(xiàng)內(nèi)容換行實(shí)現(xiàn)的相關(guān)資料,每個(gè)表單el-form由多個(gè)表單域el-form-item組成,需要的朋友可以參考下2023-09-09如何使用 Vue Router 的 meta 屬性實(shí)現(xiàn)多種功能
在Vue.js中,Vue Router 提供了強(qiáng)大的路由管理功能,通過meta屬性,我們可以在路由定義中添加自定義元數(shù)據(jù),以實(shí)現(xiàn)訪問控制、頁面標(biāo)題設(shè)置、角色權(quán)限管理、頁面過渡效果,本文將總結(jié)如何使用 meta 屬性來實(shí)現(xiàn)這些常見的功能,感興趣的朋友一起看看吧2024-06-06