前端数据可视化大屏生成器:配置 JSON → 自动生成图表大屏
前端数据可视化大屏生成器:配置 JSON → 自动生成图表大屏
·
文章目录
在低代码、可视化热潮的推动下,越来越多的前端项目希望实现“通过配置 JSON 自动生成图表大屏”的能力。本项目基于 Vue 3 和 ECharts 实现一个数据可视化大屏编辑器,支持用户通过 JSON 结构快速构建并渲染复杂图表界面,极大降低图表搭建成本。
一、核心思路概览
1. 数据结构设计
我们将可视化大屏拆解为以下两部分配置:
- layout:图表在大屏中的位置与大小(结合
vue-grid-layout
实现自由拖拽) - charts:图表的类型、标题、绑定数据等核心信息
示例配置结构如下:
{
"layout": [
{ "i": "chart1", "x": 0, "y": 0, "w": 6, "h": 4 },
{ "i": "chart2", "x": 6, "y": 0, "w": 6, "h": 4 }
],
"charts": [
{
"id": "chart1",
"type": "bar",
"title": "销售额柱状图",
"data": {
"x": ["Q1", "Q2", "Q3", "Q4"],
"y": [120, 200, 150, 80]
}
},
{
"id": "chart2",
"type": "line",
"title": "趋势折线图",
"data": {
"x": ["Jan", "Feb", "Mar", "Apr"],
"y": [30, 50, 45, 60]
}
}
]
}
二、组件结构拆解
我们将整个项目拆分为如下几个核心组件:
ChartRenderer.vue
:图表渲染器,负责渲染不同类型的 ECharts 图表LayoutGrid.vue
:基于vue-grid-layout
实现的大屏布局容器ScreenEditor.vue
:主页面,载入 JSON 配置 → 渲染布局和图表
三、核心组件代码示例
1. ChartRenderer.vue
<template>
<div ref="chartRef" class="chart-container"></div>
</template>
<script setup>
import { onMounted, watch, ref } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
chart: Object
})
const chartRef = ref()
watch(
() => props.chart,
() => {
renderChart()
},
{ immediate: true }
)
function renderChart() {
if (!chartRef.value) return
const chartInstance = echarts.init(chartRef.value)
const { type, data, title } = props.chart
const option = {
title: { text: title },
tooltip: {},
xAxis: { data: data.x },
yAxis: {},
series: [{ type, data: data.y }]
}
chartInstance.setOption(option)
}
</script>
<style scoped>
.chart-container {
width: 100%;
height: 100%;
}
</style>
2. LayoutGrid.vue
<template>
<GridLayout
v-model:layout="layout"
:col-num="12"
:row-height="30"
:is-draggable="true"
:is-resizable="true"
:margin="[10, 10]"
:use-css-transforms="true"
>
<GridItem
v-for="item in layout"
:key="item.i"
v-bind="item"
>
<ChartRenderer :chart="findChartById(item.i)" />
</GridItem>
</GridLayout>
</template>
<script setup>
import { GridLayout, GridItem } from "vue-grid-layout"
import { useChartConfig } from "@/composables/useChartConfig"
import ChartRenderer from "./ChartRenderer.vue"
const { layout, findChartById } = useChartConfig()
</script>
3. useChartConfig.ts
import { ref } from 'vue'
export const layout = ref<any[]>([])
export const charts = ref<any[]>([])
export function useChartConfig() {
function addChart(id: string, type: string) {
layout.value.push({
i: id,
x: 0,
y: Infinity,
w: 4,
h: 4
})
charts.value.push({
id,
type,
title: `${type}图示例`,
data: getDefaultData(type)
})
}
function getDefaultData(type: string) {
if (type === 'bar' || type === 'line') {
return { x: ['A', 'B', 'C'], y: [10, 20, 15] }
}
if (type === 'pie') {
return [
{ name: 'A', value: 30 },
{ name: 'B', value: 70 }
]
}
return {}
}
function findChartById(id: string) {
return charts.value.find(c => c.id === id)
}
return { layout, charts, addChart, findChartById }
}
四、功能拓展与优化建议(含完整实现)
✅ 1. 拖拽添加图表组件
支持从左侧图表组件库中拖拽图表类型 → 拖入画布 → 自动插入 layout + chart 配置。
ChartPalette.vue
<template>
<div class="chart-palette">
<div
v-for="type in chartTypes"
:key="type"
class="palette-item"
draggable="true"
@dragstart="handleDragStart(type)"
>
📊 {{ type }}
</div>
</div>
</template>
<script setup>
const emit = defineEmits(['drag-start'])
const chartTypes = ['bar', 'line', 'pie', 'radar']
function handleDragStart(type) {
emit('drag-start', type)
}
</script>
<style scoped>
.chart-palette {
width: 200px;
padding: 10px;
background: #f9f9f9;
border-right: 1px solid #ccc;
}
.palette-item {
padding: 6px 12px;
margin-bottom: 6px;
background: white;
border: 1px solid #ddd;
cursor: grab;
}
</style>
配合 LayoutGrid.vue 中增加拖拽响应:
function handleDrop(e) {
const chartType = e.dataTransfer?.getData('chart-type') || 'bar'
const newId = `chart_${Date.now()}`
addChart(newId, chartType)
}
✅ 2. 数据源接入能力(支持 API / Excel)
支持配置后端接口地址或上传 Excel 表格,动态更新图表数据。
DataSourcePanel.vue(上传 + 解析 Excel 示例)
<template>
<div class="upload">
<input type="file" @change="handleFile" accept=".xlsx, .xls" />
</div>
</template>
<script setup>
import * as XLSX from 'xlsx'
import { charts } from '@/composables/useChartConfig'
function handleFile(e) {
const file = e.target.files[0]
const reader = new FileReader()
reader.onload = (evt) => {
const data = new Uint8Array(evt.target.result)
const workbook = XLSX.read(data, { type: 'array' })
const sheet = workbook.Sheets[workbook.SheetNames[0]]
const json = XLSX.utils.sheet_to_json(sheet, { header: 1 })
// 示例:更新第一个图表的数据
if (charts.value.length) {
charts.value[0].data = {
x: json.slice(1).map(row => row[0]),
y: json.slice(1).map(row => row[1])
}
}
}
reader.readAsArrayBuffer(file)
}
</script>
✅ 3. 多主题切换支持
通过 ECharts 的 echartsTheme
参数 + Element Plus 主题切换实现。
ThemeSwitcher.vue
<template>
<select v-model="theme" @change="applyTheme">
<option value="light">明亮</option>
<option value="dark">暗黑</option>
<option value="vintage">复古</option>
</select>
</template>
<script setup>
import * as echarts from 'echarts'
import 'echarts/theme/dark'
import 'echarts/theme/vintage'
const theme = ref('light')
function applyTheme() {
echarts.disposeAllCharts?.()
// 再次初始化图表时传入 theme
}
</script>
ChartRenderer.vue 更新部分
const chartInstance = echarts.init(chartRef.value, theme.value)
✅ 4. 图表导出为图片功能(JPG / PNG)
通过 ECharts 内置方法实现图片导出。
ChartRenderer.vue 增加按钮导出
<button @click="download">导出图表</button>
function download() {
const base64 = chartInstance.getDataURL({ type: 'png' })
const a = document.createElement('a')
a.href = base64
a.download = 'chart.png'
a.click()
}
✅ 5. 图表动画配置
支持配置图表加载动画 / 更新动画。
动态配置图表 option 中的 animation
const option = {
title: { text: title },
animationDuration: 1000,
animationEasing: 'cubicOut',
...
}
✅ 6. 响应式布局支持(自适应容器)
通过监听容器宽高变化 + echarts.resize 实现响应式。
useResize.ts
export function useResize(chartRef) {
const resize = () => {
const chart = echarts.getInstanceByDom(chartRef.value)
chart?.resize()
}
onMounted(() => window.addEventListener('resize', resize))
onUnmounted(() => window.removeEventListener('resize', resize))
}
✅ 7. 图表组件扩展(地图 / 雷达 / 热力图)
可根据 type 自动加载额外图表类型。
示例:加载雷达图配置
if (type === 'radar') {
option = {
title: { text },
radar: {
indicator: [
{ name: 'A', max: 100 },
{ name: 'B', max: 100 },
{ name: 'C', max: 100 }
]
},
series: [
{
type: 'radar',
data: [{ value: [60, 80, 70], name: '维度分析' }]
}
]
}
}
到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕
更多推荐
所有评论(0)