npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zym-test-zerog/map-test-leaflet

v1.0.9

Published

MFD 2D 地图(Leaflet 实现),与 zgc_ui_2.1.0 同级的独立包

Readme

@zym-test-zerog/map-test-leaflet

Vue 3 二维地图组件包(Leaflet 实现),面向 MFD 场景:底图切换、航线/航点、围栏、备降点、飞机实时位置与测距。

与 @zym-test-zerog/map-test-ol 公开 API 一致,业务侧只需替换包名即可切换引擎。

| 项 | 说明 | | --- | --- | | 引擎 | Leaflet ^1.9.4 | | 框架 | Vue ^3.5.0(peer) | | 包形态 | 源码包(直接导出 .ts / .vue),需 Vite / Vue CLI 等能处理 SFC | | 默认中心 | [31.8206, 117.2273](合肥附近) | | 默认缩放 | 11 | | 缩放范围 | 10 ~ 24(原生瓦片到 18) |


目录

  1. 安装
  2. 工程配置
  3. 快速开始
  4. 组件 ZMap
  5. 生命周期
  6. 坐标与通用数据结构
  7. mapService API
  8. 完整业务示例
  9. 切换 OpenLayers
  10. 导出清单
  11. 注意事项与常见问题

安装

npm i @zym-test-zerog/map-test-leaflet
# 或
pnpm add @zym-test-zerog/map-test-leaflet

Peer 依赖(需自行安装):

npm i vue@^3.5.0

本包会自动安装:

  • leaflet
  • @types/leaflet
  • @makeapie/vue-component-helpers

工程配置

Vite 示例

// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  resolve: {
    // 可选:用别名统一业务导入,方便以后换引擎
    alias: {
      '@zerog/mfd-map': '@zym-test-zerog/map-test-leaflet',
    },
  },
  // 若包被预构建报错,可加入 optimizeDeps
  optimizeDeps: {
    include: ['leaflet'],
  },
});

业务代码可写成:

import { ZMap, mapService } from '@zerog/mfd-map';

容器样式

ZMap 内部使用 position: absolute; inset: 0,父级必须是定位容器且有宽高,例如:

<div style="position: relative; width: 100%; height: 100vh">
  <ZMap @map-ready="onMapReady" />
</div>

Leaflet CSS 已在组件内 import 'leaflet/dist/leaflet.css',一般无需再手动引入。


快速开始

<script setup lang="ts">
import { ref } from 'vue';
import {
  ZMap,
  mapService,
  type MapScaleResult,
} from '@zym-test-zerog/map-test-leaflet';

const scaleText = ref('');

function onMapReady() {
  // 1. 底图:vec 矢量 / img 影像
  mapService.setMapStyle('vec');

  // 2. 比例尺监听(可选)
  mapService.setScaleListener((scale: MapScaleResult) => {
    scaleText.value = scale.text;
  });

  // 3. 初始视野
  mapService.focusOnPoints(
    [{ lat: 31.82, lng: 117.23 }],
    1,   // ratio
    0,   // duration(秒),0 = 立即跳转
    200, // minRadius(米)
    { x: 0, y: 0 },
  );

  // 4. 绘制一条航线
  mapService.initAirworthiness([
    { sid: '1', lat: 31.82, lng: 117.20, attribute: 1 }, // 起飞
    { sid: '2', lat: 31.85, lng: 117.25, attribute: 2 },
    { sid: '3', lat: 31.88, lng: 117.30, attribute: 4 }, // 降落
  ]);
}
</script>

<template>
  <div class="map-page">
    <div class="scale">比例尺:{{ scaleText }}</div>
    <ZMap :page-index="0" @map-ready="onMapReady" />
  </div>
</template>

<style scoped>
.map-page {
  position: relative;
  width: 100%;
  height: 100vh;
}
.scale {
  position: absolute;
  z-index: 10;
  left: 12px;
  bottom: 12px;
  color: #fff;
}
</style>

组件 ZMap

Props

| 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | pageIndex | number | 0 | 业务页索引,透传给上层逻辑;组件本身不据此改行为 |

