船队运营可视化技术方案

基于 Vue 3 + TypeScript + Leaflet + HiFleet SDK 的海运大屏示例,面向"航线轨迹展示 + 航线总览 + 主题/语言切换 + 实时船位增量刷新"的场景。

当前工程的核心思路不是把所有能力都压到单一地图 SDK 上,而是采用:

  • HiFleet SDK 负责海图底座、底图模式、地图语言。
  • Leaflet 负责轨迹、点位、船舶标记、tooltip 等业务覆盖层。
  • Vue 3 负责大屏 UI、主题、国际化、交互状态与数据编排。

这样做的目的是把"底图能力"和"业务可定制层"分开,降低后续接真实数据或替换底图方案的成本。

当前实际技术栈

  • Vue 3 + <script setup>: 页面结构、状态编排、组件通信。
  • TypeScript: 统一定义航次、轨迹点、实时消息等模型。
  • HiFleet SDK: 底图实例、地图语言、底图模式切换。
  • Leaflet: 轨迹、船舶标记、线段、点位、tooltip 覆盖层。 当前主要通过 HiFleet SDK 暴露的 window.L 能力接入,并在 src/types/hifleet-map.ts 中维护项目内的最小类型抽象。
  • vue-i18n: 中英文切换。
  • v-scale-screen: 1920x1080 大屏缩放适配。
  • Vitest + Vue Test Utils + jsdom: service、composable、组件级单测。

当前目录结构

text 复制代码
src/
  App.vue
  main.ts
  style.css
  components/
    layout/
      ScreenAdapter.vue
    map/
      MapStage.vue
      MapToolbar.vue
      MapStage.test.ts
    panels/
      RouteListPanel.vue
  composables/
    useDashboardPreferences.ts
    useDashboardPreferences.test.ts
    useHifleetMap.ts
    useMockVoyageStream.ts
    useVoyageDashboard.ts
    useVoyageSocket.ts
  data/
    mockVoyages.ts
  i18n/
    index.ts
  locales/
    dashboard.ts
  services/
    hifleet-loader.ts
    mock-voyage-stream.ts
    mock-voyage-stream.test.ts
    trajectory-optimizer.ts
    trajectory-optimizer.test.ts
    voyage-realtime.ts
    voyage-realtime.test.ts
  types/
    hifleet-map.ts
    realtime.ts
    voyage.ts
  vite-env.d.ts

说明:

  • 当前仓库没有 ShipDetailPanel.vue,右侧详情面板仍是后续可扩展项。
  • 当前仓库没有 worker 渲染链路,海量点优化主要依赖视野过滤、抽稀和单航次增量刷新。

类型分层

当前项目把类型定义按职责拆在 src/types 下,便于业务层、服务层和地图层分别演进:

  • voyage.ts
    • 定义航次主模型,包括航次摘要、指标、挂靠计划、轨迹点和地图模式。
  • realtime.ts
    • 定义 WebSocket 实时事件、连接状态、实时快照与增量轨迹点。
  • hifleet-map.ts
    • 定义项目内使用到的 Leaflet / Map / Layer 最小能力接口,用于隔离第三方 SDK 实现细节。

架构分层

  1. App.vue
    • 负责拼装页面壳层,连接地图舞台、左侧航线面板和主题/语言偏好。
  2. useVoyageDashboard.ts
    • 维护航线列表、当前选中航次、左侧统计摘要。
  3. MapStage.vue
    • 负责地图舞台头部、主题/语言按钮、底图模式工具栏。
  4. useHifleetMap.ts
    • 负责 SDK 加载、地图实例初始化、覆盖层渲染、实时点位刷新。
  5. RouteListPanel.vue
    • 左侧航线总览列表和汇总卡片。
  6. useVoyageSocket.ts
    • 真实 WebSocket 事件接入、断线重连与心跳保活。
  7. useMockVoyageStream.ts
    • 未配置真实 WS 地址时的 mock 实时流兜底。
  8. hifleet-loader.ts
    • 负责显式注入 HiFleet SDK 的 CSS 和 JS 资源。

