使用Vue和ECharts創(chuàng)建交互式圖表的代碼示例
引言
在現(xiàn)代 Web 應用中,數(shù)據(jù)可視化是一個重要的組成部分。它不僅能夠幫助用戶更好地理解復雜的數(shù)據(jù),還能提升用戶體驗。
技術背景
Vue.js
Vue.js 是一個漸進式 JavaScript 框架,用于構建用戶界面。它易于上手,同時提供了強大的功能來構建復雜的單頁應用。Vue 的響應式系統(tǒng)使得數(shù)據(jù)綁定變得簡單高效。
ECharts
ECharts 是一個基于 JavaScript 的開源可視化庫,由百度前端技術部開發(fā)。它提供了豐富的圖表類型和高度可定制的配置選項,適用于各種數(shù)據(jù)可視化需求。
項目搭建
首先,需要創(chuàng)建一個新的 Vue 項目。如果還沒有安裝 Vue CLI,可以通過以下命令進行安裝:
npm install -g @vue/cli
然后,創(chuàng)建一個新的 Vue 項目:
vue create my-chart-app cd my-chart-app
接下來,安裝 ECharts:
npm install echarts
代碼說明
圖表容器:
- 使用
ref獲取圖表容器的 DOM 元素。 - 在
onMounted生命周期鉤子中初始化 ECharts 實例并調(diào)用updateChart方法更新圖表配置。
- 使用
圖表類型選擇:
- 使用
v-model綁定圖表類型,并在選擇改變時調(diào)用updateChart方法更新圖表。
- 使用
數(shù)據(jù)編輯:
- 提供兩個模態(tài)對話框,一個用于編輯單個數(shù)據(jù)點,另一個用于編輯所有數(shù)據(jù)點。
- 使用計算屬性
selectedXAxisValue和selectedSeriesValue來同步選中的數(shù)據(jù)點的 X 軸值和系列數(shù)據(jù)值。 - 提供
addDataPoint和deleteDataPoint方法來添加和刪除數(shù)據(jù)點,并在操作后調(diào)用updateChart方法更新圖表。
圖表配置:
- 根據(jù)不同的圖表類型(折線圖、柱狀圖、餅圖、散點圖),設置不同的圖表配置。
- 使用
label屬性常駐顯示數(shù)值標簽,并在餅圖中使用labelLine屬性設置連接線的樣式。
代碼實現(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ù)值標簽
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/>: {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ù)值標簽
position: 'outside', // 標簽位置
formatter: ': {c} (vvxyksv9kd%)', // 自定義標簽格式
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ù)值標簽
show: true,
position: 'top', // 標簽位置
color: '#666'
},
itemStyle: { color: '#5470c6' }
}
]
};
break;
default:
option = {};
}
chartInstance.setOption(option)
console.log('option',option)
}
// 監(jiā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);
// 計算屬性:獲取選中的 X 軸值
const selectedXAxisValue = computed({
get: () => xAxisData.value[selectedDataIndex.value],
set: (newValue) => handleXAxisChange(selectedDataIndex.value, newValue)
});
// 計算屬性:獲取選中的系列數(shù)據(jù)值
const selectedSeriesValue = computed({
get: () => seriesData.value[selectedDataIndex.value].toString(),
set: (newValue) => handleSeriesChange(selectedDataIndex.value, newValue)
});
// 添加數(shù)據(jù)點
const addDataPoint = () => {
xAxisData.value.push(`新數(shù)據(jù)點 ${xAxisData.value.length + 1}`);
seriesData.value.push(0);
updateChart(); // 更新圖表以反映新增的數(shù)據(jù)點
};
// 刪除數(shù)據(jù)點
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>
<!-- 編輯所有數(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>
<!-- 單個數(shù)據(jù)點模態(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ù)點 {{ 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;">
關閉
</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ù)點
</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創(chuàng)建交互式圖表的代碼示例的文章就介紹到這了,更多相關Vue ECharts交互式圖表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
el-form-item中表單項label和表單項內(nèi)容換行實現(xiàn)方法
這篇文章主要給大家介紹了el-form-item中表單項label和表單項內(nèi)容換行實現(xiàn)的相關資料,每個表單el-form由多個表單域el-form-item組成,需要的朋友可以參考下2023-09-09
如何使用 Vue Router 的 meta 屬性實現(xiàn)多種功能
在Vue.js中,Vue Router 提供了強大的路由管理功能,通過meta屬性,我們可以在路由定義中添加自定義元數(shù)據(jù),以實現(xiàn)訪問控制、頁面標題設置、角色權限管理、頁面過渡效果,本文將總結如何使用 meta 屬性來實現(xiàn)這些常見的功能,感興趣的朋友一起看看吧2024-06-06