Events

| 事件名 | 载荷 | 说明 | | --- | --- | --- | | map-ready | 无 | Leaflet Map 已创建,且已执行 mapService.init(map)。此后才能调用 mapService |

行为说明

  1. onMounted 后创建 L.map,配置无缩放控件、无 attribution、preferCanvas 等。
  2. 调用 mapService.init(map),默认底图为影像 img。
  3. 触发 map-ready。
  4. 用 ResizeObserver 监听父容器尺寸,自动 invalidateSize 并刷新比例尺。
  5. onUnmounted 时:mapService.destroy() + map.remove()。

生命周期

挂载 ZMap
  → 创建 Leaflet Map
  → mapService.init(map)   // 挂图层、默认 img 底图、绑定 moveend
  → emit('map-ready')
  → 【业务】setMapStyle / 灌航线 / 围栏 / 备降点 / 飞机点
卸载 ZMap
  → mapService.destroy()
  → map.remove()

规则:

  • 任何 mapService.* 调用必须在 map-ready 之后(或先判断 mapService.viewer)。
  • 页面切换导致组件销毁时,服务会一并清空;重新挂载后需重新灌数据。

坐标与通用数据结构

点坐标(多处通用)

以下写法均被接受:

{ lat: 31.82, lng: 117.23 }
{ latitude: 31.82, longitude: 117.23 }
[117.23, 31.82]   // [lng, lat]

无效坐标(非数字、纬度越界等)会被忽略。

MapScaleResult

interface MapScaleResult {
  value: number;           // 数值,如 500 或 2
  unit: string;            // 'm' | 'km'
  text: string;            // 如 '500m'、'2km'
  calculatedWidth: number; // 建议 UI 条宽度(px)
}

航点 attribute / type

| 值 | 含义 | 地图表现 | | --- | --- | --- | | 1 | 起飞点 | 起飞图标 | | 4 | 降落点 | 降落图标 | | 5 | 起降点 | 起降图标 | | 其他 | 普通航点 | 序号圆点 |

已飞/未飞样式由业务字段(如是否已过)参与渲染;首尾起降点使用专用图标。


mapService API

mapService 为单例。也可使用类 MapService(一般用单例即可)。

就绪判断:

if (!mapService.viewer) return; // Leaflet.Map | undefined

1. 底图与视野

setMapStyle(style: 'vec' | 'img')

| 值 | 说明 | | --- | --- | | vec | 矢量底图 | | img | 影像底图(影像 + 注记叠加) |

默认在 init 时为 img。瓦片地址指向内部服务 *.mapworld.zerogair.com,外网需改源码或自行替换图层。

setZoom(type?: 'in' | 'out')

放大 / 缩小一级(带动画)。

updateSize()

容器尺寸变化后重算画布并刷新比例尺。全屏切换、侧栏收起后建议调用。

setScaleListener(listener?)

mapService.setScaleListener((scale) => {
  // scale.text / scale.calculatedWidth
});
// 取消:
mapService.setScaleListener();

视野变化(moveend)时会自动回调;也可手动 refreshScale()。

focusOnPoints(points, ratio?, duration?, minRadius?, screenOffset?)

将视野对准点集。

| 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | points | 坐标数组 | — | 见上文坐标格式 | | ratio | number | 6 | 相对包围盒放大倍数,越大看得越远;约 10 时图形约占 1/3 | | duration | number | 1 | 动画秒数;0 立即跳转 | | minRadius | number | 1000 | 最小包围半径(米),单点或极小图形时保底视野 | | screenOffset | { x, y } | { x: 200, y: 150 } | 屏幕留白(像素)。正 x/y 向右下留白,负值向左上 |

// 对准航线,左侧留出面板空间
mapService.focusOnPoints(waypoints, 5, 0.8, 800, { x: 100, y: -300 });

// 单点居中、无动画
mapService.focusOnPoints([{ lat, lng }], 1, 0, 200, { x: 0, y: 0 });

clearAllData()

清空航线、围栏预览、编辑围栏、备降点、飞机轨迹与测距等全部业务图层。

