gis-maplibre

基于 MapLibre GL 的 Vue3 GIS 地图组件库
1,303
Install
npm install gis-maplibre
Latest Version:1.0.13
Vue:3
License:Apache-2.0
Last Updated:Sep 14, 2026
Links: npm
Maintainer: mayunhui

GisMapLibre

基于 Vue 3 + MapLibre GL + Naive UI 的企业级 GIS 地图组件库

npm version license


目录

  1. 简介
  2. 功能特性
  3. 技术栈
  4. 项目架构
  5. 环境要求
  6. 安装
  7. 快速开始
  8. Props 属性详解
  9. Events 事件详解
  10. Slots 插槽
  11. 组件暴露方法 (defineExpose)
  12. 类型定义
  13. 完整使用示例
  14. 构建产物说明
  15. 本地开发与调试
  16. npm 发布流程
  17. 贡献指南
  18. 常见问题 (FAQ)
  19. 更新日志
  20. 许可证

简介

gis-maplibre 是一个面向二次封装和业务开发者的 Vue 3 组件库,它封装了 MapLibre GL JS 的核心能力,提供:

  • 开箱即用的地图面板:搜索过滤、图例、图层管理、工具栏、底图切换、测量工具等
  • 声明式图层配置:用 Vue 的响应式数据驱动图层增删改查,无需直接操作 MapLibre API
  • 完整 TypeScript 支持:所有 Props、Events、Exposed Methods 都有完整的类型定义
  • 灵活的响应式布局:内置 n-split 分割面板,支持左右/上下分割 + 自定义插槽
  • 企业级过滤系统:前端 filter 表达式 + 服务端 URL 参数 / SQL 参数化查询双通道

功能特性

类别 功能
🗺️ 数据源 GeoJSON、Vector Tiles、Raster、Raster-DEM、Image
🎨 样式体系 颜色、透明度、边框、线宽、填充轮廓、栅格饱和度/对比度、文字标注样式,全部运行时可变
📐 测量工具 距离测量、面积测量、坐标拾取,支持拖拽编辑节点、多测量列表管理
🔍 智能过滤 多字段条件过滤(前端 filter + 服务端 URL/SQL)、防抖输入、图层级过滤持久化
📊 图层管理器 树形结构、显隐控制、样式调整、拖拽排序、飞行定位、图层删除
🎯 要素交互 Popup 弹窗、框选/点选/Ctrl+Shift 多选
⚡ 动画特效 自定义闪烁图层(requestAnimationFrame)、批量要素闪烁(setFeatureState)
🖼️ 灵活布局 左右分割(gitLeft 插槽)、上下分割(gisFooter 插槽),面板大小/显隐响应式控制
💡 图层导入 拖拽/上传 GeoJSON 文件,自动识别几何类型并配置默认样式
📦 TypeScript 完整的 Props/Events/Exposed 类型定义
🌐 本地化 内置 Naive UI 中文语言包

技术栈

依赖 用途
Vue 3.4+ 组件框架 (Composition API + <script setup>)
Pinia 2.1+ 状态管理(图层状态、搜索条件持久化)
MapLibre GL 4.1+ 地图渲染引擎
Naive UI 2.38+ UI 组件库(按钮、模态框、分割面板、上传等)
Turf.js 6.5+ 空间分析(缓冲区、面积计算、坐标转换)
dayjs 日期处理
@vicons/ionicons5 图标库
Vite 4.5+ 构建工具
TypeScript 5.4 类型系统

环境要求

依赖 版本要求
Node.js >= 18.0.0
Vue >= 3.4.0
Pinia >= 2.1.0(必须安装并注册)
包管理器 pnpm(推荐)/ npm / yarn

安装

# pnpm(推荐)
pnpm install gis-maplibre

# npm
npm install gis-maplibre

# yarn
yarn add gis-maplibre

快速开始

1. 注册 Pinia(必须)

组件内部使用 Pinia 管理图层状态。必须在使用组件前注册 Pinia:

// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
app.use(createPinia())   // ⚠️ 必须先注册 Pinia
app.mount('#app')

2. 注册组件

方式一:全局注册

// main.ts
import GisMapLibre from 'gis-maplibre'
import 'gis-maplibre/dist/style.css'

app.use(GisMapLibre) // 全局注册后可在任何组件中使用 <GisMapLibre>

方式二:按需引入

import { GisMapLibre } from 'gis-maplibre'
import 'gis-maplibre/dist/style.css'

3. 引入样式(必须)

⚠️ 版本 >=1.0.8 后 不再自动注入样式,必须手动引入:

// main.ts 或 App.vue 的 <script setup> 中
import 'gis-maplibre/dist/style.css'

// 或者直接在 CSS 文件中
// @import 'gis-maplibre/dist/style.css';

4. 基础使用

重要:地图容器必须有明确的高度,否则地图无法渲染。

<template>
  <div class="map-container">
    <GisMapLibre
      ref="mapRef"
      :config-state="configState"
      :map-config="mapConfig"
      :base-layers="baseLayers"
      :overlay-layers="overlayLayers"
      :toolbar-data="toolbarData"
      :split-config="splitConfig"
      :add-control-config="addControlConfig"
      @init-complete="onMapInit"
      @toolbar-event="onToolbarEvent"
      @layer-feature-click="onLayerClick"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'

// ⚠️ 注意:当前版本不导出类型,需要用 import type 时需要自己声明类型接口

const mapRef = ref()

// 地图初始化配置(与内置默认值深度合并)
const mapConfig = {
  center: [116.397, 39.908],
  zoom: 10,
  style: {
    glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf'
  }
}

// 组件状态配置
const configState = reactive({
  showLayerManager: true,
  addLayerIsDel: false
})

// 底图配置
const baseLayers = [
  {
    id: 'satellite',
    name: '卫星影像',
    type: 'raster',
    url: 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
    visible: true,
    opacity: 1,
    thumbColor: '#2d5016'
  }
]

// 叠加图层(必须用 ref/reactive,否则动态更新不触发)
const overlayLayers = ref([])

// 工具栏
const toolbarData = [
  { key: 'translation', name: '平移', type: 'translation', isShow: true },
  { key: 'measure', name: '测距', type: 'measure', isShow: true },
  { key: 'area', name: '测面积', type: 'area', isShow: true },
  { key: 'coordinate', name: '坐标拾取', type: 'coordinate', isShow: true }
]

// 布局配置(必须用 reactive)
const splitConfig = reactive({
  split1: { defaultSize: 0, disabled: true, size: undefined, watchProps: ['defaultSize'], min: 0, max: 1 },
  split2: { defaultSize: 1, disabled: true, size: undefined, watchProps: ['defaultSize'], min: 0, max: 1 }
})