当前 Demo 能力

  • 左侧展示航线总览、状态、装载率、ETA、延误摘要。
  • 中央地图展示多条航线轨迹,并高亮当前选中航次。
  • 支持底图模式切换:标准 / 海图 / 卫星
  • 支持中英文切换。
  • 支持大屏主题切换,并对地图本体做可见的深浅色处理。
  • 点击左侧航线卡片或地图轨迹,可切换当前选中航次。
  • 未配置 VITE_VOYAGE_WS_URL 时,默认启用 mock 实时事件流。
  • 配置 VITE_VOYAGE_WS_URL 后,可切换到真实 WebSocket 船位事件流。
  • 轨迹绘制支持按地图视野过滤、按 zoom 抽稀和单航次增量刷新。
  • 页面默认按 1920x1080 设计稿做缩放适配。

HiFleet SDK 深度定制能力边界

这一节是本项目最重要的落地结论。

当前项目实际使用了什么

当前代码里,HiFleet SDK 主要承担以下职责:

  • 创建地图实例。
  • 暴露底层 Leaflet Map 对象。
  • 切换底图模式:global / nauticalmap / satellite
  • 切换地图语言:cn / en

也就是说,本项目对 HiFleet 的使用更接近"可切换底图的海图底座",而不是"全业务渲染引擎"。

HiFleet SDK 适合做什么

基于公开文档和当前接入方式,HiFleet 更适合以下场景:

  • 使用官方海图、标准图、卫星图作为底座。
  • 使用官方支持的地图语言切换。
  • 使用官方开放的天气、洋流、风场、港口等标准能力。
  • 快速搭建航运领域的底图和海事图层能力。

HiFleet SDK 不适合做什么

如果目标是"深度定制化地图引擎",HiFleet 不是一个理想边界内的方案。当前能明确看到的限制包括:

  1. 底图换肤能力有限

    • 公开 API 能确认的是底图模式切换,不是完整的品牌级换肤系统。
    • 目前没有可靠公开能力可以像 Mapbox Style 那样对道路、海洋、陆地、文字、边界做逐层样式编排。
  2. 地图渲染管线不可控

    • SDK 内部实现和 pane 结构由其自身接管。
    • 业务方可以在外部加 Leaflet 覆盖层,但不能真正接管底图渲染规则。
  3. 地图 UI 深改空间有限

    • 顶部按钮、浮层、主题按钮这类强业务 UI,仍然建议由外层 Vue 自己实现。
    • 不建议把复杂业务交互强行塞到 SDK 内建 UI 上。
  4. 品牌化主题能力有限

    • 目前项目里做的"地图深浅主题",本质上是对底图区域做 CSS 滤镜和背景处理。
    • 这是工程上可用的增强,不等同于底图源本身支持完整皮肤系统。
  5. 更深层控制需要自建方案

    • 如果你要做矢量瓦片样式编辑、动态图层规则、品牌底图、地图控件体系统一,HiFleet 不适合作为唯一核心层。

本项目对 HiFleet 的取舍策略

本项目采用的是一个很现实的折中方案:

  • HiFleet 提供航运地图底座。
  • Leaflet 提供业务覆盖层和交互。
  • CSS 主题类 提供地图区域的深浅视觉增强。

这个策略的优点:

  • 上手快,行业能力现成。
  • 业务层仍然可控。
  • 后续替换底图成本可控。

这个策略的缺点:

  • 地图主题是"增强层",不是底图原生换肤。
  • 超深度样式统一仍然受制于 SDK 本身。

当前地图主题方案说明

当前项目里的"地图主题切换"分成两层:

  1. 大屏 UI 主题
    • 通过 data-theme 和 CSS 变量切换整体界面视觉。
  2. 地图本体主题
    • 通过 MapStage 上的主题类和 .leaflet-tile-pane 滤镜,对底图做深浅色增强。

这意味着:

  • 你可以获得肉眼可见的深浅变化。
  • 但这不代表 HiFleet 官方提供了完整地图皮肤 API。
  • 如果未来要品牌级地图风格统一,仍需要更换底图方案或自建样式系统。

平替方案

这里的"平替"不是单纯替换一个 SDK,而是根据定制深度选方案。

方案一:保留 HiFleet,继续采用"底图 + 覆盖层"模式

适合:

  • 仍然需要海图、航运领域底图能力。
  • 主要定制点在业务轨迹、船舶标记、主题壳层、面板交互。

做法:

  • 继续使用 HiFleet 做底图。
  • 业务图层全部放在 Leaflet 覆盖层。
  • 主题仍然走 CSS 滤镜增强。

优点:

  • 接入成本最低。
  • 行业能力保留最多。

缺点:

  • 深度换肤和品牌化地图风格仍然受限。

方案二:纯 Leaflet + 自有瓦片服务

