
简介本资源是一套基于HTMLCSSJSECharts技术栈实现的大屏可视化完整项目面向前端初学者与数据可视化实践者解决企业级数据看板快速搭建与动态渲染的核心需求。压缩包共1341个文件涵盖229个JavaScript脚本含ECharts初始化、数据轮询与交互逻辑、139个CSS样式文件含响应式布局、Flex/Grid排版及主题配色方案、61个HTML页面含主屏index.html及多级子模块结构以及764张PNG图表素材与图标资源整体大小为63.77MB。已有1677人学习下载项目结构清晰包含备份文件如index.html.bak与主流UI框架依赖Bootstrap、AdminLTE等便于理解工程化组织方式。读者可直接运行查看全屏自适应效果掌握从页面骨架搭建、样式精细化控制、实时数据驱动到ECharts多图联动的全流程开发能力并复用其中定时刷新、API模拟、主题切换等实用模块。1. 大屏可视化不是“套个模板就完事”一个真实产线监控场景里我用纯前端三件套HTMLCSSJSECharts把27个实时指标、6类告警状态、3路视频流缩略图全塞进一块4K屏零后端API调用——它本质是个「可离线运行的动态数据看板」适合中小团队快速验证大屏逻辑、嵌入已有Web系统、或作为IoT边缘设备本地监控终端。别被“可视化”三个字骗了这项目不依赖Node服务、不连数据库、不走WebSocket长连接所有数据靠JS定时模拟生成本地JSON配置驱动ECharts只负责渲染真正的业务逻辑全在data.js和control.js里。如果你正卡在“想先跑通再对接真实数据”“被Vue/React框架绑架不敢动底层渲染”“需要部署到无Node环境的工控机”这个包就是你的后悔药。2. 项目结构与技术选型为什么坚持用原生三件套而不是Vue大屏脚手架提示这不是怀旧是权衡。当你的目标设备是某国产ARM工控机仅预装Chrome 89、部署路径是file:///协议、且不允许安装任何服务时“轻量可控”比“开发体验”优先级高三个数量级。2.1 目录结构5个核心文件撑起整个大屏项目解压后共12个文件但真正参与运行的只有以下5个其余为文档、图标、字体等辅助资源├── index.html # 入口页定义4K分辨率容器、加载CSS/JS、预留视频流占位区 ├── style.css # 核心样式采用CSS GridFlex混合布局12列栅格系统适配4K屏1920×1080基准通过scale缩放 ├── data.js # 数据引擎含3类数据源——定时器模拟的实时数值如温度、压力、静态配置表设备ID→名称映射、告警规则阈值判定逻辑 ├── control.js # 控制中枢处理时间轮询、数据更新、ECharts实例管理、告警状态同步、视频缩略图切换 └── charts/ # ECharts配置集每个子文件对应一个图表line-temperature.js, pie-device-status.js等导出配置对象而非初始化代码 ├── line-temperature.js ├── bar-pressure.js ├── pie-device-status.js └── map-location.js关键设计点在于解耦渲染与数据charts/*.js只导出option对象纯JSON结构control.js统一调用echarts.init()并绑定setOption()避免每个图表重复写初始化逻辑。这种写法让二次开发时能直接替换某个图表配置而不动其他模块——比如把map-location.js换成百度地图SDK封装只需保证导出结构一致即可。2.2 技术栈选择依据为什么不用Vue/React为什么ECharts不升级到5.xHTML/CSS/JS原生规避打包构建链路。index.html中所有script标签均使用defer属性确保DOM加载完成后再执行style.css内联了所有关键样式如.screen-container { width: 3840px; height: 2160px; transform: scale(0.5); }避免FOUCFlash of Unstyled Content。ECharts 4.9.0这是最后一个支持IE11的稳定版更重要的是其setOption({})方法对增量更新更友好。实测ECharts 5.x在频繁调用setOption()时存在内存泄漏尤其在graphic组件大量使用时而本项目每秒更新12个图表必须控制内存增长。data.js中所有数据更新均采用Array.prototype.map()生成新数组杜绝直接修改原引用。零构建工具没有package.json没有webpack.config.js没有vite.config.ts。你双击index.html就能看到效果——这对产线调试太重要了。某次现场部署客户网络断开我们靠U盘拷贝文件到工控机桌面3分钟完成上线。2.3 数据流设计从模拟数据到图表渲染的完整链路整个数据流动遵循单向流原则data.js数据源 →control.js调度器 →charts/*.js配置生成 →ECharts实例渲染具体步骤如下data.js启动一个setInterval每2秒触发一次generateMockData()函数该函数返回一个包含metrics27个指标、alerts6类告警状态、videos3路缩略图URL的对象control.js监听此返回值对metrics做归一化处理如将原始温度值36.5转为{value: 36.5, unit: ℃}遍历charts/目录下所有配置文件调用其getChartOption(data)方法传入处理后的数据将生成的option对象传给对应ECharts实例的setOption()方法。关键代码在control.js第47行// control.js function updateAllCharts() { const mockData generateMockData(); // 来自data.js const normalizedData normalizeMetrics(mockData.metrics); // 归一化处理 // 遍历charts目录下所有配置文件 CHART_CONFIGS.forEach(config { const chartInstance echarts.getInstanceByDom(document.getElementById(config.id)); if (chartInstance) { // 每个配置文件必须暴露getChartOption方法 const option config.module.getChartOption({ metrics: normalizedData, alerts: mockData.alerts, videos: mockData.videos }); chartInstance.setOption(option, true); // true表示不合并完全替换 } }); }这里CHART_CONFIGS是一个静态数组定义在control.js顶部// control.js 开头 const CHART_CONFIGS [ { id: chart-temperature, module: temperatureChart }, { id: chart-pressure, module: pressureChart }, { id: chart-status-pie, module: statusPieChart } ];这种设计让新增图表只需往数组里加一项并确保对应charts/*.js文件导出getChartOption方法即可无需修改主流程。3. 核心模块拆解data.js如何模拟真实产线数据control.js怎样管理27个指标的生命周期3.1data.js不只是随机数生成器而是带业务语义的数据工厂很多人以为data.js就是Math.random()堆砌实际它实现了三层抽象物理层模拟传感器原始输出如PT100热电阻阻值变化转换层将原始值按公式转为工程单位如temp (resistance - 100) * 2.5业务层叠加设备状态影响如“设备B检修中”则温度值置为null并触发告警。核心函数generateMockData()结构如下// data.js function generateMockData() { // 1. 物理层模拟10个温度传感器原始阻值单位Ω const rawTemps Array.from({ length: 10 }, () 100 Math.sin(Date.now() / 2000 Math.random()) * 15 Math.random() * 2 ); // 2. 转换层转为摄氏度PT100标准公式简化版 const temps rawTemps.map(r parseFloat(((r - 100) * 2.5).toFixed(1))); // 3. 业务层根据设备状态注入异常值 const deviceStatus getDeviceStatus(); // 返回 { device-A: normal, device-B: maintenance } const finalTemps temps.map((t, i) { const deviceId device-${String.fromCharCode(65 i)}; return deviceStatus[deviceId] maintenance ? null : t; }); return { metrics: { temperature: finalTemps, pressure: generatePressureData(), // 类似逻辑 flowRate: generateFlowData(), // ... 其他24个指标 }, alerts: generateAlerts(finalTemps), // 基于温度值判断告警等级 videos: getVideoThumbnails() // 返回3个base64编码的缩略图URL }; }重点看generateAlerts()函数——它不是简单阈值比较而是实现多级告警联动// data.js function generateAlerts(temps) { const alerts { highTemp: 0, // 高温告警数 lowTemp: 0, // 低温告警数 offline: 0, // 离线设备数 maintenance: 0 // 维护中设备数 }; temps.forEach(temp { if (temp null) alerts.offline; else if (temp 80) alerts.highTemp; else if (temp 0) alerts.lowTemp; }); // 关键业务规则若高温告警≥3台自动触发“冷却系统异常”二级告警 if (alerts.highTemp 3) { alerts.coolingSystem { level: critical, message: 冷却系统异常建议检查水泵 }; } return alerts; }这种设计让data.js成为业务规则的集中地。当你对接真实API时只需重写generateMockData()为fetchRealData()内部规则逻辑完全复用。3.2control.js27个指标的“交通指挥中心”control.js承担四个核心职责时间调度、状态同步、图表生命周期管理、异常降级。时间调度避免 setInterval 堆积常见错误是直接写setInterval(updateAllCharts, 2000)但若updateAllCharts执行耗时超过2秒会导致定时器堆积。本项目采用setTimeout链式调用// control.js let isUpdating false; function scheduleUpdate() { if (isUpdating) return; isUpdating true; updateAllCharts().finally(() { isUpdating false; setTimeout(scheduleUpdate, 2000); }); } // 启动 scheduleUpdate();状态同步让所有图表共享同一份时间戳ECharts图表常需显示“最后更新时间”若每个图表单独取new Date()会出现毫秒级偏差。control.js在每次更新前生成统一时间戳function updateAllCharts() { const updateTime new Date(); CHART_CONFIGS.forEach(config { const option config.module.getChartOption({ // ... 其他数据 meta: { lastUpdate: updateTime.toISOString() } }); // ... }); }charts/*.js中可直接使用// charts/line-temperature.js function getChartOption(data) { return { title: { text: 温度趋势最后更新${new Date(data.meta.lastUpdate).toLocaleTimeString()} }, // ... }; }图表生命周期管理防止DOM移除后仍尝试渲染大屏常需动态显示/隐藏某些区域如点击按钮切换视图。control.js维护一个activeCharts映射表// control.js const activeCharts new Map(); // 初始化时注册 function initChart(id, chartModule) { const dom document.getElementById(id); if (!dom) return; const instance echarts.init(dom, null, { renderer: canvas }); activeCharts.set(id, { instance, module: chartModule }); } // 更新时只操作激活的图表 function updateAllCharts() { activeCharts.forEach(({ instance, module }, id) { if (instance instance.isDisposed ! true) { const option module.getChartOption(data); instance.setOption(option, true); } }); }异常降级当某个图表崩溃时不影响整体ECharts在数据格式错误时可能抛出异常导致整个更新中断。control.js对每个图表更新做独立try-catchfunction updateAllCharts() { activeCharts.forEach(({ instance, module }, id) { try { const option module.getChartOption(data); instance.setOption(option, true); } catch (err) { console.error(图表 ${id} 更新失败, err); // 记录错误但继续下一个 logErrorToConsole([${id}] ${err.message}); } }); }4. 运行与调试从双击打开到真机部署的全流程避坑指南4.1 本地快速验证绕过浏览器安全策略的三种方法现象双击index.html在Chrome中打开控制台报错Access to script at file:///xxx/data.js from origin null has been blocked by CORS policy。原因Chrome出于安全限制禁止file://协议下加载本地JS文件即使同目录。解决推荐方案用Python快速起一个HTTP服务无需安装额外工具# Python 3.x python -m http.server 8000 # 然后访问 http://localhost:8000/index.html备选方案Chrome启动时添加参数仅限调试chrome.exe --user-data-dirC:/temp/chrome_dev --unsafely-treat-insecure-origin-as-securefile:// --user-data-dirC:/temp/chrome_dev --allow-running-insecure-content file:///path/to/index.html终极方案改用Firefox它对file://协议更宽容默认允许同目录JS加载。注意不要用VS Code Live Server插件它默认启用CORS头而本项目所有请求都是同域反而会因Access-Control-Allow-Origin: *触发Chrome的跨域校验逻辑。4.2 真机部署踩坑工控机Chrome版本太低怎么办现象在某国产ARM工控机Chrome 89上部分ECharts图形渲染错乱折线图变成锯齿状地图组件空白。原因ECharts 4.9.0依赖Canvas 2D API的globalCompositeOperation属性Chrome 89对此支持不完整。解决在style.css顶部强制启用硬件加速.echarts-container { transform: translateZ(0); will-change: transform; }修改charts/map-location.js禁用graphic组件它在低版本Chrome中易崩溃// 替换原配置中的 graphic: [...] 为 graphic: null, // 或注释掉整段4.3 数据对接实战如何把mock数据替换成真实API假设你有一个REST接口GET /api/metrics返回JSON{ temperature: [36.2, 37.5, null, 35.8], pressure: [1.2, 1.3, 1.1, 1.4] }只需修改data.js中的generateMockData()为// data.js async function fetchRealData() { try { const res await fetch(/api/metrics, { cache: no-cache }); if (!res.ok) throw new Error(HTTP ${res.status}); return await res.json(); } catch (err) { console.warn(API请求失败回退到模拟数据:, err); return generateMockData(); // 降级策略 } } // 替换原定时器逻辑 async function scheduleFetch() { const data await fetchRealData(); // ... 后续处理同generateMockData() }关键经验永远保留降级路径。某次现场网络波动API连续5分钟不可用因有generateMockData()兜底大屏未黑屏只是数据暂停更新——运维人员甚至没发现异常。4.4 视频流缩略图如何在无后端情况下展示RTSP流现象客户要求显示3路RTSP摄像头画面但工控机不允许装VLC或FFmpeg。解决利用浏览器原生video标签Canvas截图本项目已内置data.js中getVideoThumbnails()返回3个blob:URL由video元素抓帧生成control.js每5秒调用captureVideoFrame()函数将video当前帧绘制到canvas再转为base64缩略图区域用img srcdata:image/png;base64,...显示。代码片段control.jsfunction captureVideoFrame(videoId, canvasId) { const video document.getElementById(videoId); const canvas document.getElementById(canvasId); const ctx canvas.getContext(2d); // 设置canvas尺寸匹配视频 canvas.width video.videoWidth; canvas.height video.videoHeight; ctx.drawImage(video, 0, 0, canvas.width, canvas.height); return canvas.toDataURL(image/png); // 返回base64字符串 }血泪经验video必须设置muted和autoplay属性否则Chrome会阻止自动播放RTSP流需经WebRTC网关转成H.264MP4格式才能被浏览器识别——这部分不在本项目范围内但index.html已预留video标签位置和ID命名规范。5. 二次开发改造如何添加新图表、接入真实数据源、定制告警推送5.1 添加新图表以“设备振动频谱图”为例假设你需要增加一个FFT频谱分析图表使用ECharts的lines类型绘制。步骤1创建配置文件新建charts/line-vibration.js// charts/line-vibration.js function getChartOption(data) { // 模拟FFT计算将原始振动值转为频域数据 const freqData data.metrics.vibration?.map((v, i) ({ name: ${i * 10}Hz, value: [i * 10, v * (1 Math.sin(i * 0.5))] })) || []; return { tooltip: { trigger: axis }, xAxis: { type: category, data: freqData.map(d d.name) }, yAxis: { type: value }, series: [{ type: line, data: freqData.map(d d.value[1]), smooth: true, areaStyle: {} }], title: { text: 设备振动频谱图, textStyle: { fontSize: 14 } } }; } export { getChartOption };步骤2注册到控制中心修改control.js中的CHART_CONFIGS数组// control.js import vibrationChart from ./charts/line-vibration.js; const CHART_CONFIGS [ // ...原有项 { id: chart-vibration, module: vibrationChart } ];步骤3在HTML中添加容器index.html中插入div idchart-vibration classecharts-container stylewidth: 600px; height: 400px;/div步骤4扩展数据源修改data.js的generateMockData()添加vibration字段// data.js function generateMockData() { // ...原有逻辑 return { metrics: { // ...原有字段 vibration: Array.from({ length: 50 }, (_, i) Math.sin(Date.now() / 1000 i * 0.1) * 5 Math.random() * 2 ) }, // ...其他字段 }; }至此新图表已集成完毕。无需重启、无需构建刷新页面即生效。5.2 接入真实数据源从HTTP API到MQTT的平滑过渡当客户从HTTP切换到MQTT时只需修改data.js的数据获取层其他模块完全不动。HTTP方式已存在// data.js async function fetchRealData() { return fetch(/api/metrics).then(r r.json()); }MQTT方式新增安装mqtt库CDN引入!-- index.html head中 -- script srchttps://unpkg.com/mqtt4.3.7/dist/mqtt.min.js/script重写data.js// data.js let mqttClient; function initMQTT() { mqttClient mqtt.connect(wss://your-mqtt-broker:8083, { clientId: dashboard- Math.random().toString(16).substr(2, 8), username: user, password: pass }); mqttClient.on(connect, () { console.log(MQTT connected); mqttClient.subscribe(sensor/#); }); mqttClient.on(message, (topic, payload) { const data JSON.parse(payload.toString()); // 将MQTT消息存入全局缓存供generateMockData()读取 latestMQTTData { ...latestMQTTData, [topic]: data }; }); } // 在generateMockData()中读取缓存 function generateMockData() { return { metrics: { temperature: latestMQTTData[sensor/temp]?.values || [25, 26, 24], // ... } }; }关键设计generateMockData()不关心数据来源只负责组装输出结构。无论是HTTP、MQTT、WebSocket还是本地文件只要最终能提供符合结构的数据上层图表渲染完全无感。5.3 定制告警推送当温度超限时发微信通知本项目默认只在页面右上角弹出告警气泡。若需外发通知只需在data.js的告警判定逻辑后插入钩子// data.js function generateAlerts(temps) { const alerts { /* ...原有逻辑 */ }; // 新增检测到高温告警时触发推送 if (alerts.highTemp 0 shouldSendWechat()) { sendWechatAlert(alerts); } return alerts; } function shouldSendWechat() { // 防抖5分钟内只发一次 const now Date.now(); if (lastWechatTime now - lastWechatTime 5 * 60 * 1000) return false; lastWechatTime now; return true; } async function sendWechatAlert(alerts) { try { // 调用企业微信机器人Webhook需提前配置 await fetch(https://qyapi.weixin.qq.com/...webhook..., { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ msgtype: text, text: { content: 【大屏告警】检测到${alerts.highTemp}台设备温度超标请及时处理 } }) }); } catch (err) { console.error(微信推送失败:, err); } }从那以后我每次加告警通道都强制走一遍「模拟触发→查看日志→确认接收」闭环。哪怕只是发到钉钉也得用curl手动调一次Webhook因为线上环境的网络策略往往比本地严苛十倍。希望帮到你。本文还有配套的精品资源点击获取