// 控件
const addControlConfig = [
  { key: 'NavigationControl', position: 'top-left' },
  { key: 'ScaleControl', position: 'bottom-left' },
  { key: 'SearchControl', icon: 'SearchOutline', position: 'top-right', type: 'icon' },
  { key: 'ToolbarControl', icon: 'GridOutline', position: 'top-right', type: 'icon' },
  { key: 'LayerControl', icon: 'LayersOutline', position: 'top-right', type: 'icon' }
]

const onMapInit = (map: any) => { console.log('地图就绪', map) }
const onToolbarEvent = (e: any) => { console.log('工具事件', e) }
const onLayerClick = (data: any) => { console.log('图层点击', data) }
</script>

<style>
.map-container {
  width: 100%;
  height: 100vh;
  position: relative;
}
</style>

Props 属性详解

mapConfig

  • 类型:Partial<maplibregl.MapOptions>
  • 默认值:与内置默认值深度合并

MapLibre GL 地图初始化配置,会与以下内置默认值合并:

const defaultMapConfig = {
  container: mapId,                    // 自动生成唯一容器 ID
  style: { version: 8, sources: {}, layers: [] },
  center: [109.134065, 39.506786],     // 默认中心(中国中部)
  zoom: 4,
  attributionControl: false            // 不显示 MapLibre 版权控件
}

常用覆盖项:center、zoom、style.glyphs(字体路径)。


configState

  • 类型:ConfigState
  • 默认值:见下方

组件状态配置对象,控制面板显隐、公共过滤、图层删除行为。

interface ConfigState {
  showToolbar?: boolean        // 工具栏初始是否显示(默认 false)
  showLegend?: boolean         // 图例初始是否显示(默认 false)
  showLayerManager?: boolean   // 图层管理器初始是否显示(默认 false)
  showSearchManager?: boolean  // 搜索管理器初始是否显示(默认 false)
  addLayerIsDel?: boolean      // overlayLayers 变化时是否先删除旧图层再重新添加(默认 false)
  publicFilter?: PublicFilterItem[] // 全局公共过滤条件,应用于所有图层
}
publicFilter 单项结构
interface PublicFilterItem {
  field: string            // 过滤属性字段名
  comparator: string       // 比较运算符
  value: any               // 过滤值
  isParameter?: boolean    // 是否为服务端参数过滤(默认 false)
  parameterType?: 'params' | 'sql' // 参数类型:'params'=拼接到 URL、'sql'=生成 SQL WHERE 子句
  defaultFilter?: boolean  // 是否作为默认过滤条件(默认 false)
}

使用示例:

const configState = reactive({
  publicFilter: [
    {
      field: 'keyName',
      comparator: '==',
      value: '值',
      isParameter: true,
      parameterType: 'params'
    }
  ]
})

baseLayers

  • 类型:BaseLayersConfig[]
  • 默认值:[]

底图图层配置数组,用于底图切换(在底图面板中显示为可选项)。

interface BaseLayersConfig {
  id: string          // 唯一标识(必填)
  name: string        // 显示名称
  type: string        // 底图类型,通常为 'raster'
  url: string         // 瓦片地址模板,支持 {x}/{y}/{z} 占位符
  visible: boolean    // 是否默认可见
  opacity: number     // 透明度 0-1
  thumbColor: string  // 底图切换面板中的缩略图颜色
}

示例:

const baseLayers = [
  {
    id: 'satellite',
    name: '卫星影像',
    type: 'raster',
    url: 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
    visible: true,
    opacity: 1,
    thumbColor: '#2c3e50'
  },
  {
    id: 'streets',
    name: '街道地图',
    type: 'raster',
    url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
    visible: false,
    opacity: 1,
    thumbColor: '#f39c12'
  }
]

overlayLayers

  • 类型:LayerConfig[]
  • 默认值:[]

叠加图层配置数组,这是核心配置。必须使用 ref() 或 reactive() 包装,否则动态增删图层不会响应。

LayerConfig 完整结构
interface LayerConfig {
  // ===== 基本信息 =====
  id: string                    // 图层唯一标识(必填,不可重复)
  name: string                  // 图层显示名称
  type: 'geojson' | 'vector' | 'raster' | 'raster-dem' | 'image'  // 数据源类型(必填)

  // ===== 数据源(按 type 选择) =====
  data?: GeoJSON.GeoJSON        // type='geojson' 时的数据(GeoJSON 对象)
  url?: string                  // type='vector'/'raster'/'raster-dem' 时的数据源 URL/tilejson
  tiles?: string[]              // type='vector'/'raster' 时的瓦片地址数组(优先于 url)
  imageUrl?: string             // type='image' 时的图片 URL
  imageCoordinates?: [          // type='image' 时的图片四角坐标 [左上, 右上, 右下, 左下]
    [number, number],
    [number, number],
    [number, number],
    [number, number]
  ]

  // ===== 图源/样式配置(可选,遵循 MapLibre 规范)=====
  source?: object               // 自定义 MapLibre Source 配置
  sourceLayer?: string          // Vector 瓦片的 source-layer 名称
  layers?: object | object[]    // 自定义图层样式,遵循 MapLibre Layer 规范

  // ===== 显示与交互 =====
  visible: boolean              // 是否可见
  isClick?: boolean             // 是否响应点击(触发 layer-feature-click 事件)
  autoFly?: boolean             // 加载后是否自动飞行到图层范围(默认 false)
  deletable?: boolean           // 是否允许用户在图层管理器中删除(默认 true)

  // ===== 样式相关 =====
  shape?: 'circle' | 'square' | 'triangle' | 'diamond' | 'line' | 'polygon' | 'raster'
                                // 图层形状,用于图例显示和样式分支
  sourceType?: 'point' | 'line' | 'polygon' | 'raster'
                                // 数据源空间类型,优先级高于 shape

  // ===== 层级 =====
  zIndex?: number               // 渲染层级,数值越大越靠上
  children?: LayerConfig[]      // 子图层列表(树形结构)

  // ===== 过滤 =====
  defaultFilter?: FilterItem[]  // 初始过滤条件
  filterFields?: FilterFieldConfig[] // 搜索面板的过滤字段配置

  // ===== 图标 =====
  icon?: string                 // 自定义图标路径/URL
  iconName?: string             // 图标名称(添加到 MapLibre image sprite)
  iconColor?: string            // 图标颜色

  // ===== 高亮 =====
  highlight?: HighlightConfig   // hover 高亮配置

