Vue + WebSocket 实时数据可视化实战:多源融合与模拟数据双模式设计

发布于:2025-07-26 ⋅ 阅读:(13) ⋅ 点赞:(0)

在现代交通大屏项目中,实时数据的采集和可视化尤为重要。本文结合 Vue3 和 ECharts,分享一个支持多 WebSocket 数据源实时合并、模拟数据调试、自动重连的完整设计方案,帮助你快速搭建健壮的数据可视化组件。


一、项目背景与核心需求

  • 实时接收多个 WebSocket 数据源(不同服务器或端口)

  • 设计模拟数据接口,方便本地开发调试

  • 支持数据的自动合并(如车流总量、车辆类型分布)

  • 使用 ECharts 动态展示统计数据

  • 保证 WebSocket 断线自动重连,提高稳定性


二、项目架构与核心状态管理

定义一个全局响应式对象 sources,分别存储四个数据源的数据,支持真实数据和模拟数据统一写入。



const USE_MOCK = true; // 是否启用模拟数据 
const sources = reactive({ su1: {}, su2: {}, su3: {}, su4: {}, });

三、WebSocket 连接与模拟数据设计

1. 真实 WebSocket 连接实现

使用原生 WebSocket 连接服务器,设置事件监听,支持断线自动重连。


function createRealWS(url, setTarget) {
  const ws = new WebSocket(url);

  ws.onopen = () => console.log(`🟢 WebSocket ${url} 已连接`);
  
  ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    setTarget(JSON.parse(data.data));
  };

  ws.onerror = () => console.error(`WebSocket ${url} 出错`);
  
  ws.onclose = () => {
    console.warn(`WebSocket ${url} 关闭,3秒后重连...`);
    setTimeout(() => createRealWS(url, setTarget), 3000);
  };
}

2. 模拟数据接口

为了方便本地开发,使用定时器生成结构一致的模拟数据,模拟数据每3秒刷新一次。


function createMockSource(setTarget) {
  setInterval(() => {
    const mock = {
      timestamp: Date.now(),
      globalTime: new Date().toLocaleString(),
      totalVehiCount: Math.floor(Math.random() * 1000),
      aveSpeed: +(30 + Math.random() * 10).toFixed(2),
      numVehiByType: Object.fromEntries(
        [1, 2, 3, 7, 8, 10, 11, 15, 100].map(k => [k, Math.floor(Math.random() * 100)])
      ),
    };
    setTarget(mock);
  }, 3000);
}

3. 初始化所有数据源

根据 WebSocket URL 端口号映射到对应数据源,启用模拟或真实数据。

function initAllSources() {
  const urls = [
    "ws://xx/wsStatisJd",
    "ws://xx/wsStatisJd",
    "ws://xx/wsStatisJd",
    "ws://xx/wsStatisJd",
  ];

  urls.forEach((url) => {
    const key = getSourceKeyByPort(url); // su1 su2 su3 su4
    const setFn = (data) => (sources[key] = data);

    if (USE_MOCK) {
      createMockSource(setFn);
    } else {
      createRealWS(url, setFn);
    }
  });
}

四、数据合并与格式化

合并车流总量

将四个数据源的车辆总数相加,确保数值准确。


function mergeTotal(...totals) {
  return totals.reduce((sum, val) => sum + Number(val || 0), 0);
}

合并车辆类型分布

对每种车辆类型进行累加。


格式化合并后的车辆类型数据,固定顺序输出并计算百分比


function formatVehicleTypeData(numVehiByType, total = 0) {
  const fixedOrder = [8, 3, 2, 1, 15, 7, 10, 11, 100];
  return fixedOrder.map(key => {
    const value = numVehiByType[key] || 0;
    const name = vehicleTypeMap[key] || `类型${key}`;
    const percent = total > 0 ? ((value / total) * 100).toFixed(1) : "0.0";
    return {
      name,
      value,
      percent: Number(percent),
      color: vehicleColorMap[name] || "#999999",
    };
  });
}

五、响应式数据更新与图表刷新

通过 watchEffect 监听数据变化,自动计算合并数据并刷新 ECharts 饼图。


watchEffect(() => {
  const { su1, su2, su3, su4 } = sources;

  const mergedTotal = mergeTotal(
    su1.totalVehiCount,
    su2.totalVehiCount,
    su3.totalVehiCount,
    su4.totalVehiCount
  );

  const mergedType = mergeVehicleType(
    su1.numVehiByType || {},
    su2.numVehiByType || {},
    su3.numVehiByType || {},
    su4.numVehiByType || {}
  );

  chartData.value = formatVehicleTypeData(mergedType, mergedTotal);
  realtimeTime.value = new Date().toLocaleString();
  totalVehiCount.value = mergedTotal;

  aveSpeed.value = {
    su1: su1.aveSpeed || 0,
    su2: su2.aveSpeed || 0,
    su3: su3.aveSpeed || 0,
    su4: su4.aveSpeed || 0,
  };

  InitEchart2(chartData.value);
});

六、ECharts 饼图动态渲染

初始化并动态更新饼图,颜色对应车辆类型,关闭标签和提示框保证大屏美观。


const InitEchart2 = (data) => {
  const chartDom = document.getElementById("map-left-4-1-echarts");
  if (!chartDom) return;

  if (!myChart) {
    myChart = echarts.init(chartDom);
  }

  const colorList = data.map(item => item.color || "#ccc");

  myChart.setOption({
    color: colorList,
    tooltip: { show: false },
    series: [{
      name: "车辆类型占比",
      type: "pie",
      radius: ["55%", "80%"],
      avoidLabelOverlap: false,
      itemStyle: {
        borderRadius: 1,
        borderColor: "#2c3950",
        borderWidth: 2,
      },
      label: { show: false },
      emphasis: { scale: false, label: { show: false } },
      labelLine: { show: false },
      data: data.map(({ name, value }) => ({ name, value })),
    }],
  });
};

七、启动与定时刷新逻辑

在组件挂载时,初始化数据接口和数据源,并设置定时器周期刷新相关统计数据。


onMounted(() => {
  curDayCountData();       // 获取今日车流初始数据
  initAllSources();        // 启动 WebSocket / 模拟数据
  getDeviceOnlineData();   // 设备在线率数据
  curDayEventCountData();  // 今日事件统计
  eventHistoryCountData(); // 事件历史统计

  dataRefreshTimer = setInterval(() => {
    curDayCountData();
    getDeviceOnlineData();
    curDayEventCountData();
    eventHistoryCountData();
  }, 30000);
});

onUnmounted(() => {
  if (dataRefreshTimer) clearInterval(dataRefreshTimer);
});

八、总结

  • 多数据源实时管理,灵活切换模拟/真实数据,提升开发效率

  • 自动重连机制,保证 WebSocket 长连接稳定可靠

  • 响应式合并处理,统一计算统计数据,确保展示准确

  • ECharts 动态刷新,实现流畅视觉效果,符合大屏需求

如果你正在做交通、工业或监控领域的实时可视化,这个方案值得借鉴。


网站公告

今日签到

点亮在社区的每一天
去签到