2026/5/13 12:27:14
网站建设
项目流程
php mysql网站开发项目式教程,做网站卖资料,怎样做网站的反链,看网红直播做爰的网站在鸿蒙与 Electron 的融合开发中#xff0c;跨端数据可视化是企业级应用的重要场景 —— 比如鸿蒙设备采集的工业数据、物联网传感器数据#xff0c;需要在 Electron 桌面端以图表形式直观展示#xff1b;同时 Electron 端的统计数据也能同步到鸿蒙平板端进行大屏展示。鸿蒙…在鸿蒙与 Electron 的融合开发中跨端数据可视化是企业级应用的重要场景 —— 比如鸿蒙设备采集的工业数据、物联网传感器数据需要在 Electron 桌面端以图表形式直观展示同时 Electron 端的统计数据也能同步到鸿蒙平板端进行大屏展示。鸿蒙提供了丰富的原生图表组件而 Electron 可借助 ECharts 等前端可视化库实现灵活渲染本文将实现两者的融合打造 “鸿蒙采集数据、Electron 可视化展示 双向数据同步渲染” 的完整方案附带可直接运行的代码案例和工程化配置。一、核心价值与应用场景1. 核心价值数据同源多端渲染鸿蒙与 Electron 共享同一数据源各自采用原生 / 前端技术实现图表渲染兼顾跨端一致性与原生体验。轻量化集成通过 WebSocket 实现数据实时同步无需复杂的中间件开发成本低。能力互补Electron 借助 ECharts 实现复杂的交互式图表鸿蒙利用原生图表组件保障低设备的性能表现。2. 典型应用场景工业监控鸿蒙工业网关采集设备运行数据Electron 端以折线图、仪表盘展示实时参数鸿蒙平板端展示汇总柱状图。物联网监控鸿蒙传感器采集温湿度数据Electron 端展示实时趋势图鸿蒙手机端展示告警饼图。办公统计Electron 端生成的销售数据报表同步到鸿蒙平板端进行大屏展示。二、环境搭建与前置准备1. 基础环境要求ElectronNode.jsv18、Electronv28、echarts数据可视化、wsWebSocket 数据同步、axios数据请求鸿蒙DevEco Studio最新版、鸿蒙 SDKAPI 10、鸿蒙设备 / 模拟器开启开发者模式数据模拟本文使用随机数模拟鸿蒙设备采集的传感器数据实际项目可替换为真实硬件数据采集逻辑2. 工程化初始化2.1 创建 Electron 工程bash运行# 初始化项目 mkdir harmony-electron-chart cd harmony-electron-chart npm init -y # 安装核心依赖 npm install electron electron-builder echarts ws --save npm install nodemon --save-dev2.2 配置 package.jsonjson{ name: harmony-electron-chart, version: 1.0.0, main: main/index.js, scripts: { start: electron ., dev: nodemon --exec electron ., build: electron-builder }, build: { appId: com.example.harmonychart, productName: HarmonyElectronChart, directories: { output: dist }, win: { target: nsis }, mac: { target: dmg }, linux: { target: deb } } }2.3 鸿蒙工程配置网络与权限在鸿蒙工程的entry/src/main/module.json5中添加网络权限json5{ module: { name: entry, type: entry, requestPermissions: [ { name: ohos.permission.INTERNET }, { name: ohos.permission.DISTRIBUTED_DATASYNC } ], distributedConfiguration: { deviceCommunication: true, dataSync: true } } }三、核心代码案例跨端数据同步与可视化渲染整体流程说明鸿蒙端模拟传感器数据采集温湿度、设备负载通过 WebSocket 将实时数据发送到 Electron 端同时接收 Electron 端的统计数据使用鸿蒙原生图表组件渲染。Electron 端启动 WebSocket 服务接收鸿蒙端的实时数据通过 ECharts 渲染交互式图表同时生成统计数据并发送到鸿蒙端。步骤 1Electron 端实现 WebSocket 服务与 ECharts 可视化1.1 Electron 主进程WebSocket 服务 窗口管理javascript运行// main/index.js const { app, BrowserWindow, ipcMain } require(electron); const path require(path); const WebSocket require(ws); // 全局变量 let mainWindow; let wss; // WebSocket服务实例 // 存储鸿蒙客户端连接 let harmonyWs null; // 创建Electron窗口 function createWindow() { mainWindow new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, ../preload/index.js), contextIsolation: true, sandbox: false } }); mainWindow.loadFile(path.join(__dirname, ../renderer/index.html)); mainWindow.webContents.openDevTools(); } // 启动WebSocket服务端口8080 function startWebSocketServer() { wss new WebSocket.Server({ port: 8080 }); console.log(Electron WebSocket服务已启动端口8080); wss.on(connection, (ws) { console.log(鸿蒙设备已连接); harmonyWs ws; // 接收鸿蒙端数据转发到渲染进程 ws.on(message, (data) { const dataStr data.toString(); console.log(收到鸿蒙端数据, dataStr); mainWindow.webContents.send(harmony-data, JSON.parse(dataStr)); }); // 监听连接关闭 ws.on(close, () { console.log(鸿蒙设备连接关闭); harmonyWs null; }); ws.on(error, (err) { console.error(WebSocket错误, err); harmonyWs null; }); }); } // 暴露发送数据到鸿蒙的接口 ipcMain.handle(send-data-to-harmony, (event, data) { if (harmonyWs harmonyWs.readyState WebSocket.OPEN) { harmonyWs.send(JSON.stringify(data)); return { success: true }; } else { return { success: false, message: 鸿蒙设备未连接 }; } }); // 暴露获取本机IP的接口 ipcMain.handle(get-local-ip, () { const os require(os); const interfaces os.networkInterfaces(); for (const dev in interfaces) { const iface interfaces[dev]; for (const alias of iface) { if (alias.family IPv4 !alias.internal) { return alias.address; } } } return 127.0.0.1; }); // 应用就绪后初始化 app.whenReady().then(() { createWindow(); startWebSocketServer(); app.on(activate, () { if (BrowserWindow.getAllWindows().length 0) createWindow(); }); }); app.on(window-all-closed, () { if (process.platform ! darwin) { if (wss) wss.close(); app.quit(); } });1.2 Electron 预加载脚本暴露 APIjavascript运行// preload/index.js const { contextBridge, ipcRenderer } require(electron); contextBridge.exposeInMainWorld(electronApi, { // 获取本机IP getLocalIp: () ipcRenderer.invoke(get-local-ip), // 发送数据到鸿蒙 sendDataToHarmony: (data) ipcRenderer.invoke(send-data-to-harmony, data), // 监听鸿蒙数据 onHarmonyData: (callback) ipcRenderer.on(harmony-data, (event, data) callback(data)), // 移除监听 removeHarmonyDataListener: () ipcRenderer.removeAllListeners(harmony-data) });1.3 Electron 渲染进程ECharts 可视化渲染html预览!-- renderer/index.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 title鸿蒙Electron数据可视化/title style body { font-family: Arial, sans-serif; padding: 20px; background-color: #f5f5f5; margin: 0; } .chart-container { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; max-width: 1200px; margin: 0 auto; } .chart-item { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); height: 350px; } #temperatureChart, #humidityChart, #loadChart, #alarmChart { width: 100%; height: 100%; } .status { max-width: 1200px; margin: 20px auto; padding: 10px; border-radius: 4px; background-color: #e9ecef; } button { padding: 10px 20px; margin: 10px 0; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #0056b3; } /style /head body div classstatus idstatus就绪等待鸿蒙数据连接.../div button onclicksendStatData()发送统计数据到鸿蒙/button div classchart-container div classchart-item h3实时温度℃/h3 div idtemperatureChart/div /div div classchart-item h3实时湿度%/h3 div idhumidityChart/div /div div classchart-item h3设备负载%/h3 div idloadChart/div /div div classchart-item h3告警类型分布/h3 div idalarmChart/div /div /div script src../node_modules/echarts/dist/echarts.min.js/script script // 初始化ECharts实例 const temperatureChart echarts.init(document.getElementById(temperatureChart)); const humidityChart echarts.init(document.getElementById(humidityChart)); const loadChart echarts.init(document.getElementById(loadChart)); const alarmChart echarts.init(document.getElementById(alarmChart)); // 初始化数据 let timeData []; let temperatureData []; let humidityData []; let loadData []; // 告警数据 const alarmData [ { name: 温度异常, value: 0 }, { name: 湿度异常, value: 0 }, { name: 负载过高, value: 0 }, { name: 网络异常, value: 0 } ]; // 配置图表选项 const lineOption { title: { left: center }, tooltip: { trigger: axis }, legend: { top: 0 }, grid: { left: 3%, right: 4%, bottom: 3%, containLabel: true }, xAxis: { type: category, data: timeData, boundaryGap: false }, yAxis: { type: value }, series: [{ type: line, data: [] }] }; const pieOption { title: { left: center }, tooltip: { trigger: item }, legend: { orient: vertical, left: 10 }, series: [{ name: 告警数量, type: pie, radius: [40%, 70%], data: alarmData, emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: rgba(0, 0, 0, 0.5) } } }] }; // 初始化图表 temperatureChart.setOption({ ...lineOption, series: [{ name: 温度, data: temperatureData }] }); humidityChart.setOption({ ...lineOption, series: [{ name: 湿度, data: humidityData }] }); loadChart.setOption({ ...lineOption, series: [{ name: 负载, data: loadData }] }); alarmChart.setOption(pieOption); // 状态更新函数 function updateStatus(message, isError false) { const now new Date().toLocaleString(); const statusDiv document.getElementById(status); statusDiv.innerHTML [${now}] ${message}; statusDiv.style.color isError ? red : black; } // 处理鸿蒙数据 function handleHarmonyData(data) { updateStatus(收到数据温度${data.temperature}℃湿度${data.humidity}%负载${data.load}%); // 限制数据长度最多显示20个点 const maxLen 20; const now new Date().toLocaleTimeString(); timeData.push(now); temperatureData.push(data.temperature); humidityData.push(data.humidity); loadData.push(data.load); if (timeData.length maxLen) { timeData.shift(); temperatureData.shift(); humidityData.shift(); loadData.shift(); } // 更新告警数据模拟 if (data.temperature 35) alarmData[0].value; if (data.humidity 70) alarmData[1].value; if (data.load 80) alarmData[2].value; // 更新图表 temperatureChart.setOption({ xAxis: { data: timeData }, series: [{ data: temperatureData }] }); humidityChart.setOption({ xAxis: { data: timeData }, series: [{ data: humidityData }] }); loadChart.setOption({ xAxis: { data: timeData }, series: [{ data: loadData }] }); alarmChart.setOption({ series: [{ data: alarmData }] }); } // 发送统计数据到鸿蒙 async function sendStatData() { const statData { type: stat, totalAlarm: alarmData.reduce((sum, item) sum item.value, 0), avgTemperature: (temperatureData.reduce((sum, val) sum val, 0) / temperatureData.length).toFixed(1), alarmRatio: (alarmData[0].value / (alarmData.reduce((sum, item) sum item.value, 0) || 1) * 100).toFixed(1) }; const response await window.electronApi.sendDataToHarmony(statData); if (response.success) { updateStatus(已发送统计数据${JSON.stringify(statData)}); } else { updateStatus(发送失败${response.message}, true); } } // 初始化监听鸿蒙数据 window.electronApi.onHarmonyData(handleHarmonyData); updateStatus(本机IP${await window.electronApi.getLocalIp()}等待鸿蒙设备连接); // 页面关闭时移除监听 window.onbeforeunload () { window.electronApi.removeHarmonyDataListener(); }; /script /body /html步骤 2鸿蒙端实现数据采集与 WebSocket 客户端2.1 鸿蒙端数据采集工具类模拟传感器数据typescript运行// entry/src/main/ets/utils/DataCollectUtil.ets // 模拟传感器数据结构 export interface SensorData { type: real-time; temperature: number; // 温度℃ humidity: number; // 湿度% load: number; // 设备负载% timestamp: number; } // 生成模拟传感器数据 export function generateSensorData(): SensorData { // 模拟温度25~40℃随机波动 const temperature (25 Math.random() * 15).toFixed(1); // 模拟湿度40~80%随机波动 const humidity (40 Math.random() * 40).toFixed(1); // 模拟负载30~90%随机波动 const load (30 Math.random() * 60).toFixed(1); return { type: real-time, temperature: Number(temperature), humidity: Number(humidity), load: Number(load), timestamp: Date.now() }; }2.2 鸿蒙端 WebSocket 客户端数据发送与接收typescript运行// entry/src/main/ets/utils/WebSocketClient.ets import webSocket from ohos.net.webSocket; import { BusinessError } from ohos.base; import { SensorData } from ./DataCollectUtil; // WebSocket实例 let ws: webSocket.WebSocket | null null; // 数据发送定时器 let dataTimer: number | null null; // 连接Electron的WebSocket服务 export async function connectElectronWs(serverIp: string, port: number) { try { ws webSocket.createWebSocket(); // 监听连接成功 ws.on(open, () { console.log(已连接到Electron WebSocket服务); // 启动定时发送数据每1秒发送一次 startSendDataTimer(); }); // 监听接收Electron数据 ws.on(message, (message: string | ArrayBuffer) { const data JSON.parse(message.toString()); console.log(收到Electron统计数据, data); // 触发UI更新可通过回调传递到页面 if (data.type stat) { onStatDataReceived(data); } }); // 监听连接关闭 ws.on(close, (code: number, reason: string) { console.log(WebSocket连接关闭${code} - ${reason}); stopSendDataTimer(); // 自动重连5秒后 setTimeout(() { connectElectronWs(serverIp, port); }, 5000); }); // 监听错误 ws.on(error, (error: BusinessError) { console.error(WebSocket错误, error); stopSendDataTimer(); }); // 连接服务 await ws.connect(ws://${serverIp}:${port}); } catch (error) { console.error(连接WebSocket失败, error); setTimeout(() { connectElectronWs(serverIp, port); }, 5000); } } // 启动定时发送数据 function startSendDataTimer() { if (dataTimer) { clearInterval(dataTimer); } // 每1秒发送一次模拟数据 dataTimer setInterval(() { sendSensorData(); }, 1000) as unknown as number; } // 停止定时发送数据 export function stopSendDataTimer() { if (dataTimer) { clearInterval(dataTimer); dataTimer null; } } // 发送传感器数据 function sendSensorData() { if (ws ws.readyState webSocket.ReadyState.OPEN) { const data generateSensorData(); ws.send(JSON.stringify(data)); } } // 统计数据接收回调需在页面中实现 let onStatDataReceived: (data: any) void () {}; // 设置统计数据回调 export function setStatDataCallback(callback: (data: any) void) { onStatDataReceived callback; } // 关闭WebSocket连接 export function closeWs() { stopSendDataTimer(); if (ws) { ws.close(); ws null; } }2.3 鸿蒙端页面原生图表渲染 数据展示typescript运行// entry/src/main/ets/pages/Index.ets import { connectElectronWs, setStatDataCallback, closeWs, stopSendDataTimer } from ../utils/WebSocketClient; import { Chart, ChartData, LineChartSeries, BarChartSeries } from ohos/components-chart; // 鸿蒙原生图表组件 import common from ohos.app.ability.common; Entry Component struct Index { State message: string 连接Electron中...; State statData: any { totalAlarm: 0, avgTemperature: 0, alarmRatio: 0 }; // 折线图数据接收Electron统计数据的历史 State lineData: ChartData { categories: [], series: [{ name: 总告警数, data: [] } as LineChartSeries] }; // 柱状图数据 State barData: ChartData { categories: [平均温度, 告警占比(%)], series: [{ name: 数据, data: [0, 0] } as BarChartSeries] }; private context getContext(this) as common.UIAbilityContext; private maxDataLen 10; // 折线图最多显示10个点 aboutToAppear() { // 连接Electron的WebSocket服务替换为实际Electron设备IP connectElectronWs(192.168.1.101, 8080); // 设置统计数据回调 setStatDataCallback((data) { this.handleStatData(data); }); } aboutToDisappear() { // 清理资源 closeWs(); stopSendDataTimer(); } // 处理Electron统计数据 handleStatData(data: any) { this.statData data; // 更新柱状图 this.barData.series[0].data [Number(data.avgTemperature), Number(data.alarmRatio)]; // 更新折线图 const now new Date().toLocaleTimeString(); this.lineData.categories.push(now); this.lineData.series[0].data.push(data.totalAlarm); // 限制数据长度 if (this.lineData.categories.length this.maxDataLen) { this.lineData.categories.shift(); this.lineData.series[0].data.shift(); } this.message 收到统计数据总告警${data.totalAlarm}条平均温度${data.avgTemperature}℃; } build() { Column() { Text(this.message) .fontSize(20) .fontWeight(FontWeight.Bold) .margin({ top: 20, bottom: 20 }) .textAlign(TextAlign.Center); // 原生折线图总告警数趋势 Chart({ type: Chart.Type.LINE, data: this.lineData, options: { title: { text: 总告警数趋势 }, legend: { position: Chart.LegendPosition.BOTTOM }, xAxis: { label: { rotate: 30 } }, yAxis: { min: 0 } } }) .width(90%) .height(300) .margin({ bottom: 20 }); // 原生柱状图平均温度与告警占比 Chart({ type: Chart.Type.BAR, data: this.barData, options: { title: { text: 统计数据 }, legend: { position: Chart.LegendPosition.BOTTOM } } }) .width(90%) .height(300); } .width(100%) .height(100%) .backgroundColor(Color.White); } }四、运行与测试流程1. 鸿蒙侧运行在 DevEco Studio 中修改鸿蒙代码中的 Electron 设备 IP 地址确保两者处于同一局域网。将鸿蒙工程运行到真机或模拟器应用将自动连接 Electron 的 WebSocket 服务并发送模拟数据。2. Electron 侧运行执行命令启动 Electron 应用bash运行npm run start查看 Electron 界面将实时显示鸿蒙端发送的温湿度、负载数据并渲染为折线图和饼图。点击 “发送统计数据到鸿蒙” 按钮鸿蒙端将接收统计数据并通过原生图表渲染。3. 测试验证点Electron 端是否实时接收鸿蒙数据并更新图表。鸿蒙端是否接收 Electron 统计数据并渲染原生图表。数据超过最大长度时图表是否自动截断旧数据。连接断开后鸿蒙端是否自动重连。五、工程化优化与避坑指南1. 优化建议数据压缩若传输大量数据可使用 gzip 压缩数据后再通过 WebSocket 发送减少网络开销。数据持久化将重要的传感器数据存储到鸿蒙分布式数据库和 Electron 本地数据库便于历史数据查询。异常处理添加数据合法性校验过滤异常数据如温度为负数、负载超过 100%。图表交互增强Electron 端添加图表的缩放、导出图片功能鸿蒙端添加图表的点击事件响应。2. 常见坑点与解决方案WebSocket 连接失败确保 Electron 与鸿蒙设备处于同一局域网关闭防火墙检查端口是否被占用。鸿蒙图表组件渲染失败确保鸿蒙工程已正确引入图表组件依赖DevEco Studio 的 SDK 版本与组件版本兼容。Electron 图表数据更新卡顿限制数据长度如最多显示 20 个点避免渲染过多数据导致性能下降。数据同步延迟减少 WebSocket 发送数据的频率如从 1 秒一次改为 2 秒一次或使用批量发送策略。六、扩展场景真实硬件数据采集本文使用随机数模拟传感器数据实际项目中可替换为真实硬件数据采集逻辑鸿蒙端通过鸿蒙的硬件访问 API如 GPIO、串口读取传感器数据或通过 MQTT 接收物联网设备数据。Electron 端通过串口、USB 或网络协议读取本地硬件数据同步到鸿蒙端进行渲染。七、总结本文通过 WebSocket 实现了鸿蒙与 Electron 的实时数据同步结合 ECharts 和鸿蒙原生图表组件完成了跨端数据可视化的完整方案。这种方案充分利用了 Electron 前端可视化的灵活性和鸿蒙原生组件的性能优势解决了跨端数据展示的核心问题。开发者可基于本文的思路拓展更多功能如添加数据告警阈值设置、历史数据回放、多设备数据聚合展示等进一步完善跨端数据可视化体系。随着鸿蒙生态的不断完善Electron 与鸿蒙的融合将为数据可视化类跨端应用带来更多创新可能。欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net)一起共建开源鸿蒙跨平台生态。