  // ===== 内部标记 =====
  isImported?: boolean          // 是否为导入图层(通常不需手动设置)
}
FilterItem 结构
interface FilterItem {
  field: string                 // 属性字段名
  comparator: string            // 比较运算符(同 publicFilter)
  value: any                    // 过滤值
  isDisabledDelete?: boolean    // 是否禁止用户删除该过滤条件(默认 false)
  isParameter?: boolean         // 是否为服务端参数过滤(默认 false)
  parameterType?: 'params' | 'sql' // 参数类型
  isLayerAttr?: boolean         // 是否为图层属性过滤(默认 false)
}
FilterFieldConfig 结构
interface FilterFieldConfig {
  label: string                 // 显示标签
  value: string                 // 数据中的属性字段名
  type: 'input' | 'select' | 'number' | 'date'  // 表单控件类型
  dateType?: DatePickerType     // type='date' 时的 NDatePicker 类型
  transformation?: TransformFn  // 过滤转换函数(拼接条件前执行)
  valueAssignmentTransFormation?: TransformFn // 赋值转换函数(回填前执行)
  options?: any[]               // type='select' 时的下拉选项
  isParameter?: boolean         // 是否为服务端参数(URL/SQL)
  parameterType?: 'params' | 'sql'  // 参数类型
  isLayerAttr?: boolean         // 是否为图层属性过滤(默认 false)
  isDisabledDelete?: boolean    // 是否禁止用户删除该字段(默认 false)
  time?: number                 // 输入防抖延迟(ms),仅 isLayerAttr=true 时生效
}
dataConfig.ts 中的内置控件列表(供参考):
key 控件名称 来源 用途
NavigationControl 导航控件 MapLibre 原生 缩放、旋转
FullscreenControl 全屏控件 MapLibre 原生 全屏切换
ScaleControl 比例尺 MapLibre 原生 比例尺显示
TerrainControl 地形控件 MapLibre 原生 地形开关
SearchControl 搜索 系统控件 搜索/过滤面板
ToolbarControl 工具 系统控件 工具栏
LayerControl 图层 系统控件 图层管理器
LegendControl 图例 系统控件 图例面板
MapControl 地图 系统控件 底图切换
CoordinatePickControl 坐标拾取 系统控件 拾取坐标
TranslationControl 平移 系统控件 平移模式
ImportControl 导入 系统控件 GeoJSON 导入
AreaControl 测面 系统控件 面积测量(含子菜单)
RangControl 测距 系统控件 距离测量(含子菜单)
selectElements 选择 系统控件 要素选择(含子菜单)

关键行为说明:

key 行为
SearchControl 打开/关闭搜索过滤面板
ToolbarControl 打开/关闭工具栏
LayerControl 打开/关闭图层管理器
LegendControl 打开/关闭图例面板
MapControl 打开/关闭底图切换面板
CoordinatePickControl 进入坐标拾取模式,点击地图获取坐标并复制到剪贴板
AreaControl 带子菜单:开始测量 / 删除
RangControl 带子菜单:开始测量 / 删除
selectElements 带子菜单:开始选择 / 清空选择

Events 事件详解

事件名 参数签名 触发时机
init-complete (map: maplibregl.Map) 地图初始化完成(load 事件后)
toolbar-event (event: ToolbarEvent) 工具栏按钮点击、测量完成、坐标拾取、要素选择变更等
map-event (event: { type, data }) 统一定义的地图事件(图层导入/删除等)
layer-feature-click (dataInfo: Record<string, { feature, properties, event }>) isClick: true 的图层被点击(300ms 防抖)
control-click (event: { key: string }) 自定义控件按钮被点击
layer-delete (layerId: string) 图层被删除
overlay-layers-reorder (layers: LayerConfig[]) 叠加图层顺序被重排
deleted-event (event: any) 测量结果被删除
dragged-event (event: any) 测量节点被拖拽
search-layer-filter-data (layerId: string, filterData: any) 搜索组件的过滤条件变化

toolbar-event 详情

toolbar-event 的 event 对象结构取决于操作类型:

// 普通按钮点击
{ key: 'myCustomButton', name: '自定义按钮', type: 'button', action: 'click' }

// 测距完成
{
  key: 'measure', name: '测距', type: 'measure',
  data: {
    id: string, type: 'distance',
    distance: number, unit: string,  // unit 如 'km'、'm'
    points: number[][], coordinates: number[][]
  }
}

// 测面积完成
{
  key: 'area', name: '测面积', type: 'area',
  data: {
    id: string, type: 'area',
    area: number, unit: string,       // unit 如 'km²'、'm²'
    points: number[][], coordinates: number[][]
  }
}

// 坐标拾取
{
  key: 'coordinate', name: '坐标拾取', type: 'coordinate',
  action: 'end',
  data: { lng: number, lat: number }
}

// 要素选择(实时推送选中结果)
{
  key: 'selectElements', name: '选择要素', type: 'selectElements',
  action: 'update',
  value: Record<string, any[]>  // { [layerId]: [feature1, feature2, ...] }
}

Slots 插槽

插槽名 位置 显示条件 说明
gitLeft 左侧面板 split1.disabled === false 常用于放自定义面板或统计图表
gisHeader 地图上方 始终渲染 可用于自定义工具栏、标题栏等
gisFooter 底部面板 split2.disabled === false 常用于结果面板、分析弹窗等

Props 属性详解(续)

toolbarData

  • 类型:ToolbarButtonConfig[]
  • 默认值:[]

工具栏按钮配置数组,定义地图上方工具栏显示的按钮。

interface ToolbarButtonConfig {
  key: string                   // 唯一标识(内置类型见下表)
  name: string                  // 按钮显示名称
  type?: string                 // 按钮类型:内置工具类型名 | 'button' | 'select'
  isShow?: boolean              // 是否显示在工具栏上
  icon?: string                 // Naive UI 图标名,如 'BarChartOutline'
  iconSize?: number             // 图标大小
  options?: Array<{             // type='select' 时的下拉选项
    label: string
    value: string
    icon?: string
  }>
}

内置工具栏 key 及其行为:

key name 行为
translation 平移 退出所有测量/选择模式,恢复地图拖拽
measure 测距 进入距离测量模式
area 测面积 进入面积测量模式
coordinate 坐标拾取 点击地图获取坐标并复制到剪贴板
layerManager 图层管理 打开图层管理器面板
legend 图例 打开图例面板
importLayer 导入图层 打开 GeoJSON 导入弹窗
selectElements 选择要素 进入要素选择模式

自定义按钮示例:

const toolbarData = [
  { key: 'translation', name: '平移', type: 'translation', isShow: true },
  // 自定义普通按钮(通过 toolbar-event 监听点击)
  { key: 'statisticalAnalysis', name: '统计分析', type: 'button', isShow: true, icon: 'BarChartOutline' },
  // 自定义下拉按钮
  {
    key: 'yearSelect', name: '年度', type: 'select', isShow: true,
    options: [
      { label: '2024年', value: '2024' },
      { label: '2025年', value: '2025' }
    ]
  }
]

splitConfig

  • 类型:SplitConfig
  • 默认值:见下方

布局分割配置,控制左侧面板和底部面板的显隐与大小。必须使用 reactive() 包装。