destroy(removeFromMap?)

销毁服务状态。一般由 ZMap 卸载时调用,业务侧无需手动调。


2. 航线(适航航点)

典型流程:

// 首次
await mapService.initAirworthiness(waypoints);
// 更新
await mapService.setAirworthinessPositions(waypoints);
await mapService.previewAirworthiness();
// 聚焦
mapService.setFocusWaypointIndex(2); // 业务序号,内部会 -1
// 清空
await mapService.clearAirworthiness();

航点数据示例

type Waypoint = {
  sid?: string | number;   // 唯一标识(也可用 id / no)
  lat: number;
  lng: number;
  alt?: number;
  attribute?: number;      // 1 起飞 / 4 降落 / 5 起降 / 其他普通
  type?: number;           // 与 attribute 二选一
  // ...业务扩展字段可保留,渲染时会合并
};

const waypoints = [
  { sid: 'wp-1', lat: 31.80, lng: 117.20, attribute: 1 },
  { sid: 'wp-2', lat: 31.83, lng: 117.24, attribute: 2 },
  { sid: 'wp-3', lat: 31.86, lng: 117.28, attribute: 4 },
];

| 方法 | 说明 | | --- | --- | | initAirworthiness(positionList?) | 写入航点并渲染 | | setAirworthinessPositions(positions) | 替换航点并渲染 | | setFocusWaypointIndex(index) | 高亮焦点航点;传入业务序号(从 1 起),内部转为 index - 1 | | setAirworthinessPointPosition(position) | 按 sid 更新单点 | | setAirworthinessActivePointSid(sid) | 设置当前激活点 | | setAirworthinessWaypointInfo(info) | 按 sid 合并航点信息 | | delAirworthinessPosition(sid) | 删除航点 | | drawAirworthiness() | 强制重绘 | | previewAirworthiness() | 预览(当前实现为重绘) | | clearAirworthiness() | 清空航线图层 | | destroyAirworthiness() | 同清空 | | setDiffHeight(height) | 占位 API(与 OL 包对齐,Leaflet 侧无效果) |


3. 飞机实时位置与尾迹

// 可选:历史轨迹一次性补齐,避免飞机从起点“飞”一遍
mapService.seedAircraftTrail([
  { lat: 31.80, lng: 117.20 },
  { lat: 31.81, lng: 117.21 },
  { lat: 31.82, lng: 117.22 },
]);

// 实时点(持续推送)
mapService.sendRouteData({
  lat: 31.825,
  lng: 117.225,
  heading: 45,        // 航向角(度),可选;缺失时按位移推算
  // 其它字段可用于弹窗展示
});

// 跟随飞机(type === 'gs' 时开启跟随)
mapService.setCameraOptions({ type: 'gs' });
// 取消跟随
mapService.setCameraOptions({ type: '' });

mapService.destroyRouteManager(); // 清除飞机与尾迹

| 方法 | 说明 | | --- | --- | | initRouteManager(force?) | 初始化尾迹图层;force 为 true 时先销毁再重建 | | seedAircraftTrail(points?) | 批量写入历史轨迹,飞机落在最后一个点 | | sendRouteData(data) | 推送实时点;短间隔时做插值动画,长间隔(>2s)直接跳变 | | setCameraOptions({ type }) | type === 'gs' 时跟随飞机 | | destroyRouteManager() | 移除飞机图标与尾迹 |

尾迹最多保留约 8000 个点(内部降采样)。


4. 备降点

mapService.sendAlternatePointData([
  {
    id: 'alt-1',           // 或 airportId
    lat: 31.90,
    lng: 117.10,
    name: '备降场 A',
    linked: true,          // 关联态影响图标样式
  },
]);

mapService.setAlternatePointLinked('alt-1', false);
mapService.updateAlternatePoint({ id: 'alt-1', lat: 31.91, lng: 117.11, name: '备降场 A' });
mapService.deleteAlternatePoint('alt-1');
mapService.clearAllAlternatePoints();

