高德地图_infoWindow的动态组件渲染

infoWindow的基本使用方法

// infowidnow 的 innerHTML
var infoWindowContent =
    '<div className="custom-infowindow input-card">' +
        '<label style="color:grey">故宫博物院</label>' +
        '<div class="input-item">' +
            '<div class="input-item-prepend">' +
                '<span class="input-item-text" >经纬度</span>' +
            '</div>' +
            '<input id="lnglat" type="text" />' +
        '</div>' +
        // 为 infowindow 添加自定义事件
        '<input id="lnglat2container" type="button" class="btn" value="获取该位置经纬度" onclick="getLngLat()"/>' +
    '</div>';

// 创建一个自定义内容的 infowindow 实例
var infoWindow = new AMap.InfoWindow({
    position: lnglat,
    offset: new AMap.Pixel(0, -30),
    content: infoWindowContent
});
  • 如上代码所示: 使用new AMap.InfoWindow便可以创建一个简单的infoWindow信息窗体

动态创建infoWindow窗体内容

import { h, render, getCurrentInstance } from "vue"
interface IUseInfoWindowReturn {
  readonly infoWindow: AMap.InfoWindow | null
  readonly container: HTMLDivElement | null
  getInfoWindow: () => AMap.InfoWindow
  openInfoWindow: (params: IOpenInfoWindowParams) => void
  closeInfoWindow: () => void
  destroyInfoWindow: () => void
}
interface IOpenInfoWindowParams {
  mapInstance: AMap.Map,
  position: AMap.LngLat | [number, number],
  data: any,
  component: Component,
  allowScroll?: boolean,
  events?: Record<string, (...args: any[]) => void>
}
//#region useInfoWindow: 生成infoWindow
async function useInfoWindow(): Promise<IUseInfoWindowReturn> {
  let infoWindow: AMap.InfoWindow | null = null
  let container: HTMLDivElement | null = null
  const internalInstance = getCurrentInstance()
  const appContext = internalInstance?.appContext
  const $Store = useStore() // 用于获取在pinia中的全局AMap
  const AMap = await $Store.initAMap()

  //#region 1. getInfoWindow:初始化 InfoWindow 实例
  const getInfoWindow = (): AMap.InfoWindow => {
    if (!infoWindow) {
      infoWindow = new AMap.InfoWindow({
        offset: new AMap.Pixel(0, -30),
        isCustom: true,
        autoMove: true,
      })
    }
    return infoWindow
  }
  //#endregion

  //#region 2. 打开 InfoWindow
  const openInfoWindow = (
    {
      mapInstance, // 当前的地图实例
      position, // infowindow的打开坐标
      data, // 传入组件的数据
      component, // 组件
      allowScroll = false, // 默认不允许在信息窗口上滚动鼠标缩放地图
      events = {} // 在组件上挂载的自定义事件
    }: IOpenInfoWindowParams
  ) => {
    try {
      const iw = getInfoWindow()
      closeInfoWindow() // 打开前先清理旧内容

      container = document.createElement("div")
      container.classList.add('info_window_container')
      const vNode = h(component, {
        data, infoWindow: iw,
        ...Object.fromEntries(
          Object.entries(events).map(([name, fn]) => [
            "on" + name[0].toUpperCase() + name.slice(1),
            fn
          ])
        )
      }) // 所有的自定义事件都以on开头

      if (!allowScroll) {
        const onEnter = () => mapInstance.setStatus({ scrollWheel: false })
        const onLeave = () => mapInstance.setStatus({ scrollWheel: true })
        container.addEventListener("mouseenter", onEnter)
        container.addEventListener("mouseleave", onLeave)
      } // 禁止滚轮事件

      //  关联上下文, 否则组件的中useRouter/pinia等无法使用
      if (appContext) {
        vNode.appContext = appContext;
      }
      
      render(vNode, container)
      iw.setContent(container)
      iw.open(mapInstance, position)
    } catch (err) {
      console.log(err)
    }
  }
  //#endregion

  //#region 3. 关闭 InfoWindow
  const closeInfoWindow = () => {
    if (infoWindow) {
      infoWindow.close()
      const content = infoWindow.getContent()
      if (content instanceof HTMLElement) {
        render(null, content) // 卸载旧 vnode
      }
    }
  }
  //#endregion

  //#region 4. 销毁 InfoWindow
  const destroyInfoWindow = () => {
    closeInfoWindow()
    infoWindow = null
    container = null
  }
  //#endregion

  //#region 5. 返回一个可始终访问最新 infoWindow 的对象
  return {
    get infoWindow() {
      return infoWindow
    },
    get container() {
      return container
    },
    getInfoWindow,
    openInfoWindow,
    closeInfoWindow,
    destroyInfoWindow,
  }
  //#endregion
}
//#endregion

对应的组件需要接收的props

  • 在组件的props中需要接data, infowWindow两个属性
interface IInfoComProps {
  data: {
    location: ILocation
  }
  infoWindow: AMap.InfoWindow
}
const $props = defineProps<IInfoComProps>()

调用hook动态生成信息窗体

  1. 初始化hook
let INFOWINDOW: IUseInfoWindowReturn = null
async function initHooks() {
  INFOWINDOW = await $keyAreaHooks.useInfoWindow()
}
  1. 调用一个组件来动态地生成信息窗体
// 以一个labelMarker的右键点击事件为例
async function onLabelMarkerRightClick(e) {
  const location: ILocation = e.target.getExtData().location
  if (INFOWINDOW) {
    INFOWINDOW.closeInfoWindow()
    INFOWINDOW.openInfoWindow({
      mapInstance,
      position: new AMap.LngLat(location.lng, location.lat),
      data: { location },
      component: $constants.infoWindowType.lcoationMarkerInfo, // 组件
      events: {
        edit: onLocationEdit,
        del: onLocationDel
      } // 在组件内部有自定义事件edit与del, 它们会经过处理以onEdit与onDel的形式挂载到组件上
    })
  }
}
  1. 附录: 组件中的自定义事件的触发
//#region 业务逻辑: 修改
function onEdit() {
  $emits('edit', { locationDetail: locationDetail.value })
}
//#endregion

//#region 业务逻辑: 删除
function onDel() {
  $emits('del', { locationDetail: locationDetail.value })
}
//#endregion
posted @ 2026-01-05 17:48  Syinho  阅读(5)  评论(0)    收藏  举报