interface SplitConfig {
  split1: {                     // 左右分割(左侧面板 = gitLeft 插槽)
    defaultSize: number | string  // 默认大小(0-1 为比例,'200px' 为固定像素)
    disabled: boolean             // true=隐藏面板,false=显示
    size?: number | string        // 受控大小(通常用 defaultSize 即可)
    watchProps: string[]          // Naive UI n-split 监听属性,固定 ['defaultSize']
    min: number                   // 最小值(0-1 比例)
    max: number                   // 最大值(0-1 比例)
  }
  split2: {                     // 上下分割(底部面板 = gisFooter 插槽)
    defaultSize: number | string
    disabled: boolean
    size?: number | string
    watchProps: string[]
    min: number
    max: number
  }
}

动态控制示例:

const splitConfig = reactive({
  split1: { defaultSize: 0, disabled: true, size: undefined, watchProps: ['defaultSize'], min: 0, max: 1 },
  split2: { defaultSize: 1, disabled: true, size: undefined, watchProps: ['defaultSize'], min: 0, max: 1 }
})

// 打开左侧面板
function openLeftPanel() {
  splitConfig.split1.defaultSize = 0.3
  splitConfig.split1.disabled = false
}

// 打开底部面板(40% 高度)
function openFooterPanel() {
  splitConfig.split2.defaultSize = 0.4
  splitConfig.split2.disabled = false
}

// 关闭所有面板
function closeAll() {
  splitConfig.split1.disabled = true
  splitConfig.split2.disabled = true
}

addControlConfig

  • 类型:ControlConfig[] | null
  • 默认值:null

自定义地图控件配置。传 null 时使用内置默认控件组(见 dataConfig.ts)。

interface ControlConfig {
  key: string                   // 控件标识(见控件一览表)
  icon?: string                 // Naive UI 图标名(icon 类型控件)
  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
  type?: string                 // 控件类型:'icon' 等
  title?: string                // 控件提示文字
  size?: number                 // 图标大小
  callBack?: (event: any) => void  // 自定义点击回调(可选)
  children?: ControlConfig[]    // 子控件(嵌套菜单)
}

示例:

const addControlConfig = [
  // MapLibre 原生控件
  { key: 'NavigationControl', position: 'top-left' },
  { key: 'FullscreenControl', position: 'top-left' },
  { key: 'ScaleControl', position: 'bottom-left' },

  // 系统控件
  { key: 'SearchControl', icon: 'SearchOutline', position: 'top-right', type: 'icon' },
  { key: 'LayerControl', icon: 'LayersOutline', position: 'top-right', type: 'icon' },

  // 带子菜单的自定义控件
  {
    key: 'customGroup',
    icon: 'GridOutline',
    position: 'top-right',
    type: 'icon',
    title: '自定义工具组',
    children: [
      { key: 'customAction1', icon: 'PlayOutline', type: 'icon', title: '动作一' },
      { key: 'customAction2', icon: 'TrashOutline', type: 'icon', title: '动作二' }
    ]
  }
]

组件暴露方法 (defineExpose)

通过 ref 获取组件实例后,可调用以下方法和访问以下属性。

const mapRef = ref()
// 然后通过 mapRef.value.xxx 调用

核心实例(只读属性)

属性 类型 说明
map maplibregl.Map MapLibre GL 地图实例
maplibregl typeof maplibregl MapLibre GL 命名空间(用于创建 Popup、Marker 等)
turf typeof turf Turf.js 空间分析工具库
importedLayers Ref<LayerConfig[]> 导入的图层列表(响应式)
unifiedLayers Ref<LayerConfig[]> 统一图层列表(叠加图层 + 导入图层合并,响应式)
measureMode Ref<'distance' | 'area' | 'coordinate' | null> 当前测量模式
measureResult Ref<any> 当前测量结果

图层管理方法

toggleLayerManager()
  • 说明:切换图层管理器面板的显示/隐藏
  • 参数:无
  • 返回值:void
toggleLayer(layerId: string, visible: boolean)
  • 说明:切换单个图层(含其子图层)的可见性
  • 参数:
    • layerId — 图层 ID
    • visible — true 显示,false 隐藏
  • 返回值:void
setLayersVisible(layerIds: string | string[])
  • 说明:设置指定图层的可见性(勾选状态),同步更新图层管理器面板
  • 参数:
    • layerIds — 单个图层 ID 或 ID 数组
  • 返回值:void
allLayersVisibility(layerState: boolean)
  • 说明:批量控制所有图层的勾选状态
  • 参数:
    • layerState — true 全选,false 取消全部
  • 返回值:void
layerChecked(checkedKeys: string[])
  • 说明:批量设置图层勾选状态,未在数组中的图层取消勾选
  • 参数:
    • checkedKeys — 需要勾选的图层 ID 数组
  • 返回值:void
layerStyleChange(layerId: string, style: LayerStyle)
  • 说明:动态修改图层样式,自动路由到正确的 paint/layout 属性
interface LayerStyle {
  color?: string | string[]          // 填充/线条颜色
  borderColor?: string | string[]    // 边框颜色(面图层的 outline)
  borderOpacity?: number | number[]  // 边框透明度
  borderWidth?: number               // 边框宽度
  fillOutlineColor?: string | string[] // 填充描边颜色
  layerOpacity?: number | number[]   // 图层整体透明度
  rasterSaturation?: number          // 栅格饱和度(仅 raster)
  rasterContrast?: number            // 栅格对比度(仅 raster)
  titleColor?: string | string[]     // 文字标注颜色(symbol 图层)
  titleSize?: number                 // 文字标注大小(symbol 图层)
}
  • 参数:
    • layerId — 图层 ID
    • style — 样式配置对象
  • 返回值:void
layerDelete(layerId: string)
  • 说明:从地图和图层列表中删除指定图层,同时移除关联的 source、outline、symbol 图层
  • 参数:
    • layerId — 要删除的图层 ID
  • 返回值:void
layerReorder(layers: LayerConfig[], layerType: 'overlay' | 'imported', checkedKeys?: string[])
  • 说明:对指定类型的图层进行重排
  • 参数:
    • layers — 重排后的图层数组
    • layerType — 'overlay'(叠加图层)或 'imported'(导入图层)
    • checkedKeys — 可选,保持勾选状态的图层 ID 数组
  • 返回值:void
layerLocate(layer: LayerConfig)
  • 说明:飞行定位到指定图层的空间范围。对不同类型图层采用不同策略:
    • GeoJSON:直接使用数据坐标计算边界
    • Vector:回到初始视口,等 idle 后查询可见要素并飞到要素范围
    • Raster/Raster-DEM:回到初始视口
    • Image:使用 imageCoordinates 计算边界
  • 参数:
    • layer — 图层配置对象
  • 返回值:void
