可视化大屏系列(3):ECharts 实现生产状态、环境监测、告警提醒
适配方式适用场景优点缺点大屏固定比例展示还原设计稿,结构清晰文字随缩放可能模糊rem + vw 单位普通 PC 页面或小屏幕更自然的文字与组件缩放样式调整工作量大自适应布局区域模块化布局、快速搭建需配合缩放或动态计算特性WebSocket双向通信✅ 支持(客户端与服务端双向)⛔ 仅服务端推送通道持久化✅ 支持持久连接✅ 支持持久连接支持断线重连✅ 需手动实现✅ 内置重连应用场景聊天、物联网状态、工
可视化大屏系列(3):ECharts 实现生产状态、环境监测、告警提醒

目录
一、响应式大屏布局设计
二、图表容器适配与缩放方案
三、环境监测与生产状态图表设计
四、实时数据推送:WebSocket/SSE 实践
五、告警数据的图形高亮与动画提醒
六、主题定制与图表动态切换
七、总结与扩展建议
一、响应式大屏布局设计
在智慧工地场景中,可视化大屏常部署于会议室 LED 大屏或电视墙上,屏幕尺寸差异较大,若不做适配容易出现布局错乱或缩放异常。因此,构建一套适配不同分辨率、兼容主流大屏尺寸的响应式布局方案,是系统建设的关键第一步。
1. 设计思路与目标
目标:
- ✅ 自动缩放:适配常见大屏分辨率(1920×1080、2560×1440 等);
- ✅ 统一缩放比例:以设计稿为基准,保持页面元素比例不变;
- ✅ 灵活布局:兼容纵向、横向滚动区域,自适应组件尺寸;
- ✅ 统一组件容器:通过统一容器封装,复用性更强。
思路:
- 使用
transform: scale()按比例缩放整个容器; - 以设计稿(如 1920×1080)为基准,计算宽高缩放比;
- 选取较小缩放比,保持容器完整显示;
- 配合
Tailwind CSS或SCSS进行自适应样式设计。
2. 封装大屏容器组件
封装一个名为 <ScreenContainer> 的通用容器组件,自动根据窗口尺寸进行缩放。
Vue 3 + <script setup> 实现方式:
<!-- components/ScreenContainer.vue -->
<template>
<div class="w-screen h-screen bg-[#0f1c2e] overflow-hidden text-white">
<div ref="scaleWrapper" class="w-full h-full origin-top-left" :style="scaleStyle">
<slot />
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
const DESIGN_WIDTH = 1920
const DESIGN_HEIGHT = 1080
const scaleWrapper = ref(null)
const scaleStyle = ref({})
const updateScale = () => {
const w = window.innerWidth
const h = window.innerHeight
const scale = Math.min(w / DESIGN_WIDTH, h / DESIGN_HEIGHT)
scaleStyle.value = {
width: `${DESIGN_WIDTH}px`,
height: `${DESIGN_HEIGHT}px`,
transform: `scale(${scale})`,
}
}
onMounted(() => {
updateScale()
window.addEventListener('resize', updateScale)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', updateScale)
})
</script>
使用方法:
<template>
<ScreenContainer>
<!-- 大屏布局区域 -->
<Header />
<MainGrid />
<Footer />
</ScreenContainer>
</template>
<script setup>
import ScreenContainer from '@/components/ScreenContainer.vue'
</script>
3. 栅格化布局设计(主内容区)
布局采用 Tailwind CSS 的 grid 布局(或 SCSS + rem/vw),划分左中右三栏或九宫格区域,适合放置多个图表、统计卡片、地图区域等。
<!-- 例:三列九宫格结构 -->
<div class="grid grid-cols-3 grid-rows-3 gap-4 p-4">
<div class="col-span-1 row-span-1 bg-blue-900 rounded-xl p-2">左上</div>
<div class="col-span-1 row-span-2 bg-green-900 rounded-xl p-2">中中</div>
<div class="col-span-1 row-span-1 bg-yellow-900 rounded-xl p-2">右上</div>
<!-- 其他区域 -->
</div>
常用辅助样式建议:
| Tailwind 类名 | 功能 |
|---|---|
aspect-video |
固定视频/图表宽高比 |
w-full h-full |
占满容器 |
overflow-hidden |
防止内容溢出 |
rounded-2xl |
美观圆角 |
backdrop-blur |
背景模糊层效果 |
4. 响应式适配策略总结
| 适配方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| transform scale | 大屏固定比例展示 | 还原设计稿,结构清晰 | 文字随缩放可能模糊 |
| rem + vw 单位 | 普通 PC 页面或小屏幕 | 更自然的文字与组件缩放 | 样式调整工作量大 |
| grid + flex | 自适应布局区域 | 模块化布局、快速搭建 | 需配合缩放或动态计算 |
二、图表容器适配与缩放方案
在智慧工地的大屏应用中,ECharts 图表承担着展示生产状态、环境监测、人员行为等核心数据的重任。为了确保图表在不同分辨率下依然保持清晰、布局合理,图表容器与图表自身的缩放适配能力就显得尤为关键。
1. 图表容器宽高与响应式设计
图表的显示区域需要具备以下能力:
- 支持 自适应缩放:不同屏幕下等比例缩放;
- 保持 宽高比不失真:尤其是环状图、雷达图等;
- 配合
transform: scale()或resize事件重新渲染图表。
推荐做法是:使用百分比宽高 + resizeObserver/监听容器变化重新调整图表尺寸。
<!-- 示例:响应式图表容器 -->
<template>
<div ref="chartRef" class="w-full h-full" />
</template>
<script setup>
import { onMounted, ref, onBeforeUnmount } from 'vue'
import * as echarts from 'echarts'
const chartRef = ref(null)
let chartInstance = null
const initChart = () => {
if (!chartRef.value) return
chartInstance = echarts.init(chartRef.value)
chartInstance.setOption({
title: { text: '环境温湿度' },
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
yAxis: { type: 'value' },
series: [{ type: 'line', data: [120, 200, 150] }]
})
}
const resizeChart = () => {
if (chartInstance) chartInstance.resize()
}
onMounted(() => {
initChart()
window.addEventListener('resize', resizeChart)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', resizeChart)
})
</script>
2. ECharts 图表组件化封装
为了提升图表复用性,我们建议封装一个通用 <BaseChart /> 组件,通过 props 传入图表配置项、主题、颜色、图表类型等参数,实现图表“即插即用”。
💡 示例:通用图表组件封装
<!-- components/BaseChart.vue -->
<template>
<div ref="chartDom" class="w-full h-full" />
</template>
<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
option: Object,
theme: String,
autoResize: { type: Boolean, default: true }
})
const chartDom = ref(null)
let chart = null
const renderChart = () => {
if (!chartDom.value) return
chart = echarts.init(chartDom.value, props.theme || 'light')
chart.setOption(props.option)
}
const resize = () => chart?.resize()
onMounted(() => {
renderChart()
if (props.autoResize) window.addEventListener('resize', resize)
})
onBeforeUnmount(() => {
if (props.autoResize) window.removeEventListener('resize', resize)
chart?.dispose()
})
watch(() => props.option, (newOpt) => {
if (chart && newOpt) {
chart.setOption(newOpt, true)
}
}, { deep: true })
</script>
3. 图表缩放适配策略汇总
| 方式 | 原理说明 | 场景适用 |
|---|---|---|
echarts.resize() |
调用 ECharts 提供的 API,重新计算尺寸 | 容器宽高变化、页面切换 |
transform: scale() |
容器整体缩放,图表随之变化(字体可能模糊) | 大屏场景,全页面缩放 |
vw/rem + media query |
CSS 媒体查询+相对单位 | 小屏、PC端响应式布局 |
ResizeObserver |
监听容器尺寸变化,自动触发图表调整 | 多区域自适应图表容器 |
4. 常见图表适配示例
饼图适配:
option = {
tooltip: { trigger: 'item' },
legend: { top: '5%', left: 'center' },
series: [
{
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: { show: false },
data: [{ name: '正常', value: 60 }, { name: '告警', value: 40 }]
}
]
}
柱状图自适应字体大小:
xAxis: {
axisLabel: {
fontSize: 12 * scale, // 根据大屏缩放比例动态设置
rotate: 30
}
}
scale可以从外部传入当前页面的缩放比例,由父组件或布局容器提供。
5. 图表性能优化建议
- ⚙️ 避免频繁
setOption全量渲染,使用setOption(option, false)增量更新; - ⚙️ 图表大量数据时,开启
sampling或dataZoom; - ⚙️ 可视区域以外图表延迟加载,减少初始渲染压力;
- ⚙️ 批量图表封装
IntersectionObserver实现懒加载; - ⚙️ 页面缩放时 节流 resize 函数调用。
三、环境监测与生产状态图表设计
智慧工地的大屏可视化不仅承担数据汇总展示的作用,更承担着实时环境监控、生产效率监管与异常告警的任务。在实际开发中,围绕“环境监测”和“生产状态”这两个维度,我们需要设计一套清晰、动态、交互友好的图表系统。
1. 业务场景需求分析
✅ 环境监测图表数据示例:
- PM2.5、PM10、温湿度、噪音、风速等环境数据;
- 需要实时展示当前数据 + 近12小时趋势;
- 某些指标如 PM2.5 超标需高亮告警。
✅ 生产状态图表数据示例:
- 各施工区域或设备的当前工作状态(运转/待机/故障);
- 各班组产量/工序完成情况;
- 效率对比图、环比柱状图、占比饼图等。
2. 图表类型设计与适配建议
| 图表类型 | 适用场景 | 展示重点 |
|---|---|---|
| 折线图 | 温湿度/噪音趋势 | 实时变化趋势 |
| 柱状图 | 工序完成率/设备运行 | 直观对比效率 |
| 饼图/环形图 | 当前状态占比 | 正常/故障比重 |
| 仪表盘 | 实时数值展示 | 当前 PM2.5 等指标 |
| 雷达图 | 多项环境指标对比 | 安全等级综合评估 |
3. 通用图表组件封装方案
基于 Vue3 + ECharts + 组合式 API,我们封装统一的 <ChartCard /> 图表展示组件,实现数据和样式传参即可复用:
📦 示例:ChartCard.vue
<template>
<div class="chart-card w-full h-full bg-white rounded-xl p-4 shadow-md">
<div class="flex justify-between items-center mb-2">
<h3 class="text-lg font-bold">{{ title }}</h3>
<span v-if="unit" class="text-gray-500 text-sm">单位:{{ unit }}</span>
</div>
<BaseChart :option="option" :theme="theme" />
</div>
</template>
<script setup>
import { computed } from 'vue'
import BaseChart from './BaseChart.vue'
const props = defineProps({
title: String,
unit: String,
chartData: Array,
type: String,
theme: String
})
// 根据 type 动态生成 ECharts 配置
const option = computed(() => {
switch (props.type) {
case 'line':
return {
xAxis: { type: 'category', data: props.chartData.map(i => i.time) },
yAxis: { type: 'value' },
series: [{ data: props.chartData.map(i => i.value), type: 'line' }]
}
case 'gauge':
return {
series: [{
type: 'gauge',
data: [{ value: props.chartData[0]?.value || 0 }],
detail: { valueAnimation: true }
}]
}
// 其他类型可扩展
default:
return {}
}
})
</script>
使用方式:
<ChartCard
title="PM2.5 实时监测"
unit="μg/m³"
type="gauge"
:chartData="[{ value: 55 }]"
/>
4. 数据格式与动态更新设计
数据应统一设计格式,方便传入图表组件,例如:
// 后端推送或接口获取数据格式
{
env: {
pm25: 55,
humidity: 46,
temperature: 27.3,
noise: 56,
windSpeed: 3.5,
timestamp: "2025-04-21 10:00:00"
},
production: [
{ area: '塔吊A区', status: '运行中' },
{ area: '升降梯B区', status: '故障' },
...
]
}
动态更新通过 props 数据响应 + ECharts 的 setOption({ ... }, true) 实现。
如实时线图追加方式:
const updateLineChart = (newData) => {
const option = chartInstance.getOption()
option.series[0].data.push(newData.value)
option.xAxis[0].data.push(newData.time)
chartInstance.setOption(option, true)
}
5. 状态告警与高亮处理
可通过图表颜色变化、背景高亮等方式提示异常,如:
color: value > limit ? 'red' : 'green'
仪表盘中:
axisLine: {
lineStyle: {
color: [
[0.6, '#91cc75'], // 安全
[0.8, '#fac858'], // 预警
[1, '#ee6666'] // 危险
]
}
}
6. 多图联动布局(组合图表)
通过 grid 布局、Flex 容器将多个图表组合,形成板块式视图:
<div class="grid grid-cols-3 gap-4">
<ChartCard title="PM2.5" :chartData="pmData" type="gauge" />
<ChartCard title="温湿度" :chartData="tempData" type="line" />
<ChartCard title="噪音水平" :chartData="noiseData" type="bar" />
</div>
四、实时数据推送:WebSocket/SSE 实践
智慧工地的大屏展示系统要求数据实时性强,尤其在环境监测、设备状态、告警提醒等模块中,传统轮询方式无法满足“秒级响应”的需求。此时,WebSocket 和 SSE(Server-Sent Events) 成为实现“前后端实时通信”的关键技术。
1. WebSocket / SSE 技术简介
| 特性 | WebSocket | SSE(Server-Sent Events) |
|---|---|---|
| 双向通信 | ✅ 支持(客户端与服务端双向) | ⛔ 仅服务端推送 |
| 通道持久化 | ✅ 支持持久连接 | ✅ 支持持久连接 |
| 支持断线重连 | ✅ 需手动实现 | ✅ 内置重连 |
| 应用场景 | 聊天、物联网状态、工地设备告警 | 实时数据推送,如温度、湿度曲线等 |
| 浏览器兼容性 | ✅ 所有现代浏览器 | ✅ 更佳兼容性,但 IE 不支持 |
本文以 WebSocket 为主,SSE 可用于环境数据轻量推送作为备选方案。
2. 实践场景与数据示例
✅ 场景1:PM2.5 实时监测
- 服务端每隔5秒推送当前传感器数据;
- 前端自动追加到折线图末尾,模拟趋势变化。
✅ 场景2:设备状态告警推送
- 如升降梯故障、施工机械过热;
- 服务端推送告警状态,前端图表变色高亮。
3. Vue3 中封装 WebSocket 通用模块
我们可以封装一个 WebSocket 管理模块 useWebSocket.js,支持自动重连、消息处理回调、统一状态管理等。
📦 示例代码:/composables/useWebSocket.js
import { ref, onUnmounted } from 'vue'
export function useWebSocket(url, onMessage) {
const ws = ref(null)
const isConnected = ref(false)
let reconnectTimer = null
const connect = () => {
if (ws.value) ws.value.close()
ws.value = new WebSocket(url)
ws.value.onopen = () => {
isConnected.value = true
console.log('[WebSocket] 已连接')
}
ws.value.onmessage = (e) => {
const data = JSON.parse(e.data)
onMessage && onMessage(data)
}
ws.value.onerror = (e) => {
console.error('[WebSocket] 错误:', e)
}
ws.value.onclose = () => {
isConnected.value = false
console.warn('[WebSocket] 连接关闭,5秒后尝试重连...')
reconnectTimer = setTimeout(connect, 5000)
}
}
onUnmounted(() => {
if (ws.value) ws.value.close()
clearTimeout(reconnectTimer)
})
connect()
return {
isConnected,
send: (msg) => ws.value?.send(JSON.stringify(msg)),
}
}
4. 图表组件接收 WebSocket 数据并更新
以折线图为例,结合 ECharts setOption() 动态更新图表数据:
// chartData 是响应式数组
const chartData = ref([])
const { isConnected } = useWebSocket('wss://api.example.com/ws/env', (data) => {
// 假设 data: { type: 'pm25', value: 55, time: '10:00' }
chartData.value.push({ name: data.time, value: data.value })
// 限制最多保留30个点
if (chartData.value.length > 30) chartData.value.shift()
})
在 <ChartCard /> 中使用 watchEffect 监听 chartData 变化:
watchEffect(() => {
myChart.setOption({
xAxis: { data: chartData.value.map(d => d.name) },
series: [{ data: chartData.value.map(d => d.value) }]
}, true)
})
5. WebSocket 消息处理通用设计建议
建议服务端推送数据统一格式,如:
{
"type": "pm25",
"value": 68,
"unit": "μg/m³",
"time": "2025-04-21 10:30:12"
}
前端根据 type 区分展示内容,提高拓展性。
6. SSE 简要使用示例(备选)
如服务端支持 SSE,可通过如下方式连接:
const evtSource = new EventSource('https://api.example.com/sse/env')
evtSource.onmessage = function(e) {
const data = JSON.parse(e.data)
console.log('[SSE] 接收到数据:', data)
}
7. 实时推送小结与优化建议
| 建议 | 说明 |
|---|---|
| 消息格式标准化 | 保证 type + value + time 结构 |
多图表场景下使用 type 分发 |
一套 WebSocket 连接服务多个模块 |
| 后端支持心跳机制 | 保持长连接稳定性 |
| 前端封装自动重连逻辑 | 应对偶发断线问题 |
| 图表更新加节流 | 避免频繁重绘卡顿 |
五、告警数据的图形高亮与动画提醒
在图表中高亮告警项,可以通过以下方式:
- ECharts 动态更新数据点样式
- 添加闪烁动画(如定时更换颜色)
- 结合动画库实现弹出提示
// 更新告警状态样式
chart.setOption({
series: [{
name: '粉尘浓度',
markPoint: {
data: [{ type: 'max', name: '最大值' }],
itemStyle: { color: 'red' }
}
}]
})
五、告警数据的图形高亮与动画提醒
1. 告警信息的识别与结构定义
通常,告警数据从后端实时推送,或定期轮询而来。建议统一定义告警消息的结构:
{
"type": "temperature",
"level": "warning",
"value": 88,
"location": "塔吊#3",
"time": "2025-04-21 15:12:34",
"msg": "温度过高,超出阈值"
}
前端根据 type 识别图表模块,根据 level 渲染不同颜色/动画。
2. 图表中的告警高亮处理
✅ 示例场景:柱状图中某设备温度过高,需高亮该柱体
我们可在图表初始化后动态修改指定数据项的样式:
myChart.setOption({
series: [{
type: 'bar',
data: chartData.map(item => ({
value: item.value,
itemStyle: item.value > item.warn ? {
color: '#ff4d4f',
shadowColor: '#ff7875',
shadowBlur: 10
} : {}
}))
}]
})
如果你使用的是封装组件,只需传入 warn 字段,组件内部封装处理:
<ChartBar :data="deviceList" :warnKey="'warn'" :valueKey="'value'" />
3. 使用 ECharts 动画实现跳动/闪烁效果
告警时可以通过颜色渐变动画、放大缩小动画增强提醒效果。例如:
✅ 圆点闪烁动画:
symbol: 'circle',
symbolSize: 18,
itemStyle: {
color: '#ff4d4f',
opacity: 0.8
},
emphasis: {
scale: true
}
✅ 使用 timeline 或 setInterval 控制样式闪烁:
let isRed = true
setInterval(() => {
myChart.setOption({
series: [{
itemStyle: {
color: isRed ? '#ff4d4f' : '#fa8c16'
}
}]
})
isRed = !isRed
}, 600)
⚠️ 注意:频繁更新图表可能引发性能问题,建议节流控制或对小型组件单独处理。
4. 整合 WebSocket 告警推送与图表响应
将告警消息绑定图表响应逻辑:
const { isConnected } = useWebSocket('wss://api.example.com/ws/alerts', (alert) => {
if (alert.type === 'temperature') {
updateChartHighlight(alert.location, alert.value)
notifyUser(alert.msg)
}
})
updateChartHighlight 为封装函数,标记指定设备:
function updateChartHighlight(name, value) {
const index = chartData.findIndex(item => item.name === name)
if (index !== -1) {
chartData[index].value = value
chartData[index].isWarn = true
}
}
5. 提示与用户交互联动
除了图表变化,我们还可以通过以下方式进一步提醒用户:
- 顶部/右侧浮动告警面板;
- 播放提示音(可选);
- 点击图表高亮项跳转告警详情页。
示例代码:
<template>
<u-notify :show="showAlert" type="error" message="塔吊#3温度过高" />
</template>
<script setup>
const showAlert = ref(false)
watch(alertData, () => {
showAlert.value = true
setTimeout(() => showAlert.value = false, 3000)
})
</script>
6. 动态聚焦与视图滚动联动(适用于地图 + 列表)
若页面存在地图或设备列表,可以通过告警触发视图自动滚动或聚焦设备图标(适配地图场景):
map.panTo([alert.lng, alert.lat])
highlightDeviceMarker(alert.id)
scrollToDevice(alert.id)
7. 小结与优化建议
| 功能要点 | 建议做法 |
|---|---|
| 告警状态样式区分 | 根据 level 区分颜色:正常、预警、严重等 |
| 动画效果 | 控制频率,适度使用 shadow、scale 动效 |
| 多图联动 | 统一状态标识(如设备ID),实现图表+地图联动 |
| 用户可交互操作 | 点击跳转、查看历史数据、确认告警等 |
| 封装图表告警能力 | 提供如 highlightWarn(name) 方法方便外部调用 |
七、总结与扩展建议
本篇我们实现了基于 Vue3 + ECharts 的多种数据监控图表:
- 实现响应式大屏适配与组件封装;
- 支持 WebSocket 推送,实现真正的“实时监测”;
- 灵活切换图表主题,提升视觉统一性;
- 模块结构可支持快速扩展:如 AI 预测分析、安全预警等级评估等。
扩展建议:
- 加入大屏轮播(多个图表定时切换展示);
- 引入数据缓存与降采样策略,提升大屏性能;
- 集成图表下载、报表生成等能力,增强业务支持。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)