适合:

  • 需要稳定二维地图能力。
  • 需要深浅主题、品牌底图、底图替换自由度。
  • 不强依赖 HiFleet 官方航运图层。

做法:

  • 直接使用 Leaflet
  • 底图改为自有瓦片、第三方瓦片或深浅色双套 tile 源。
  • 业务轨迹仍沿用当前覆盖层思路。

优点:

  • 成本适中。
  • 底图可控性明显提升。
  • 与当前项目代码结构最兼容。

缺点:

  • 需要自己解决海图来源和行业数据接入。

方案三:MapLibre GL JS + 矢量瓦片样式

适合:

  • 需要真正的地图换肤、品牌色、图层规则、文字样式统一。
  • 需要高度可编排的地图主题系统。

做法:

  • MapLibre GL JS 替代 HiFleet 底图层。
  • 使用矢量瓦片和 style JSON 做地图样式控制。
  • 航运业务图层按 GL 图层或自定义 overlay 重建。

优点:

  • 真正支持深度样式定制。
  • 地图视觉体系最完整。

缺点:

  • 迁移成本最高。
  • 航运专项图层需要自己补。

方案四:Cesium 或三维引擎

适合:

  • 目标已经从二维运营大屏升级到三维全球态势、航线回放、时空演化。

优点:

  • 三维表现力强。

缺点:

  • 工程复杂度高。
  • 对当前二维业务场景通常属于过度设计。

选型建议

如果你的目标是:

  • 快速上线航运地图大屏
    • 继续用 HiFleet + Leaflet
  • 做深浅主题、品牌化 UI,但接受底图不是完全原生换肤
    • 继续用当前方案。
  • 做真正的地图换肤系统、品牌地图、可编排样式体系
    • 优先评估 Leaflet + 自有底图MapLibre GL JS
  • 做三维态势或全球地球场景
    • 再考虑 Cesium

实时通信设计

WebSocket 事件类型

  • voyage.snapshot: 航次快照,更新进度、ETA、装载率、速度、告警等。
  • voyage.point: 增量轨迹点,适合船位流式刷新。
  • voyage.alert: 告警事件,直接推送异常标签。
  • heartbeat: 心跳包,用于保活和链路可观测。

通信建议

  1. 接口层统一做协议归一化
    • 前后端字段不要直接散落到组件层,统一在 voyage-realtime.ts 处理。
  2. 增量更新代替全量覆盖
    • voyage.point 只追加单点,并限制单航次最大轨迹长度。
  3. 断线自动重连
    • 当前 demo 已带指数退避重连和心跳保活。
  4. mock 流兜底
    • 无真实 WS 地址时,自动切换到 mock 事件流,方便演示与联调。

环境变量

当前项目实际读取以下 Vite 环境变量:

  • VITE_VOYAGE_WS_URL
    • 真实 WebSocket 地址;未配置时自动回退到 mock 实时流。
  • VITE_HIFLEET_SDK_KEY
    • HiFleet SDK key;配置后会按官方 key 地址拼接 SDK 脚本。
  • VITE_HIFLEET_SDK_SRC
    • 自定义 SDK JS 地址;适合内网代理、私有镜像或固定版本接入。
  • VITE_HIFLEET_SDK_CSS
    • 自定义 SDK CSS 地址;用于覆盖默认样式资源地址。

示例:

bash 复制代码
VITE_VOYAGE_WS_URL=wss://example.com/voyage/ws
VITE_HIFLEET_SDK_KEY=your-sdk-key
VITE_HIFLEET_SDK_SRC=https://sdk.hifleet.com/sdk/get?key=your-sdk-key
VITE_HIFLEET_SDK_CSS=https://sdk.hifleet.com/sdk/theme.css

地图性能策略

当前已落地

  1. 按视野过滤
    • 仅更新与当前地图 bounds 相交的航次图层。
  2. 按 zoom 抽稀
    • zoom 越低,折线和点位抽稀越强,降低低层级渲染压力。
  3. 单航次增量刷新
    • 实时事件只更新对应航次图层,不再整图重绘。
  4. 覆盖层与底图分离
    • 底图交给 SDK,业务图层交给 Leaflet,减少耦合。

后续生产建议

  1. 按视野过滤
    • 仅渲染当前 bounds 内的重点航次。
  2. 图层分级
    • 航线线、船位、告警、港口分层管理,按需开关。
  3. 更大规模优化
    • 万级以上节点建议评估 Leaflet.CanvasPixiOverlay 或 WebGL 方案。
  4. 更强主题能力
    • 如果地图主题需求继续升级,优先换底图技术栈,而不是继续堆 CSS 滤镜。