UnifiedLayersReorder(unifiedLayersList: LayerConfig[], checkedKeys?: string[])
  • 说明:统一图层重排(叠加图层和导入图层的统一排序),重新计算所有图层 zIndex 并移动到正确位置
  • 参数:
    • unifiedLayersList — 重排后的统一图层列表
    • checkedKeys — 保持勾选的图层 ID 数组
  • 返回值:void

过滤与搜索方法

searchLayerFilter(layerId: string, filter: any, urlParams: any, layer?: object, config?: object)
  • 说明:对指定图层应用过滤条件,自动判断数据源类型:
    • GeoJSON 源:直接设置 filter 表达式
    • Vector 源:如果有 URL 参数或 SQL,重建 source 和 layer;否则直接设置 filter
    • Raster/Raster-DEM/Image:不支持过滤,弹出警告
  • 参数:
    • layerId — 目标图层 ID
    • filter — MapLibre filter 表达式,如 ['==', ['get', 'year'], '2024']
    • urlParams — URL 参数对象,结构为 { params: string, sql: string }
    • layer — 可选,图层配置对象
    • config — 可选,额外配置:
      • config.layerManagerSearch — 是否在图层管理器中标记当前选中图层
      • config.layerSearch — 是否回填图层管理器的搜索表单
  • 返回值:void
layersFilterSet(layers: Array<{layerId: string, filters: any}>)
  • 说明:批量设置多个图层的过滤条件
  • 参数:
    • layers — 图层过滤配置数组,每项包含 layerId 和对应的过滤条件
  • 返回值:void
setLayerConditionsStore(layerId: string, customConditions: any)
  • 说明:持久化存储图层的过滤条件(用于搜索面板状态保持)
  • 参数:
    • layerId — 图层 ID
    • customConditions — 自定义过滤条件数据
  • 返回值:void
getLayerConditionsStore(layerId: string)
  • 说明:获取已存储的图层过滤条件
  • 参数:
    • layerId — 图层 ID
  • 返回值:any — 该图层的过滤条件数据
searchMapliberParameter(filterData: FilterItem[])
  • 说明:将过滤条件数组转换为 MapLibre GL filter 表达式(前端过滤用)
  • 参数:
    • filterData — 过滤条件数组
  • 返回值:any[] — MapLibre filter 表达式数组
searchUrlParamsOrSql(filterData: FilterItem[])
  • 说明:将参数化过滤条件按 parameterType 拆分为 URL 参数和 SQL WHERE 子句
  • 参数:
    • filterData — 过滤条件数组(isParameter: true 的项)
  • 返回值:{ params: string, sql: string }

动画与特效方法

flyToLayer(data: GeoJSON.FeatureCollection | GeoJSON.Feature, fitBoundsOptions?: object)
  • 说明:计算 GeoJSON 数据的边界并飞行定位
  • 参数:
    • data — GeoJSON 数据
    • fitBoundsOptions — 可选,fitBounds 配置(padding、maxZoom、duration 等)
  • 默认值:padding=50, maxZoom=20, duration=1000ms
  • 返回值:void
createCustomBlinkLayer(sourceId: string, config: CustomBlinkLayerType)
  • 说明:创建一个独立的闪烁图层(使用 requestAnimationFrame),常用于临时高亮
  • 参数:
    • sourceId — 自定义数据源/图层 ID
    • config — 闪烁图层配置
interface CustomBlinkLayerType {
  source: {                    // 同 MapLibre source 规范
    type: string
    data?: { type: string, features: any[] }
    [key: string]: any
  }
  layer: {                     // 同 MapLibre layer 规范
    type: string               // 图层类型:circle、line、fill 等
    layout?: any
    paint?: any
  }
  blink: {
    layerKey: 'paint' | 'layout'  // 要改变的属性类型
    key: string                   // 属性 key,如 'circle-opacity'、'line-width'
    frequency?: number            // 闪烁频率(Hz),默认 1
    minValue: number | string     // 属性最小值
    maxValue: number | string     // 属性最大值
    often?: number                // 持续时长(毫秒),到期后自动停止并移除图层
  }
}
  • 返回值:{ stop: () => void } — 控制对象,调用 stop() 停止动画并移除图层

示例:

const blinkCtrl = mapRef.value.createCustomBlinkLayer('temp-blink', {
  source: { type: 'geojson', data: featureCollection },
  layer: { type: 'circle', paint: { 'circle-radius': 10, 'circle-color': '#ff0000' } },
  blink: {
    layerKey: 'paint',
    key: 'circle-opacity',
    frequency: 2,          // 每秒闪烁 2 次
    minValue: 0.2,
    maxValue: 1,
    often: 5000            // 5 秒后自动停止并移除
  }
})
// 手动停止:blinkCtrl.stop()
startBatchBlink(targets: BlinkTarget[], options?: BlinkOptions, features?: any)
  • 说明:对多个图层的指定要素进行批量闪烁(使用 setFeatureState 修改 isFlashing 状态)
  • 注意:闪烁依赖图层的 promoteId 配置或 setFeatureState 的 source/id/sourceLayer 三元组
interface BlinkTarget {
  layerId: string                  // 图层 ID
  featureIds: (string | number)[]  // 要素 ID 数组
  attrKey: string                  // 用于匹配要素的属性 key
}

interface BlinkOptions {
  duration?: number      // 单次闪烁时长(毫秒),默认 500
  blinkTimes?: number    // 闪烁次数,Infinity=无限闪烁,默认 Infinity
  flyTo?: boolean        // 是否自动飞行到要素范围,默认 true
  visibleRange?: number  // 可视范围边距(像素),要素在视口内时不飞行,超出才飞行
}
  • 参数:
    • targets — 批量闪烁目标数组
    • options — 闪烁选项
    • features — 可选,预传入的要素数据(避免从地图查询)
  • 返回值:void
getFeatureGeometryById(layerId: string, key: string, featureId: any)
  • 说明:从图层中查找指定 ID 的要素几何对象
  • 查找策略依次为:
    1. GeoJSON 源的 getData()
    2. querySourceFeatures(支持多 sourceLayer)
    3. queryRenderedFeatures(当前视口内)
  • 参数:
    • layerId — 图层 ID
    • key — 用于匹配要素的 property key
    • featureId — 要查找的要素 ID 值
  • 返回值:Feature | null
stopBatchBlink()
  • 说明:停止所有批量闪烁动画,清除所有要素的 isFlashing 状态
  • 参数:无
  • 返回值:void

控件方法

addControl(controlData: ControlConfig[])
  • 说明:动态添加自定义控件到地图
  • 参数:
    • controlData — 控件配置数组(同 addControlConfig prop 格式)
  • 返回值:void
