<p align="center">Map Leaflet SDK - A Vue.js 3 library</p>

- 💪 Vue 3 Composition API
- 🔥 Written in TypeScript

## 使用示例

```html
<template>
  <div class="home" style="width: 100%; height: 100%">
    <BaseMapComponent
      ref="baseMap"
      :right-menu="status.rightMenuList"
      @initMap="initMap"
      @selectShipItem="selectShipItem"
      @selectedShipUpdate="selectedShipUpdate"
      @rightMenuShip="rightMenuShip"
      @aiDetectionClick="aiDetectionClick"
    >
      <!-- 提供默认插槽 -->
      <section
        v-show="status.isShowTools"
        class="toolbar"
        @dblclick.stop
        @click.stop
      >
        <el-button @click="shipShow">显示船舶</el-button>
        <el-button :disabled="isDisabled" @click="local">定位船舶</el-button>
        <el-button :disabled="isDisabled" @click="cancelLocate"
          >取消定位船舶</el-button
        >
        <el-button @click="closeRightMenu">关闭右键菜单</el-button>
        <el-button @click="drawLine">绘制航线</el-button>
        <el-button @click="drawLineEdit">编辑航线</el-button>
        <el-button @click="cancleDrawLineEdit">取消编辑</el-button>
        <el-button @click="showTrack">显示历史轨迹</el-button>
        <el-button :disabled="!status.trackId" @click="appendTrack"
          >追加历史轨迹</el-button
        >
        <el-button :disabled="!status.trackId" @click="hideOrShowTrack"
          >隐藏历史轨迹</el-button
        >
        <el-button :disabled="isDisabled" @click="showTrackForecast"
          >显示气象预测轨迹</el-button
        >
        <el-button :disabled="isDisabled" @click="hideTrackForeacast"
          >隐藏气象预测轨迹</el-button
        >
        <el-button @click="showNavigationalWarning">显示航行警告</el-button>
        <el-button @click="hideNavigationalWarning"
          >隐藏第一条航行警告</el-button
        >

        <el-button @click="localPirate">定位海盗</el-button>
        <el-button @click="cancelLocalPirate">取消定位海盗</el-button>
        <el-button @click="showNavigation">显示ais航标</el-button>
        <el-button @click="hideNavigation">隐藏ais航标</el-button>
        <el-button @click="aisNavLocation">定位ais航标</el-button>
        <el-button @click="showShipSj">显示船舶三角</el-button>
        <el-button @click="hideShipSj">隐藏船舶三角</el-button>

        <el-form label-width="auto" class="demo-ruleForm" label-position="top">
          <el-form-item label="图层显示">
            <section style="display: flex; padding: 0 5px; flex-wrap: wrap">
              <section
                v-for="(item, index) in getLayer"
                :key="index"
                style="display: flex"
                flex
                items-center
              >
                <label style="flex: 1" flex-1>{{ item.label }}</label>
                <section>
                  <el-switch
                    v-model="item.value"
                    @change="
                      (val) => handleChange(item.typeName, val, item.typeCode)
                    "
                  />
                </section>
              </section>
            </section>
          </el-form-item>
          <el-form-item label="水文气象">
            <section style="display: flex; padding: 0 5px; flex-wrap: wrap">
              <section
                v-for="(item, index) in getWeather"
                :key="index"
                style="display: flex"
                items-center
              >
                <label style="flex: 1">{{ item.label }}</label>
                <section>
                  <el-switch
                    v-model="item.value"
                    @change="
                      (val) => handleChange(item.typeName, val, item.typeCode)
                    "
                  />
                </section>
              </section>
            </section>
          </el-form-item>
        </el-form>

        <el-button @click="() => drawPoline('clear')">清空绘制</el-button>
        <el-button @click="() => drawPoline('Rectange')"
          >长方形/ 正方形</el-button
        >
        <el-button @click="() => drawPoline('Polygon')">多边形</el-button>
        <el-button @click="() => drawPoline('Circle')">圆形</el-button>
        <el-button @click="() => drawPoline('Sector')">扇形</el-button>
        <el-button @click="() => drawPoline('Ellipse')">椭圆</el-button>
        <el-button @click="() => drawPoline('FreeDraw')"
          >自由绘制按住鼠标左键开始绘制</el-button
        >

        <el-button @click="() => showArea('areas')">回显区域数据</el-button>
        <el-button @click="() => showArea('lines')">回显航线</el-button>
        <el-button @click="() => showArea('markers')">回显marker点</el-button>
      </section>
      <!-- <template #rightMenu>
        <div>测试插槽</div>
      </template> -->
    </BaseMapComponent>
    <ElAffix position="bottom" :offset="160" :z-index="10000">
      <ElButton type="primary" @click="handleShowTools">工具箱</ElButton>
    </ElAffix>
  </div>
</template>

<script setup>
  import { computed, reactive, ref } from 'vue'
  import { ElAffix, ElButton } from 'element-plus'
  import { BaseMapComponent } from '@map-sdk/components'
  import { trackAi, trackList, trackList3 } from './trackList'
  import { haifeng } from './haifeng'
  import { hl } from './hl'
  import { yangliu } from './yangliu'

  const baseMap = ref(null)
  const isShowShip = ref(false)
  const status = reactive({
    isShowTools: false,
    map: null,
    trackId: null,
    settingList: {
      layer: [
        {
          label: '全球排放控制区',
          typeName: 'showGlobalEmissionControlArea',
        },
        {
          label: '中国领海基线',
          typeName: 'showBaselineOfChinaTerritorialSea',
        },
        {
          label: '全球重要海峡',
          typeName: 'showStraitsOfGlobalImportance',
        },
        {
          label: '全球海区',
          typeName: 'showGlobalMarineArea',
        },
        {
          label: '全球时区',
          typeName: 'showGlobalTimeZone',
        },
        {
          label: '经纬网格',
          typeName: 'showWarpAndLatitudeGrid',
        },
        {
          label: '中国沿海航路',
          typeName: 'showSeaRoute',
        },
        {
          label: '渔区边界',
          typeName: 'showFisheryManager',
        },
        {
          label: 'AIS图层显示',
          typeName: 'showAisManager',
        },

        {
          label: '海洋地震',
          typeName: 'showEarthQuakeAreaSymbol',
        },
        {
          label: '海盗事件',
          typeName: 'showPiratesSymbol',
        },
        {
          label: '碰撞高风险区',
          typeName: 'showCollisionRiskSymbol',
        },
        {
          label: '碍航网具',
          typeName: 'showObstacleareaSymbol',
        },
        {
          label: '渔业协定水域',
          typeName: 'showFinshAreaSymbol',
        },
        {
          label: '🌓全球晨昏线',
          typeName: 'showTerminatorSymbol',
        },
      ],
      weather: [
        {
          label: '海区预报',
          typeName: 'showSeaAreaForecast',
        },
        {
          label: '潮汐',
          typeName: 'showTide',
        },
        {
          label: '台风',
          typeName: 'showTyphoon',
        },
        {
          label: '风场',
          typeName: 'showSeaBreeze',
        },
        {
          label: '海风',
          typeName: 'showWind',
        },
        {
          label: '海流',
          typeName: 'showHL',
        },

        {
          label: '洋流',
          typeName: 'showYangliu',
        },
        {
          label: '水温',
          typeName: 'showWaterTemperature',
          typeCode: '0',
        },
        {
          label: '冰情',
          typeName: 'showIceCondition',
          typeCode: '1',
        },
        {
          label: '海冰',
          typeName: 'showSeaIce',
          typeCode: '2',
        },
        {
          label: '海风',
          typeName: 'showSeaBreezeLayer',
          typeCode: '3',
        },
        {
          label: '海风箭头',
          typeName: 'showSeaBreezeArrow',
          typeCode: '4',
        },
        {
          label: '海风+箭头',
          typeName: 'showSeaBreezeArrow',
          typeCode: '3,4',
        },
        {
          label: '降雨量',
          typeName: 'showRainfall',
          typeCode: '5',
        },
        {
          label: '气压',
          typeName: 'showAtmosphericPressure',
          typeCode: '6',
        },
        {
          label: '能见度',
          typeName: 'showVisibility',
          typeCode: '7',
        },
        {
          label: '气温',
          typeName: 'showAirTemperature',
          typeCode: '8',
        },
        {
          label: '海浪',
          typeName: 'showSeaWave',
          typeCode: '9',
        },
        {
          label: '海浪箭头',
          typeName: 'showWaveArrow',
          typeCode: '10',
        },
        {
          label: '海浪+箭头',
          typeName: 'showHaiLang',
          typeCode: '9,10',
        },
      ],
    },
    rightMenuList: [
      {
        label: '传入菜单',
        clickCallback: (e, data) => {
          console.log('点击了click', e, data)
        },
      },
    ],
  })
  const getLayer = computed(() => status.settingList.layer)
  const getWeather = computed(() => status.settingList.weather)
  const isDisabled = computed(() => !isShowShip.value)
  const handleShowTools = () => {
    status.isShowTools = !status.isShowTools
    console.log('isShowTools', status.isShowTools)
  }
  const initMap = (map) => {
    console.log(`地图：${map}`)
    status.map = map
  }
  const selectShipItem = (item) => {
    console.log('selectShipItem', item)
  }
  const selectedShipUpdate = (item) => {
    console.log('selectedShipUpdate', item)
  }
  const local = () => {
    if (baseMap.value) {
      // 通过 ref 访问子组件的方法
      baseMap.value.locate('14554a117d5c4e98bf34d1a8e05babe8')
    }
  }

  const shipShow = () => {
    baseMap.value.shipShow([
      {
        source: 0,
        mmsi: 229402000,
        shipid: 'EDC78302A978E5B5',
        lng: 113.925383,
        lat: 22.364165,
        hdg: 128,
        cog: 155.1,
        sog: 0.09,
        rot: 0,
        name: 'CEPOLIS',
        length: 180,
        width: 32,
        left: 10,
        trail: 33,
        // tracks: [],
      },
      {
        source: 0,
        mmsi: 477887901,
        shipid: '7F1B356A294C6D13',
        tradetype: 2,
        name: 'SITC HENGDE 11',
        cnname: '',
        callsign: 'VRVG3',
        draught: 9100,
        dest: 'SHEKOU,CN',
        eta: '07-02 12:00',
        laststa: 1719904688,
        lng: 113.868147,
        lat: 22.484455,
        sog: 0,
        cog: 6.64,
        hdg: 34.2,
        rot: 0,
        navistatus: 5,
        lastdyn: 1720060310,
        satelliteutc: 0,
        lastdyn_active: true,
        type: 100,
        imo: 0,
        length: 180,
        width: 32,
        left: 10,
        trail: 33,
      },
      {
        name: 'SITC HENGDE',
        shipid: '14554a117d5c4e98bf34d1a8e05babe8',
        orgId: '91cf0b2216e74f688e86f19213139423',
        lng: 114.0297,
        lat: 22.3498,
        cog: 13.776,
        rot: 1.1,
        sog: 0,
        mmsi: '477887900',
        hdg: 128,
        // 按比例缩放值
        length: 180,
        width: 32,
        left: 10,
        trail: 33,
      },
      {
        name: '测试船舶定位船舶',
        source: 0,
        mmsi: 412321605,
        shipid: 'BF29CDE29F45D02E',
        lng: 121.121472,
        lat: 38.722985,
        cog: 163.1,
        sog: 6.2,
        rot: 327.67,
        is_radar: false,
        navistatus: 255,
        lastdyn: 1711503635,
        type: 30,
        length: 26,
        tracks: [],
        istop: false,
        state: 0,
        lineWidth: 1,
        rotate: 0,
        shiptype: 1,
        lineColor: '#00BCD4',
        heightLineColor: '#00BCD4',
        color: '#00BCD4',
      },
      {
        name: '测试预测轨迹船舶',
        source: 0,
        mmsi: '4131101',
        shipid: '6138CF3E5B032BD2',
        lng: 121.13555,
        lat: 38.727367,
        sog: 65.535,
        cog: 65.535,
        hdg: 65.535,
        rot: 32.767,
        tradetype: 99,
        type: 60,
        imo: '524288000',
        matchtype: 0,
        cnname: '',
        callsign: 'BAPO',
        length: 1300,
        width: 200,
        left: 150,
        trail: 1130,
        draught: 5000,
        dest: 'LANSHAN,CN',
        eta: '08-01 04:10',
        laststa: 1439241968,
        navistatus: 255,
        lastdyn: 1718602690,
        satelliteutc: 0,
      },
    ])
    isShowShip.value = true
  }
  const closeRightMenu = () => {
    baseMap.value.closeRightMenu()
  }
  const drawLine = () => {
    baseMap.value.drawLine()
  }
  const drawLineEdit = () => {
    baseMap.value.drawLineEdit()
  }
  const cancleDrawLineEdit = () => {
    baseMap.value.cancleDrawLineEdit()
  }
  const getTrack = (start) => {
    return trackList.slice(start * 20, 20 * start + 20)
  }
  // 测试多次追加  "rollup-plugin-javascript-obfuscator": "^1.0.4",
  const testLoad = () => {
    let i = 0
    const interval = setInterval(() => {
      if (trackList.length <= i * 20) {
        interval && clearInterval(interval)
        baseMap.value.appendFinish(status.trackId)
        return
      }
      const list = getTrack(i)
      baseMap.value.appendTrack(status.trackId, list)
      i++
    }, 1000)
  }
  const showTrack = () => {
    status.trackId = baseMap.value.showTrack(
      '123123trackId',
      { name: '船舶名称', mmsi: '0000000' },
      trackList3.init, // trackAi.track1.list, //getTrack(0),
      {
        isEnablePlayer: true, //是否需要播放轨迹，默认值：false
        lineColor: '#00BCD4',
        // ai 检测
        aiDetection: {
          show: true,
          activityColor: '#06c84a',
          datas: [],
          event: {
            clickCallback: (item) => {
              console.log('点击回调函数', item)
              emit('AiDetectionClick', item)
            },
          },
        },
      }
    )
  }

  const emit = defineEmits(['AiDetectionClick'])

  const appendTrack = () => {
    baseMap.value.appendTrack(status.trackId, trackList3.track, trackList3.ai)
    // testLoad()
  }
  const hideOrShowTrack = () => {
    // trackId isHide 是否隐藏 true:隐藏  false：显示 默认true
    baseMap.value.hideOrShowTrack(status.trackId)
    status.trackId = null
  }
  const showTrackForecast = () => {
    baseMap.value.showTrackForecast()
  }
  const hideTrackForeacast = () => {
    baseMap.value.hideTrackForeacast()
  }
  const showNavigationalWarning = () => {
    baseMap.value.showNavigationalWarning()
  }
  const hideNavigationalWarning = () => {
    baseMap.value.hideNavigationalWarning('1')
  }
  const handleChange = (layerType, isShow, item) => {
    let data
    if (layerType === 'showSeaBreeze') {
      data = haifeng.data
    }
    if (layerType === 'showYangliu') {
      data = yangliu.data
    }
    if (layerType === 'showHL') {
      data = hailiu.data
    }
    if (layerType === 'showWind') {
      data = haifeng.data
    }
    baseMap.value.showLayer(layerType, isShow, item, data)
  }

  /**
   * 右键菜单回调函数
   * @param {*} param0
   */
  const rightMenuShip = ({ ship, style }) => {
    console.log(ship, style)
  }
  /**
   * ai 检测回调函数
   * @param {*} item
   */
  const aiDetectionClick = (item) => {
    console.log(item)
  }

  const drawPoline = (type) => {
    baseMap.value.drawPoline(type)
  }
  const showArea = (type) => {
    baseMap.value.showArea(type)
  }
  const cancelLocate = () => {
    baseMap.value.cancelLocate()
  }
  const localPirate = () => {
    baseMap.value.localPirate()
  }
  const cancelLocalPirate = () => {
    baseMap.value.cancelLocalPirate()
  }
  const showNavigation = () => {
    baseMap.value.showNavigation()
  }
  const hideNavigation = () => {
    baseMap.value.hideNavigation()
  }
  const aisNavLocation = () => {
    baseMap.value.aisNavLocation()
  }
  const showShipSj = () => {
    baseMap.value.showShipSj(
      '/ship-tiles/tileserver/mvt/cache?z={z}&x={x}&y={y}&shipNaviStat=0,1,2,3,4,5,6,7,8,15,255&srid=3395',
      {
        elaneOptions: {
          shipDetailURL: '/Ship/GetVector?k={k}',
          drawType: 'solid',
          radius: 3,
          enableMousemove: true,
          enableElaneClick: true,
        },
      }
    )
  }
  const hideShipSj = () => {
    baseMap.value.hideShipSj()
  }
</script>

<style lang="scss" scoped>
  .toolbar {
    position: absolute;
    z-index: 1000;
    background-color: rgb(153 240 255 / 50%);
    backdrop-filter: blur(5px);
    padding: 10px 53px;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
    flex-direction: row;
    align-items: stretch;
  }
  .toolbar button {
    margin: 2.5px 5px;
  }
  ul,
  li {
    list-style: none;
  }
</style>
```

## 效果图

![总览](./image/1.png)
![绘制船舶](./image/2.png)
![选中船舶](./image/3.png)
![历史轨迹](./image/4.png)
![气象轨迹](./image/5.png)
![航行警告](./image/6.png)
![晨昏线](./image/7.png)