| 方法 | 说明 | | --- | --- | | sendAlternatePointData(data) | 全量替换备降点(先清空再写入) | | updateAlternatePoint(data) | 新增或更新单个 | | setAlternatePointLinked(id, linked) | 切换关联样式 | | deleteAlternatePoint(id) | 删除 | | clearAllAlternatePoints() | 清空 | | selectAlternatePoint / setAllowHighlightOnClick / setAlternatePointEditMode / setPointDraw / setAlternateName | 占位 API,与 OL 对齐 |


5. 围栏

围栏分两类:

  1. 预览围栏(列表展示):sendFenceData / deleteFence / setFenceLinked
  2. 编辑围栏(当前正在画的一个):drawFence / setPositions / setFenceName / clearPolygon
// 预览多个围栏
mapService.sendFenceData(
  [
    {
      fenceId: 'f-1',        // 或 id
      fenceName: '围栏甲',   // 或 name;showName=false 时不显示名称
      linked: true,
      positions: [           // 或 polygonVertices,至少 3 点
        { lat: 31.80, lng: 117.20 },
        { lat: 31.80, lng: 117.25 },
        { lat: 31.85, lng: 117.25 },
        { lat: 31.85, lng: 117.20 },
      ],
    },
  ],
  true, // showName
);

mapService.setFenceLinked('f-1', false);
mapService.deleteFence('f-1');

// 编辑态:画一个多边形
await mapService.drawFence({
  name: '新建围栏',
  positions: [/* ... */],
});
mapService.setFenceName('重命名');
mapService.setPositions([/* 新顶点 */]);
mapService.clearPolygon(); // 清编辑围栏

| 方法 | 说明 | | --- | --- | | sendFenceData(data, showName?) | 全量同步预览围栏;未出现在列表中的旧 id 会被移除 | | deleteFence(id) | 删除预览围栏 | | setFenceLinked(id, linked) | 关联/未关联颜色(绿 / 灰) | | drawFence(data) | 进入编辑多边形 | | setPositions(positions) | 更新编辑多边形顶点 | | setFenceName(name) | 更新编辑围栏名称标注 | | clearPolygon() | 清除编辑围栏 | | clearFencePositions() | 清空编辑顶点(保留编辑态结构) | | setActivePointSid / setSectorHeight / changeFenceMode / setPointPosition / delFencePosition / setAllowPopup | 占位 API |


6. 测距

mapService.measureWork();
// 左键加点,右键结束当前路径;可画多条
// ...
mapService.clearMeasureWork();

进入测距后:

  • 光标变为十字
  • 双击缩放会被临时禁用
  • 需再调 clearMeasureWork() 退出并清除线条

7. 事件监听

mapService.setEventListener((e, eventName) => {
  console.log(eventName, e);
});

// 取消
mapService.setEventListener();

业务也可主动派发:

mapService.setOnEvent(payload, 'custom-event');

具体事件名依赖业务层约定;备降点等场景常在 composable 里注册监听。


8. 常量

import {
  MAP_MIN_ZOOM,    // 10
  MAP_MAX_ZOOM,    // 24
  MAP_NATIVE_ZOOM, // 18
} from '@zym-test-zerog/map-test-leaflet';

完整业务示例

模拟 MFD 主页面:就绪 → 底图 → 航线 / 围栏 / 备降点 → 实时飞机。

<script setup lang="ts">
import { onUnmounted, ref } from 'vue';
import { ZMap, mapService, type MapScaleResult } from '@zym-test-zerog/map-test-leaflet';

const scale = ref<MapScaleResult | null>(null);
let timer: number | undefined;

