使用Vue和ECharts創(chuàng)建交互式圖表的代碼示例
引言
在現(xiàn)代 Web 應(yīng)用中,數(shù)據(jù)可視化是一個(gè)重要的組成部分。它不僅能夠幫助用戶(hù)更好地理解復(fù)雜的數(shù)據(jù),還能提升用戶(hù)體驗(yàn)。
技術(shù)背景
Vue.js
Vue.js 是一個(gè)漸進(jìn)式 JavaScript 框架,用于構(gòu)建用戶(hù)界面。它易于上手,同時(shí)提供了強(qiáng)大的功能來(lái)構(gòu)建復(fù)雜的單頁(yè)應(yīng)用。Vue 的響應(yīng)式系統(tǒng)使得數(shù)據(jù)綁定變得簡(jiǎn)單高效。
ECharts
ECharts 是一個(gè)基于 JavaScript 的開(kāi)源可視化庫(kù),由百度前端技術(shù)部開(kāi)發(fā)。它提供了豐富的圖表類(lèi)型和高度可定制的配置選項(xiàng),適用于各種數(shù)據(jù)可視化需求。
項(xiàng)目搭建
首先,需要?jiǎng)?chuàng)建一個(gè)新的 Vue 項(xiàng)目。如果還沒(méi)有安裝 Vue CLI,可以通過(guò)以下命令進(jìn)行安裝:
npm install -g @vue/cli
然后,創(chuàng)建一個(gè)新的 Vue 項(xiàng)目:
vue create my-chart-app cd my-chart-app
接下來(lái),安裝 ECharts:
npm install echarts
代碼說(shuō)明
圖表容器:
- 使用
ref獲取圖表容器的 DOM 元素。 - 在
onMounted生命周期鉤子中初始化 ECharts 實(shí)例并調(diào)用updateChart方法更新圖表配置。
- 使用
圖表類(lèi)型選擇:
- 使用
v-model綁定圖表類(lèi)型,并在選擇改變時(shí)調(diào)用updateChart方法更新圖表。
- 使用
數(shù)據(jù)編輯:
- 提供兩個(gè)模態(tài)對(duì)話(huà)框,一個(gè)用于編輯單個(gè)數(shù)據(jù)點(diǎn),另一個(gè)用于編輯所有數(shù)據(jù)點(diǎn)。
- 使用計(jì)算屬性
selectedXAxisValue和selectedSeriesValue來(lái)同步選中的數(shù)據(jù)點(diǎn)的 X 軸值和系列數(shù)據(jù)值。 - 提供
addDataPoint和deleteDataPoint方法來(lái)添加和刪除數(shù)據(jù)點(diǎn),并在操作后調(diào)用updateChart方法更新圖表。
圖表配置:
- 根據(jù)不同的圖表類(lèi)型(折線(xiàn)圖、柱狀圖、餅圖、散點(diǎn)圖),設(shè)置不同的圖表配置。
- 使用
label屬性常駐顯示數(shù)值標(biāo)簽,并在餅圖中使用labelLine屬性設(shè)置連接線(xiàn)的樣式。
代碼實(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(["初始階段", "開(kāi)發(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} (0aocqws%)'
},
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} (wqacess%)', // 自定義標(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)聽(tīng)圖表點(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)對(duì)話(huà)框狀態(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>
<!-- 圖表類(lèi)型選擇 -->
<select v-model="chartType" @change="updateChart" style="margin-top: 20px; padding: 8px; border: 1px solid #ccc; border-radius: 4px;">
<option value="line">折線(xiàn)圖</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)對(duì)話(huà)框 -->
<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)對(duì)話(huà)框 -->
<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;">序號(hào)</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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue監(jiān)聽(tīng)使用方法和過(guò)濾器實(shí)現(xiàn)
這篇文章主要介紹了Vue監(jiān)聽(tīng)使用方法和過(guò)濾器實(shí)現(xiàn),過(guò)濾器為頁(yè)面中數(shù)據(jù)進(jìn)行強(qiáng)化,具有局部過(guò)濾器和全局過(guò)濾器2022-06-06
vue插槽slot的簡(jiǎn)單理解與用法實(shí)例分析
這篇文章主要介紹了vue插槽slot的簡(jiǎn)單理解與用法,結(jié)合實(shí)例形式分析了vue插槽slot的功能、原理、相關(guān)使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2020-03-03
vue實(shí)現(xiàn)滾動(dòng)條始終懸浮在頁(yè)面最下方
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)滾動(dòng)條始終懸浮在頁(yè)面最下方,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)
這篇文章主要介紹了vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)的方法,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2020-11-11
Vue3 ref構(gòu)建響應(yīng)式變量失效問(wèn)題及解決
這篇文章主要介紹了Vue3 ref構(gòu)建響應(yīng)式變量失效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
el-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)大的路由管理功能,通過(guò)meta屬性,我們可以在路由定義中添加自定義元數(shù)據(jù),以實(shí)現(xiàn)訪問(wèn)控制、頁(yè)面標(biāo)題設(shè)置、角色權(quán)限管理、頁(yè)面過(guò)渡效果,本文將總結(jié)如何使用 meta 屬性來(lái)實(shí)現(xiàn)這些常見(jiàn)的功能,感興趣的朋友一起看看吧2024-06-06