大屏响应式方案

  • 已集成 v-scale-screen,由 ScreenAdapter.vue 做统一封装。
  • 当前按 1920x1080 设计稿做等比缩放,并启用 bodyOverflowHidden

自动化测试

当前项目包含以下高价值测试:

  • voyage-realtime.test.ts
    • 校验 WebSocket 消息归一化和增量数据合并。
  • trajectory-optimizer.test.ts
    • 校验长轨迹抽稀、视野过滤和 zoom 级别策略。
  • mock-voyage-stream.test.ts
    • 校验 mock 事件流生成逻辑。
  • useDashboardPreferences.test.ts
    • 校验主题/语言初始化、切换和持久化。
  • MapStage.test.ts
    • 校验地图主题类、头部交互事件和主题同步逻辑。

测试命令:

bash 复制代码
npm run test
npm run test:run

注意:

  • 当前个别 Windows 环境如果本机 localhost 解析异常,Vitest 启动可能被环境阻断。
  • 这类问题通常属于本机网络/hosts 配置,不是测试断言本身失败。

接真实数据时的建议

  1. 航线基础信息接口
    • 返回船名、MMSI、状态、ETA、装载率、预警信息。
  2. 航迹接口
    • 返回轨迹点数组,字段建议包含 latlontimestampspeedlabel
  3. WebSocket 增量推送
    • 仅更新单航次最新点位和状态,不必整页重绘。
  4. 历史轨迹查询接口
    • 首屏加载历史轨迹,实时流只补增量点。
  5. 图层权限分级
    • 如果生产环境涉及天气、风场、港口等能力,建议把 SDK 图层权限和业务开关拆开管理。

运行方式

首次运行请先安装依赖:

bash 复制代码
npm install
npm run dev
npm run test:run

如果内网无法直接访问 npm registry,建议:

  • 配置公司私有镜像源。
  • 或直接把当前源码合入你现有的大屏工程中。

HiFleet 接入说明

HiFleet 文档给出的异步地址本质上会注入一份 CSS 和一份 JS。为了避免 document.write 在异步场景覆盖页面,本 demo 在 hifleet-loader.ts 中改为显式注入这两个资源地址。

当前加载策略支持三种来源:

  • 优先使用 VITE_HIFLEET_SDK_SRCVITE_HIFLEET_SDK_CSS 指定的自定义资源地址。
  • 如果只提供 VITE_HIFLEET_SDK_KEY,则按 key 拼接官方 SDK 地址。
  • 如果以上都未提供,则回退到仓库里保留的历史公开 CSS/JS 地址。

需要注意的是,当前代码并没有实现 SDK 加载失败后自动降级到本地纯 Leaflet 底图。如果 SDK 脚本不可达,页面会进入地图初始化失败状态,并显示相应错误提示。因此在生产环境中,建议优先通过环境变量固定 SDK 资源来源,避免运行时依赖不稳定的公共地址。

后续可扩展项

  • 接入真实船位刷新与轨迹回放。
  • 增加天气、风场、洋流图层切换。
  • 增加区域筛选、船公司筛选、异常告警联动。
  • 增加右侧详情面板或点击船舶浮层详情。
  • 如果地图主题要求继续上升,评估 Leaflet + 自有瓦片MapLibre GL JS 迁移方案。
相关推荐
大家的林语冰1 小时前
ESLint 近期动态大全,新版本正式发布,antfu 大佬推荐的插件也更新了!
前端·javascript·前端工程化
只会cv的前端攻城狮1 小时前
DSL 领域模型架构设计:消灭 CRUD 重复工作
前端·架构
码事漫谈2 小时前
时序数据库2026盘点:国产数据库如何以“融合多模”走出差异化之路?
前端·后端
道友可好2 小时前
让 AI 自己验收,等于让学生自己批卷
前端·人工智能·后端
yingyima2 小时前
Go 语言正则表达式速查手册:30 分钟掌握核心语法与实战技巧
前端
大蝴蝶博努奇a2 小时前
使用ChatGPT 解决各类代码报错
前端
胡志辉3 小时前
深入浅出 call、apply、bind
前端·javascript·后端
iccb10133 小时前
5年,一个程序员是如何把私有化在线客服系统做到第一名的
前端·后端·github
假如让我当三天老蒯3 小时前
回归基本功:Map/Set 与 WeakMap/WeakSet 的区别
前端·面试