function onMapReady() {
  mapService.setMapStyle('img');
  mapService.setScaleListener((s) => {
    scale.value = s;
  });

  const airline = [
    { sid: '1', lat: 31.80, lng: 117.20, attribute: 1 },
    { sid: '2', lat: 31.84, lng: 117.26, attribute: 2 },
    { sid: '3', lat: 31.88, lng: 117.32, attribute: 4 },
  ];
  mapService.initAirworthiness(airline);

  mapService.sendFenceData([
    {
      fenceId: 'fence-1',
      fenceName: '作业区',
      linked: true,
      positions: [
        { lat: 31.81, lng: 117.22 },
        { lat: 31.81, lng: 117.28 },
        { lat: 31.86, lng: 117.28 },
        { lat: 31.86, lng: 117.22 },
      ],
    },
  ]);

  mapService.sendAlternatePointData([
    { id: 'alt-1', lat: 31.90, lng: 117.15, name: '备降 A', linked: true },
  ]);

  mapService.focusOnPoints(airline, 5, 0, 800, { x: 100, y: -200 });

  // 模拟实时飞机
  mapService.initRouteManager();
  let t = 0;
  timer = window.setInterval(() => {
    t += 0.01;
    mapService.sendRouteData({
      lat: 31.80 + t * 0.08,
      lng: 117.20 + t * 0.12,
      heading: 50,
    });
  }, 500);
}

function zoomIn() {
  mapService.setZoom('in');
}
function zoomOut() {
  mapService.setZoom('out');
}
function toggleMeasure(on: boolean) {
  if (on) mapService.measureWork();
  else mapService.clearMeasureWork();
}

onUnmounted(() => {
  if (timer) clearInterval(timer);
});
</script>

<template>
  <div class="wrap">
    <div class="toolbar">
      <button @click="zoomIn">放大</button>
      <button @click="zoomOut">缩小</button>
      <button @click="toggleMeasure(true)">测距</button>
      <button @click="toggleMeasure(false)">清除测距</button>
      <span v-if="scale">{{ scale.text }}</span>
    </div>
    <ZMap @map-ready="onMapReady" />
  </div>
</template>

<style scoped>
.wrap {
  position: relative;
  width: 100%;
  height: 100vh;
}
.toolbar {
  position: absolute;
  z-index: 20;
  top: 12px;
  left: 12px;
  display: flex;
  gap: 8px;
}
</style>

切换 OpenLayers

两个包 API 相同:

npm uninstall @zym-test-zerog/map-test-leaflet
npm i @zym-test-zerog/map-test-ol
- import { ZMap, mapService } from '@zym-test-zerog/map-test-leaflet';
+ import { ZMap, mapService } from '@zym-test-zerog/map-test-ol';

若使用了 Vite alias @zerog/mfd-map,只需改 alias 指向,业务 import 不用动。


导出清单

import {
  // 组件
  ZMap,
  // 默认导出同 ZMap
  default as MapComponent,

  // 服务
  mapService,
  MapService,

  // 组件 props / emits 定义(供二次封装)
  mapProps,
  mapEmits,

  // 常量
  MAP_MIN_ZOOM,
  MAP_MAX_ZOOM,
  MAP_NATIVE_ZOOM,

  // 兼容旧名(已 deprecated)
  leafletMapProps,
  leafletMapEmits,
} from '@zym-test-zerog/map-test-leaflet';

import type {
  MapProps,
  MapEmits,
  MapScaleResult,
  LeafletMapProps,
  LeafletMapEmits,
} from '@zym-test-zerog/map-test-leaflet';

注意事项与常见问题

  1. 必须等 map-ready
    否则 mapService.viewer 为空,调用会被直接 return。

  2. 源码包
    未打包成 JS;消费端需支持 Vue SFC + TypeScript + less(组件内使用了 less)。

  3. 底图域名
    默认瓦片:http://{s}.mapworld.zerogair.com/anhui/hefei/...
    外网或无权限环境底图会空白,需改 mapService.ts 中的 TILE_BASE。

  4. sendAlternatePointData / sendFenceData 是全量同步
    每次调用会按新列表重建;增量更新请用 updateAlternatePoint / setFenceLinked 等。

  5. setFocusWaypointIndex 传业务序号
    传入 1 表示第一个点;内部会减 1。

  6. 占位 API
    部分方法仅为与 OpenLayers 包对齐而存在,Leaflet 实现为空操作(如 setDiffHeight、changeFenceMode、setAllowPopup 等)。调用不会报错,但无视觉效果。

  7. 单例注意
    同一应用内同时挂多个 ZMap 会争用同一个 mapService,不建议并行多实例。

License

ISC