简介:这是一套 Vue 大屏可视化项目实例集合,同时覆盖 Vue2 与 Vue3 工程形态,面向需要完成课程设计、毕业设计、大作业、工程实训,或希望快速搭建数据可视化大屏的开发者。压缩包内共 187 个文件,整体约 31MB;核心源码集中在 49 个 js、37 个 vue 与 37 个 css 文件中,另配有多张 png/jpg/gif 图片、HTML 入口、map 映射、字体和项目配置等辅助内容。不同大屏实例按目录组织,便于直接定位和对照查看。这些工程并非只包含静态页面,还涉及数据请求、图表展板、样式适配、组件拆分等常见落地细节。所有源码经过严格测试,确认功能正常后才上传,可直接运行,也可直接修改复刻;目前已有 2005 人学习下载。对初学者,可从项目搭建、页面布局、样式适配逐步模仿;对进阶学习者,则可基于图表组件、数据模块和代码结构继续扩展,用于新的大屏项目或真实业务数据接入。
1. 一套能直接跑的 Vue 大屏实例集合,远比自己搭脚手架省事
做数据可视化大屏这件事,最花时间的往往不是图表的配置,而是从零搭一套完整的 Vue2/Vue3 工程、配好自适应布局、写好公共边框和滚动表格。这套 Vue 大屏实例集合解决的正是这个问题:它把若干个可运行的大屏项目打包在一起,覆盖了后台监控、政务数据、运营分析等常见可视化场景,每个实例都包含完整的页面、组件、图表、mock 数据和适配方案。你不需要从空目录开始敲脚手架,解压后装依赖就能看到效果。适合正在做可视化项目、需要快速出效果给业务方看,或者想研究别人大屏工程结构的前端开发者。
2. 实例集合的内在结构:Vue2 / Vue3 两套并存,选型逻辑要先看懂
2.1 解压后的目录长什么样
先别急着跑npm install,花几分钟把压缩包解开看一下整体结构。这类实例集合通常不是单个项目,而是按“大屏场景”拆分的子项目,或者一个大工程里按路由分多个页面。我见过的是后一种居多:一个主工程下挂多个 view,每个大屏一个目录,互相独立,共用组件和工具函数。典型结构如下:
project-X/ ├─ package.json ├─ vue.config.js # 老一批 vue-cli 项目的配置文件 ├─ vite.config.js # 新一批 vite 项目的配置文件(两套并存) ├─ public/ │ └─ static/images/ # 大屏背景图、logo └─ src/ ├─ main.js ├─ App.vue ├─ router/ │ └─ index.js # 每个大屏对应一条路由 ├─ views/ │ ├─ energy-dashboard/ # 园区能耗大屏 │ ├─ monitor-screen/ # 运维监控大屏 │ └─ market-board/ # 运营数据看板 ├─ components/ │ ├─ BorderBox/ # 各种科技感边框 │ ├─ ScrollTable/ # 滚动列表 │ └─ NumberFlip/ # 数字翻牌 ├─ mock/ │ ├─ energy.json │ └─ monitor.json └─ utils/ ├─ flexible.js # rem 适配 ├─ resize.js # scale 缩放适配 └─ request.js # axios 封装先看懂views/和components/的关系很有用。每个大屏页面的逻辑都在views/xxx/index.vue里,公共的边框、图表容器、滚动表格被抽到了components/。你改数据看mock/,改布局看views/,改通用样式看components/。如果某个实例的目录结构不是这样,而是每个大屏单独一个完整工程,那就要注意了:公共能力可能没有下沉,改造时每个大屏要分别维护,成本高不少。
2.2 Vue2 与 Vue3 实例的依赖差异
这套集合最值得注意的一点是 Vue2 和 Vue3 的实例同时存在。老项目大多基于 vue-cli 4 + webpack,新项目基于 Vite。依赖版本差异直接决定你启动时会不会翻车:
| 对比项 | Vue2 老实例 | Vue3 新实例 |
|---|---|---|
| 脚手架 | vue-cli 4 / webpack | Vite 4 或 5 |
| 语言 | JavaScript Options API | JavaScript Composition API |
| ECharts | 4.x(按需引入) | 5.x(按需引入) |
| 适配方案 | flexible.js + rem | transform: scale |
| 样式 | scss + 全局变量 | scss + css variables |
| 数据 | mock 文件 + 定时器 | axios + 轮询 + mock |
为什么两套并存?因为历史项目里有一批大屏是 2020 年前后做的,当时 Vue2 是绝对主力,而且配套的地图组件、旧版 ECharts 插件只兼容 Vue2。后加的实例用 Vue3 是为了更好维护和上新技术栈。你在选实例时不用盲目追求 Vue3——如果你的部署环境是老旧浏览器,Vue2 那批反而更稳;如果是新项目且对后续扩展有要求,选 Vue3 实例。
2.3 按场景拆解实例,改起来才快
集合里的大屏实例不是简单换个标题,而是场景化设计过的。看几个典型场景就明白它们的差异:
- 园区能耗大屏:左侧是设备列表和告警信息,中间是能耗趋势折线图,右侧是电水气占比的环形图。这类屏偏后台监控,强调的是“状态实时变化”,轮询数据是核心。
- 运维监控大屏:主体是一个大图(机柜分布、链路拓扑),四周是 KPI 卡片和小图表,颜色偏暗色科技风。这类屏对图表交互要求不高,但对地图和拓扑图的渲染性能有要求。
- 运营数据看板:以柱状图、饼图、排名列表为主,视觉偏亮色系,强调“给领导演示”,所以动画效果做得比较多。
理解场景差异后再去改代码就快了:后台监控类,重点改接口轮询和告警逻辑;演示类,重点改动画时长和视觉配色。不要一上来就把一个亮色演示屏改成暗色监控屏,因为主题变量、背景图、组件配色是整套联动的,硬改会改出一堆视觉死角。
3. 从跑通到改造:一条可复现的大屏落地流程
3.1 装依赖与启动,先让页面起来
拿到集合后第一件事是安装依赖。直接用默认源装容易在网络波动时卡住,我一般习惯用国内镜像源,命令如下:
# 在工程根目录执行,指定镜像源减少超时风险 npm install --registry=https://registry.npmmirror.com # 如果遇到 peerDependencies 冲突,加 --legacy-peer-deps 重试 npm install --legacy-peer-deps装完依赖后启动开发服务:如果是 Vite 工程,执行npm run dev;如果是 vue-cli 工程,执行npm run serve。这里有个很实用的参数:Vite 启动时加上--host可以暴露局域网地址,方便你用手机或另一台电脑访问大屏效果。命令是npm run dev -- --host。
启动成功后,先逐个打开路由列表里的大屏,确认哪些页面能跑、哪些页面报错。集合类资源经常出现个别页面依赖了未安装的额外包,比如地图组件库缺失会导致地图区域空白。这一步是为了提前摸清哪些实例是完整的,不要等改到一半才发现页面起不来。
3.2 定位可改点:先找数据文件,再找图表配置
大屏页面改起来最需要区分的是“数据来源”和“图表配置”两层。常见做法是:页面组件从 mock 文件取数,图表配置写死在组件的option里。先打开一个实例的index.vue,你会看到类似下面的结构:
// views/energy-dashboard/index.vue(简化示例) import { ref, onMounted } from 'vue' import energyMock from '@/mock/energy.json' // 静态数据入口 import * as echarts from 'echarts' const chartRef = ref(null) const chart = ref(null) onMounted(() => { chart.value = echarts.init(chartRef.value) renderChart(energyMock.data) }) function renderChart(data) { chart.value.setOption({ series: [{ type: 'line', data: data.trend, // 改数据源只动这里 smooth: true, lineStyle: { width: 2 } }] }) }数据入口和渲染逻辑是分离的。改 mock 数据里的data.trend数组,图表就会跟着变,不需要动任何图表配置。参数说明:smooth控制折线是否平滑,改成false就是直角折线;lineStyle.width控制线宽,大屏一般在 2~3 之间,太粗会显得不够精致。初次改造时建议先只替换数据和标题,不要动setOption里的配置项,否则可能因为坐标轴、tooltip 联动关系没理清而把图表改花。
3.3 第一次实际改造:标题、主题色和默认数据
跑通之后,可以做一次最小化改造来验证自己对结构理解对不对。改三个地方:
- 大屏顶部标题:一般在
index.vue模板里找到固定文案,直接替换。 - 主题色:多数实例用 SCSS 变量统一管理,全局搜
$primary-color这类变量名,改成你自己的品牌色。 - 默认数据:打开
mock/xxx.json,把数组里的数值随机替换几个,刷新页面看图表是否随之变化。
// src/styles/variables.scss // 实例集合里统一维护的主题变量 $primary-color: #00e4ff; // 改成你的主色,注意同时看饱和度和亮度 $panel-bg: rgba(6, 30, 50, 0.8); // 面板背景 $font-color: #e0e6f0; // 主文字色提示:改主题色时不要只看色值,还要看透明度
rgba的最后一个参数。大屏背景是深色的,透明度低于 0.6 时文字可读性会显著下降。
这一步做完,你已经可以自信地说这个实例在你的环境里跑通了。接下来再深入做真实接口对接和自适应调整,那才是在生产环境里真正会被追着问的部分。
4. 数据对接与自适应改造:从写死 JSON 到真实可交付
4.1 把 mock 数据换成 axios 请求
集合里的实例默认走本地 mock 数据,真实项目里不可能让业务方去改 JSON 文件。标准做法是封装一个 request 实例,把数据获取从静态导入改成异步请求:
// src/utils/request.js import axios from 'axios' const request = axios.create({ baseURL: '/api', // 走 vite/webpack 代理,避免跨域 timeout: 10000, // 大屏接口一般 10s 超时足够 headers: { 'Content-Type': 'application/json' } }) // 请求拦截器:统一带 token 或设备标识 request.interceptors.request.use(config => { const token = localStorage.getItem('screen_token') if (token) config.headers.Authorization = `Bearer ${token}` return config }) export default request页面里原来import energyMock from '@/mock/energy.json'的地方,改成在onMounted里请求数据:
import request from '@/utils/request' async function fetchData() { const res = await request.get('/screen/energy', { params: { area: 'east', period: 'last24h' } }) chart.setOption(buildOption(res.data)) }这里baseURL设为/api是常用的规避跨域手段,通过开发服务器的代理转发到后端。timeout: 10000的含义是接口十秒没响应就放弃,大屏项目里这很重要——如果后端服务宕机,轮询接口会一直堆积,10 秒超时能及时断掉,避免浏览器被阻塞。生产环境里baseURL会改成网关地址,但用相对路径/api仍是兼容性最稳的做法。
4.2 定时轮询必须绑定生命周期清理
大屏多数场景需要自动刷新数据。很多人在onMounted里塞一个setInterval,然后组件销毁时忘记清理。在单页应用里,路由切换后定时器还在跑,接口请求会打到不存在的页面上下文里,图表实例也可能已经销毁,随后报错把控制台刷屏。
正确写法是:
import { onMounted, onUnmounted, ref } from 'vue' const timer = ref(null) function startPolling() { timer.value = setInterval(async () => { const res = await request.get('/screen/energy') updateCharts(res.data) }, 30000) // 刷新间隔:30 秒 } function updateCharts(data) { // 逐个更新图表实例的 series 数据 chart.setOption({ series: [{ data: data.trend }] }) } onMounted(() => { fetchData() // 进来先拉一次,不要等 30 秒后才出数据 startPolling() }) onUnmounted(() => { clearInterval(timer.value) // 离开大屏页面时清掉定时器 })轮询间隔选 30 秒是有讲究的。大屏是给上级查看的展示型界面,不是交易系统,不需要秒级实时;间隔太短会让服务器承受无谓压力,间隔太长又显得数据不新鲜。一般常规监控类数据 30~60 秒一次,告警类数据才考虑 5 秒一次。另外,页面第一次进入时一定要先手动fetchData(),再启动定时器,否则用户看到的前几秒是空数据。
4.3 两套自适应方案怎么选
集合里老实例大多用 flexible.js 做 rem 适配,新实例用transform: scale。这两个方案各有利弊,先看对比:
| 适配方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| flexible + rem | 根字号随屏幕宽度缩放,所有 rem 单位自动变化 | 文字、边距整体等比缩放,代码透明 | 依赖设计稿基准宽;图表 canvas 不会自动缩放 |
| transform: scale | 整个容器按宽高比缩放 | 图表和 dom 一起缩放,效果最“整” | 周围留白或放大裁切;坐标定位固定 |
如果实例用的是 flexible.js,它的核心逻辑是这样的:
// utils/flexible.js,基于 1920 设计宽计算根字号 function resize() { const baseWidth = 1920 // 设计稿基准宽度 const clientWidth = document.documentElement.clientWidth const fontSize = (clientWidth / baseWidth) * 100 document.documentElement.style.fontSize = fontSize + 'px' } window.addEventListener('resize', resize) resize()这段代码的效果是:把屏幕宽度除以设计稿宽度,得到一个比例系数,再乘以 100 作为根字号。页面里所有用rem写的尺寸都会跟着这个值等比缩放。注意两个参数:baseWidth必须和你 UI 设计稿一致,不是随便写的;根字号放大到 100px 是为了让 1rem = 100px,开发时好换算,比如设计稿 16px 就写0.16rem。
用 rem 方案时最容易踩的坑是 ECharts 的 canvas 不认 rem。图表宽度通常从容器获取,容器尺寸变了图表不会自动跟着变,需要在resize事件里手动调用chart.resize():
function onWindowResize() { chart.resize() // 通知 echarts 重新计算大小 } window.addEventListener('resize', onWindowResize)如果你的实例是transform: scale方案,则不需要管 ECharts 的 resize,整个大屏容器被 CSS 缩放,图表会跟随视觉大小同步变化。这套方案更适合分辨率固定的大屏投放场景,但要注意缩放后鼠标坐标和点击区域会偏移,需要做坐标换算。
5. 避坑与常见问题:大屏实例复现中的五个翻车点
5.1 npm install 报 ELIFECYCLE,node-sass 编译失败
老一批 Vue2 实例依赖 node-sass,而 node-sass 对 Node 版本非常敏感。现象是npm install执行到一半报错,显示gyp ERR! ELIFECYCLE或node-sass编译失败。原因是本机 Node 版本太高(比如 18 以上),而项目里 node-sass 版本太旧,找不到对应版本的二进制文件。
解决思路是不要硬刚 node-sass。优先看package.json里有没有sass替代依赖;没有的话,安装sass并替换:先删掉node_modules和package-lock.json,再把node-sass模块改成sass。如果项目必须用 node-sass,就安装低版本 Node,比如用 nvm 切到 14 或 16,再重新安装依赖。
5.2 ECharts 图表区域空白,容器高度为 0
启动成功后页面出来了,但所有图表是中空的,控制台没有报错。最常见的根因是图表容器父级使用了百分比高度,而父级没有明确高度,导致容器实际高度为 0。ECharts 初始化时拿到的宽高是 0,渲染出来自然空白。
解决方式是给图表容器一个明确的高度。我们在大屏中经常把图表放在flex布局的卡片里,需要在卡片内部给图表区域height: 300px或flex: 1同时约束 min-height。另外,实例集合里有些大屏在v-if控制的布局下渲染图表,等 DOM 挂载后再init才能拿到正确高度,必要时加一句nextTick再初始化。
5.3 build 后部署在子路径下,所有图片和接口 404
本地开发正常,执行npm run build后部署到服务器二级目录,页面打开了但图片全裂、接口地址少了一截路径。原因是构建资源路径写死了绝对路径/,部署环境实际在/screen/子目录下。
Vite 工程在vite.config.js里设置base: './',vue-cli 工程在vue.config.js里设置publicPath: './'。这里我一般会确认一下部署路径层级,如果静态资源嵌套更深,要手动改成完整相对路径。接口地址也要注意,baseURL如果写成绝对路径,部署后同样会找不到,建议把接口请求也改成相对路径走代理。
5.4 路由切换后控制台刷屏,图表实例没有销毁
大屏页面切到其他路由再切回来,控制台开始刷Get ctx of undefined之类的报错。原因是echarts.init创建的实例没有在组件销毁时调用dispose,实例残留还在监听着窗口事件,或者组件重新渲染时对同一个 DOM 节点重复初始化。
解决方式是在组件销毁生命周期里手动释放实例。之前 4.2 节讲过onUnmounted清理定时器,这里同样要加一行:
onUnmounted(() => { if (chart.value) { chart.value.dispose() chart.value = null } clearInterval(timer.value) })dispose的作用是释放 ECharts 实例占用的浏览器内存和事件绑定,不调用它的话长时间在大屏间切换,页面会明显变卡。这也是大屏项目最容易被忽略的内存泄漏来源。
5.5 rem 适配下,ECharts 字体越变越大
用 flexible.js 的方案,页面里用 rem 写的文字缩放正常,但 ECharts 内部的axisLabel文字尺寸不会自适应。更麻烦的是,如果你给 ECharts 的fontSize也写了 rem,屏幕变宽时根字号变大,图表文字会跟着膨胀,导致图表标题溢出画布。
解决方式是把 ECharts 的字体、间距全部锁定为 px 值,不要让它们走 rem 体系。大屏设计稿基准如果是 1920,就直接在配置里统一写 12px、14px,搭配chart.resize()保证画布大小变化,文字尺寸保持稳定。这也是为什么新一批 Vue3 实例改用transform: scale的原因,整块缩放彻底避开了 ECharts 字体 rem 化的问题。如果你被迫维护 rem 老项目,遇到图表字体缩放异常时不要怀疑是计算问题,直接锁 px 是最快的后悔药。
6. 进阶用法:把多个实例抽成自己的大屏组件骨架
6.1 只留骨架,去掉业务
当你同时打开集合里三四个大屏页面,会发现重复度极高:顶部是标题和时间;左右两侧是面板卡片;底部或中央是主图表。与其每次从不同实例里复制粘贴,不如把它们的公共骨架抽出来,做一个通用的大屏容器组件。
我建议的抽取方式是提炼一个ScreenLayout组件,接收插槽和配置项,把页面的公共逻辑集中管理:
// components/ScreenLayout/index.vue(思路简化版) <template> <div class="screen-layout"> <header class="layout-header"> <span class="title">{{ title }}</span> <span class="time-display">{{ currentTime }}</span> </header> <main class="layout-body"> <slot name="left-panel" /> <slot name="center-panel" /> <slot name="right-panel" /> </main> </div> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue' const props = defineProps({ title: { type: String, default: '数据可视化大屏' }, refreshInterval: { type: Number, default: 30000 } }) const currentTime = ref('') function updateTime() { const now = new Date() currentTime.value = `${now.toLocaleDateString()} ${now.toLocaleTimeString()}` } const timer = setInterval(updateTime, 1000) onMounted(updateTime) onUnmounted(() => clearInterval(timer)) </script>这个组件的价值在于:把标题、时钟、整体三栏布局这些所有大屏都有的东西收进一个黑匣子,业务页面只关心自己左侧、中间、右侧要放什么图表。refreshInterval参数可以控制整个大屏的刷新节奏,子图表自己监听这个值做轮询,就不用每个页面都写一遍定时器。
6.2 数据入口统一收敛
再进一步,我给所有大屏页面强制了一个约定:页面组件内不允许出现setInterval和echarts.init,只允许调用一个useScreenData的 composable,它负责请求、轮询、异常处理。这样拆过三四个实例后,大屏开发变成搭积木——选布局、填图表、配数据源。
从那以后我每次接到大屏需求,都会先花一两个小时把这套集合里对口的组件和样式抽一遍,而不是从零写边框和标题栏。这种“先抽骨架再填内容”的方式帮我避开了很多重复改样式的体力活,也让我在遇到新需求时能快速判断出工作量。希望这套思路和这份实例集合在使用中也能帮到你。
本文还有配套的精品资源,点击获取