// 动态添加控件示例
mapRef.value.addControl([
  {
    key: 'myDynamicBtn',
    icon: 'StarOutline',
    position: 'top-right',
    type: 'icon',
    title: '动态按钮',
    callBack: (event: any) => {
      console.log('动态按钮被点击', event)
    }
  }
])

类型定义

LayerConfig 完整类型

详见 overlayLayers 章节的 LayerConfig 完整结构。

FilterFieldConfig 完整类型

type DatePickerType =
  | 'date' | 'month' | 'year' | 'daterange' | 'monthrange' | 'yearrange'

type TransformFn = (key: string, value: any) => any

interface FilterFieldConfig {
  label: string
  value: string
  type: 'input' | 'select' | 'number' | 'date'
  dateType?: DatePickerType          // type='date' 时生效
  transformation?: TransformFn       // 过滤值转换(拼接前)
  valueAssignmentTransFormation?: TransformFn  // 赋值转换(回填前)
  options?: any[]                    // type='select' 的下拉选项
  isParameter?: boolean              // 是否走服务端过滤
  parameterType?: 'params' | 'sql'   // 参数类型
  isLayerAttr?: boolean              // 是否为图层属性过滤
  isDisabledDelete?: boolean         // 是否禁止用户删除该字段
  time?: number                      // 防抖延迟 (ms)
}

BaseLayersConfig 完整类型

interface BaseLayersConfig {
  id: string
  name: string
  type: string
  url: string
  visible: boolean
  opacity: number
  thumbColor: string
}

SplitConfig 完整类型

interface SplitConfig {
  split1: {
    defaultSize: number | string
    disabled: boolean
    size?: number | string
    watchProps: string[]
    min: number
    max: number
  }
  split2: {
    defaultSize: number | string
    disabled: boolean
    size?: number | string
    watchProps: string[]
    min: number
    max: number
  }
}

事件类型

type MapEventType =
  | 'toolbar-click'        // 工具栏按钮点击
  | 'measure-complete'     // 测量绘制完成
  | 'area-complete'        // 面积绘制完成
  | 'measure-dragged'      // 测量拖动完成
  | 'area-dragged'         // 面积拖动完成
  | 'measure-deleted'      // 测量删除
  | 'coordinate-pick'      // 坐标拾取
  | 'analysis-click'       // 分析按钮点击
  | 'reserve-click'        // 储量按钮点击
  | 'layer-imported'       // 图层导入完成
  | 'layer-deleted'        // 图层删除

interface MapEvent {
  type: MapEventType
  data: MapEventData
}

// 各事件数据类型
interface ToolbarClickData { key: string; name: string; action?: string; value?: string }
interface MeasureCompleteData { id: string; type: 'distance'; distance: number; unit: string; points: number[][]; coordinates: number[][] }
interface AreaCompleteData { id: string; type: 'area'; area: number; unit: string; points: number[][]; coordinates: number[][] }
interface MeasureDraggedData { id: string; type: 'distance'; distance: number; unit: string; points: number[][]; coordinates: number[][] }
interface AreaDraggedData { id: string; type: 'area'; area: number; unit: string; points: number[][]; coordinates: number[][] }
interface MeasureDeletedData { id: string; type: 'distance' | 'area'; points: number[][] }
interface CoordinatePickData { lng: number; lat: number }
interface AnalysisClickData { timestamp: number }
interface ReserveClickData { timestamp: number }
interface LayerImportedData { layerId: string; layerName: string; fileName: string; featureCount: number; geojson: GeoJSON.FeatureCollection }
interface LayerDeletedData { layerId: string; layerName: string }

Blink 相关类型

interface CustomBlinkLayerType {
  source: { type: string; data?: { type: string; features: any[] }; [key: string]: any }
  layer: { type: string; layout?: any; paint?: any }
  blink: {
    layerKey: 'paint' | 'layout'
    key: string
    frequency?: number     // 闪烁频率 (Hz),默认 1
    minValue: number | string
    maxValue: number | string
    often?: number         // 持续时长 (ms),到期自动停止
  }
}

interface BlinkTarget {
  layerId: string
  featureIds: (string | number)[]
  attrKey: string
}

interface BlinkOptions {
  duration?: number       // 单次闪烁时长 (ms),默认 500
  blinkTimes?: number     // 闪烁次数,Infinity=无限,默认 Infinity
  flyTo?: boolean         // 是否自动飞行到要素范围,默认 true
  visibleRange?: number   // 可视范围边距 (px)
}

HighlightConfig

interface HighlightConfig {
  color?: string        // 线→线颜色 / 面→边框颜色 / 点→描边颜色
  width?: number        // 线→线宽 / 面→边框宽 / 点→描边宽
  fillColor?: string    // 面→填充颜色
  pointColor?: string   // 点→填充颜色
  opacity?: number      // 可选透明度
}

完整使用示例

示例一:GeoJSON 点图层 + 过滤 + 点击弹窗

<template>
  <div class="map-wrap">
    <GisMapLibre
      ref="mapRef"
      :config-state="configState"
      :map-config="mapConfig"
      :base-layers="baseLayers"
      :overlay-layers="overlayLayers"
      :toolbar-data="toolbarData"
      :split-config="splitConfig"
      :add-control-config="controls"
      @layer-feature-click="onLayerClick"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'

const mapRef = ref()

const configState = reactive({
  addLayerIsDel: false,
  showLayerManager: true
})

const mapConfig = {
  center: [116.397, 39.908],
  zoom: 10
}

const baseLayers = [
  { id: 'osm', name: 'OpenStreetMap', type: 'raster',
    url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
    visible: true, opacity: 1, thumbColor: '#f0f0f0' }
]

const overlayLayers = ref([
  {
    id: 'sites',
    name: '站点分布',
    type: 'geojson',
    shape: 'circle',
    visible: true,
    isClick: true,
    autoFly: true,
    data: {
      type: 'FeatureCollection',
      features: [
        { type: 'Feature',
          properties: { name: '站点A', year: '2024', status: 'active' },
          geometry: { type: 'Point', coordinates: [116.38, 39.90] } },
        { type: 'Feature',
          properties: { name: '站点B', year: '2023', status: 'inactive' },
          geometry: { type: 'Point', coordinates: [116.42, 39.92] } }
      ]
    },
    layers: [
      { type: 'circle', layerType: 'circle', layout: {},
        paint: {
          'circle-radius': 8,
          'circle-color': '#1890ff',
          'circle-opacity': 0.8,
          'circle-stroke-width': 2,
          'circle-stroke-color': '#fff'
        } }
    ],
    filterFields: [
      { label: '年度', value: 'year', type: 'select', options: ['2024', '2023'] },
      { label: '状态', value: 'status', type: 'select', options: ['active', 'inactive'] },
      { label: '名称', value: 'name', type: 'input', isLayerAttr: true, time: 500 }
    ]
  }
])

