在低代码、可视化热潮的推动下,越来越多的前端项目希望实现“通过配置 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: '维度分析' }]
      }
    ]
  }
}

到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕

在这里插入图片描述

Logo

永洪科技,致力于打造全球领先的数据技术厂商,具备从数据应用方案咨询、BI、AIGC智能分析、数字孪生、数据资产、数据治理、数据实施的端到端大数据价值服务能力。

更多推荐