const toolbarData = [
  { key: 'translation', name: '平移', type: 'translation', isShow: true },
  { key: 'measure', name: '测距', type: 'measure', isShow: true }
]

const splitConfig = reactive({
  split1: { defaultSize: 0, disabled: true, size: undefined, min: 0, max: 1, watchProps: ['defaultSize'] },
  split2: { defaultSize: 1, disabled: true, size: undefined, min: 0, max: 1, watchProps: ['defaultSize'] }
})

const controls = [
  { key: 'NavigationControl', position: 'top-left' },
  { key: 'ScaleControl', position: 'bottom-left' },
  { key: 'SearchControl', icon: 'SearchOutline', position: 'top-right', type: 'icon' },
  { key: 'LayerControl', icon: 'LayersOutline', position: 'top-right', type: 'icon' }
]

const onLayerClick = (dataInfo: any) => {
  const layerIds = Object.keys(dataInfo)
  layerIds.forEach(id => {
    const { properties } = dataInfo[id]
    console.log(`点击了图层 ${id},属性:`, properties)
  })
}
</script>

<style scoped>
.map-wrap { width: 100%; height: 100vh; }
</style>

示例二:动态增删图层

import { ref } from 'vue'

const overlayLayers = ref<any[]>([])
const mapRef = ref()

// 添加 GeoJSON 面图层
function addPolygonLayer() {
  const layerId = `polygon-${Date.now()}`
  overlayLayers.value.push({
    id: layerId,
    name: '新多边形图层',
    type: 'geojson',
    shape: 'polygon',
    visible: true,
    isClick: true,
    deletable: true,
    autoFly: true,
    data: {
      type: 'FeatureCollection',
      features: [{
        type: 'Feature',
        properties: {},
        geometry: {
          type: 'Polygon',
          coordinates: [[
            [116.25, 40.05], [116.55, 40.05],
            [116.55, 39.75], [116.25, 39.75],
            [116.25, 40.05]
          ]]
        }
      }]
    },
    layers: [
      { type: 'fill', layerType: 'fill', layout: {},
        paint: { 'fill-color': '#52c41a', 'fill-opacity': 0.6, 'fill-outline-color': '#52c41a' } },
      { type: 'line', layerType: 'outline', layout: {},
        paint: { 'line-color': '#237804', 'line-width': 2, 'line-opacity': 1 } }
    ]
  })
}

// 通过 ref 调用方法删除图层
function removeLayerById(id: string) {
  mapRef.value?.layerDelete(id)
}

// 修改图层样式
function changeLayerColor(layerId: string, color: string) {
  mapRef.value?.layerStyleChange(layerId, {
    color,
    layerOpacity: 0.7
  })
}

// 定位到图层
function locateToLayer(layer: any) {
  mapRef.value?.layerLocate(layer)
}

// 切换图层可见性
function toggleById(layerId: string) {
  mapRef.value?.toggleLayer(layerId, false)
}

示例三:矢量瓦片图层 + 服务端过滤

const overlayLayers = ref([
  {
    id: 'vector-buildings',
    name: '建筑矢量瓦片',
    type: 'vector',
    shape: 'polygon',
    visible: true,
    tiles: ['https://api.example.com/tiles/{z}/{x}/{y}.pbf'],
    sourceLayer: 'buildings',
    layers: [
      { type: 'fill', layerType: 'fill', layout: {},
        paint: { 'fill-color': '#ff7a45', 'fill-opacity': 0.7 } },
      { type: 'line', layerType: 'outline', layout: {},
        paint: { 'line-color': '#d4380d', 'line-width': 1 } }
    ],
    // 过滤字段配置:服务端 SQL 过滤
    filterFields: [
      {
        label: '建筑年份',
        value: 'build_year',
        type: 'select',
        options: [
          { label: '2020年', value: '2020' },
          { label: '2021年', value: '2021' }
        ],
        isParameter: true,       // 走服务端过滤
        parameterType: 'sql'     // 生成 SQL WHERE 条件
      },
      {
        label: '建筑名称',
        value: 'name',
        type: 'input',
        isParameter: true,
        parameterType: 'params'  // 拼接到 URL 参数
      }
    ],
    // 默认过滤
    defaultFilter: [
      { field: 'build_year', comparator: '==', value: '2021', isDisabledDelete: true }
    ]
  }
])

示例四:自定义闪烁动画

const mapRef = ref()

function startBlink() {
  if (!mapRef.value) return

  const blinkConfig = {
    source: {
      type: 'geojson',
      data: {
        type: 'FeatureCollection',
        features: [{
          type: 'Feature',
          properties: {},
          geometry: {
            type: 'Polygon',
            coordinates: [[
              [116.35, 39.95], [116.45, 39.95],
              [116.45, 39.88], [116.35, 39.88],
              [116.35, 39.95]
            ]]
          }
        }]
      }
    },
    layer: {
      type: 'fill',
      layout: {},
      paint: {
        'fill-color': '#ff0000',
        'fill-opacity': 0.8
      }
    },
    blink: {
      layerKey: 'paint',
      key: 'fill-opacity',
      frequency: 3,         // 每秒 3 次
      minValue: 0.2,
      maxValue: 0.9,
      often: 5000           // 5 秒后自动停止
    }
  }

  const ctrl = mapRef.value.createCustomBlinkLayer('alarm-1', blinkConfig)

  // 可提前手动停止
  // ctrl.stop()
}

示例五:批量要素闪烁(查询结果高亮)

const mapRef = ref()

function highlightSearchResults() {
  if (!mapRef.value) return

  mapRef.value.startBatchBlink(
    [
      {
        layerId: 'explorationRight',
        featureIds: ['id1', 'id2', 'id3'],
        attrKey: 'dsid'
      },
      {
        layerId: 'miningRight',
        featureIds: ['id4'],
        attrKey: 'dsid'
      }
    ],
    {
      duration: 1000,    // 每次闪烁 1 秒
      blinkTimes: 5,     // 闪烁 5 次后自动停止
      flyTo: true,       // 先飞行到要素范围
      visibleRange: 100  // 如果要素已在视口 100px 内则不飞行
    }
  )
}

function stopAllBlink() {
  mapRef.value?.stopBatchBlink()
}

示例六:插入自定义面板(插槽)

<template>
  <div class="map-wrap">
    <GisMapLibre
      ref="mapRef"
      :config-state="configState"
      :map-config="mapConfig"
      :split-config="splitConfig"
      @init-complete="onMapInit"
    >
      <!-- 左侧面板:自定义图层管理器 -->
      <template #gitLeft>
        <div class="custom-left-panel">
          <h3>自定义面板</h3>
          <p>这里可以放统计、图表等</p>
        </div>
      </template>

      <!-- 底部面板:分析结果 -->
      <template #gisFooter>
        <div class="custom-footer-panel">
          <h3>分析结果</h3>
        </div>
      </template>
    </GisMapLibre>
  </div>
</template>

<script setup lang="ts">
const splitConfig = reactive({
  split1: { defaultSize: 0.3, disabled: false, size: undefined, watchProps: ['defaultSize'], min: 0, max: 1 },
  split2: { defaultSize: 0.4, disabled: false, size: undefined, watchProps: ['defaultSize'], min: 0, max: 1 }
})

const onMapInit = (map: any) => {
  console.log('地图就绪,可开始业务逻辑')
}
</script>

构建流程

  1. Vite 库模式构建 (vite build --config vite.config.lib.ts):产出 es + umd 两种格式
  2. TypeScript 类型生成 (vue-tsc --project tsconfig.lib.json):生成 .d.ts 类型声明
  3. 代码混淆:通过 vite-plugin-javascript-obfuscator 进行字符串加密与标识符混淆
  4. 压缩:通过 terser 进行代码压缩,移除所有 console.* 调用

本地开发与调试

开发目录说明

  • packages/ — 组件库源码。开发时通过 @/ 别名引用(使用 vite.config.lib.ts 中的 alias 配置)
  • src/ — 开发预览目录。src/main.ts 是开发入口,src/components/gis/index.vue 是主要的演示页面
  • 修改组件 — 修改 packages/gis-mapliber/ 中的文件后,热更新即可看到效果

贡献指南

代码规范

  • 语言:TypeScript + Vue 3 Composition API(<script setup>)
  • 包管理器:pnpm
  • 构建:Vite 库模式,入口为 packages/index.ts
  • 类型:所有新增的 Props、Events、Exposed Methods 必须在类型文件中声明
  • 样式:使用 Less 编写,打包后产出 dist/style.css
feat: 新增图层批量闪烁功能
fix: 修复图层删除后 source 未清除的问题
docs: 更新 README.md 组件方法文档
refactor: 重构 useMeasurement composable
chore: 升级 vite 到 5.x

常见问题 (FAQ)

Q1: 地图不显示,一片空白?

A: 检查以下几点:

  1. 地图容器是否有明确的高度(如 height: 100vh 或 height: 500px)
  2. 是否引入了样式 import 'gis-maplibre/dist/style.css'(v1.0.8+ 必须手动引入)
  3. 浏览器控制台是否有报错

Q2: overlayLayers 动态 push 后不生效?

A: overlayLayers 必须用 ref() 或 reactive() 包装:

// ✅ 正确
const overlayLayers = ref([])

// ❌ 错误(不会触发响应)
let overlayLayers = []

Q3: splitConfig 修改 disabled 无效?

A: splitConfig 必须用 reactive() 包装:

// ✅ 正确
const splitConfig = reactive({ split1: { disabled: true }, split2: { disabled: true } })

// ❌ 错误
const splitConfig = { split1: { disabled: true }, split2: { disabled: true } }

Q4: 控制台提示 "Pinia is not installed"?

A: 确保在使用 GisMapLibre 之前注册了 Pinia:

// main.ts
import { createPinia } from 'pinia'
app.use(createPinia())  // 必须在 app.use(GisMapLibre) 之前

Q5: 矢量瓦片图层如何配置 source-layer?

A: 在 layers 数组的每个样式对象中指定 'source-layer':

{
  type: 'vector',
  tiles: ['https://example.com/tiles/{z}/{x}/{y}.pbf'],
  layers: [
    {
      type: 'fill',
      layerType: 'fill',
      'source-layer': 'buildings',  // 矢量瓦片中的图层名
      paint: { 'fill-color': '#ff0000' }
    }
  ]
}

Q6: 闪烁动画不工作?

A:

  • createCustomBlinkLayer — 检查 source.type、layer.type 是否正确
  • startBatchBlink — 确保图层配置中有 promoteId 或使用了正确的 source/sourceLayer/id 三元组;检查 attrKey 的值是否与要素属性中的键对应

Q7: 如何获取地图实例来做自定义操作?

A: 通过 ref 获取组件实例,然后访问 .map 属性:

const mapRef = ref()
// 在 @init-complete 回调中或之后的任意时刻
const mapInstance = mapRef.value?.map
mapInstance?.addSource(...)
mapInstance?.addLayer(...)

也可以直接使用暴露的 maplibregl 和 turf 属性。

Q8: 服务端过滤(isParameter)的工作原理?

A: 当用户修改过滤面板的值时,组件会:

  1. 收集所有 isParameter: true 的过滤条件
  2. 根据 parameterType 拆分为 URL 参数(?field=value)和 SQL WHERE 子句
  3. 将结果通过 search-layer-filter-data 事件传递给父组件
  4. 父组件负责将参数拼接到实际的瓦片请求 URL 中

Q9: addLayerIsDel 的作用?

A: 当设置为 true 时,每次 overlayLayers 变化(增/删/改),组件会先删除地图上所有已有图层再重新添加。默认 false(增量更新)。如果你的数据源 URL 不变但需要重新加载数据,可设为 true。

Q10: 如何自定义 Naive UI 主题?

A: 在最外层包裹 <n-config-provider> 并传入自定义 theme,或修改 gis-maplibre/dist/style.css 中的 CSS 变量。组件内部的 config-provider 自带 zhCN 和 dateZhCN 本地化配置。


更新日志

v1.0.10

  • 稳定版本

v1.0.8

  • 样式不再自动注入,需手动引入 import 'gis-maplibre/dist/style.css'

v1.0.0

  • 初始发布:基于 MapLibre GL + Vue 3 + Naive UI 的企业级 GIS 组件库
  • 支持 GeoJSON、Vector、Raster、Raster-DEM、Image 五种数据源
  • 内置图层管理器、搜索过滤、工具栏、图例、测量工具
  • 批量闪烁和自定义闪烁动画
  • 要素选择(框选/点选/多选)

许可证

Apache-2.0


作者:mayunhui 591878820@qq.com

Related Packages

my-openlayer

my-openlayer 是一个基于 OpenLayers 的现代地图组件库,专为 Web GIS 应用开发者设计...

9,722 9
@opengis/partsmap

partsmap-plugins

9,182
gis-map-szdp

GIS通用组件 - Vue 3 超图版本

6,714
mtk-map

基于 Vue 2 和 MapTalks 的地图组件库

2,551
nuxt-maplibre-gl

Nuxt module for vue3-maplibre-gl — auto-import components, composables, CSS, and...

1,053 6

Version History

Version Released Vue License
1.0.13 ^3.4.0 Apache-2.0
1.0.12 ^3.4.0 Apache-2.0
1.0.11 ^3.4.0 Apache-2.0
1.0.10 ^3.4.0 Apache-2.0
1.0.9 ^3.4.0 Apache-2.0