| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- <template>
- <web-view
- ref="webviewRef"
- id="leafletWebView"
- :src="webviewUrl"
- @message="handleMessage"
- @load="handleLoad"
- class="leaflet-webview"
- ></web-view>
- </template>
- <script setup lang="uts">
- import { ref, computed, watch } from 'vue'
- import type { MapMarker, MapLayerType } from '../../types/map'
- // Props
- type Props = {
- latitude?: number
- longitude?: number
- zoom?: number
- markers?: MapMarker[]
- layerType?: MapLayerType
- tiandituKey?: string
- }
- const props = withDefaults(defineProps<Props>(), {
- latitude: 32.556211,
- longitude: 111.494811,
- zoom: 13,
- markers: () => [],
- layerType: 'tianditu-img',
- tiandituKey: '4c9c8a818d3cb25bf5ccbd749e7e67c8'
- })
- // Emits
- const emit = defineEmits<{
- (e: 'markerTap', event: any): void
- (e: 'regionChange', region: any): void
- (e: 'layerChange', layerType: MapLayerType): void
- (e: 'ready'): void
- (e: 'error', error: string): void
- }>()
- // WebView 引用(内置组件使用对应的 Element 类型)
- const webviewRef = ref<UniWebViewElement | null>(null)
- // WebView 是否已加载
- const isWebviewLoaded = ref(false)
- // WebView URL
- const webviewUrl = computed<string>(() => {
- return `/static/leaflet/leaflet-map.html?lat=${props.latitude}&lng=${props.longitude}&zoom=${props.zoom}&layer=${props.layerType}&key=${props.tiandituKey}`
- })
- // 处理 WebView 加载完成
- const handleLoad = (): void => {
- isWebviewLoaded.value = true
- console.log('WebView loaded')
-
- // 延迟触发 ready 事件,等待地图初始化完成
- // 因为 web-view 在不同平台的消息机制不同,所以直接在 load 完成后触发
- setTimeout(() => {
- console.log('Auto-triggering ready event after WebView load')
- emit('ready')
- }, 1000)
- }
- // 处理 WebView 消息
- const handleMessage = (e: any): void => {
- try {
- console.log('WebView message received')
-
- // WebView 的 message 事件在不同平台可能有不同的数据结构
- // 直接触发 ready 事件,让地图组件可以开始工作
- console.log('Emitting ready event')
- emit('ready')
- } catch (error: any) {
- console.error('Error handling WebView message:', error)
- // 即使出错也触发 ready
- emit('ready')
- }
- }
- // 执行 JavaScript(内置组件直接调用方法)
- const evalJS = (js: string): void => {
- if (!isWebviewLoaded.value || webviewRef.value == null) {
- return
- }
-
- // #ifdef APP
- // App 平台:web-view 是原生组件,直接调用 evalJS 方法
- webviewRef.value.evalJS(js)
- // #endif
-
- // #ifdef WEB
- // Web 平台:web-view 是 iframe,通过 contentWindow 执行 JavaScript
- try {
- // 尝试多种方式获取 iframe 元素
- let iframeElement = null
-
- // 方式1:通过 $el 访问
- const iframe = webviewRef.value as any
- if (iframe && iframe.$el) {
- if (iframe.$el.tagName === 'IFRAME') {
- iframeElement = iframe.$el
- } else {
- // 可能是包装元素,查找其中的 iframe
- iframeElement = iframe.$el.querySelector('iframe')
- }
- }
-
- // 方式2:通过 ID 查找
- if (iframeElement == null) {
- iframeElement = document.getElementById('leafletWebView')
- }
-
- // 方式3:查找所有 iframe
- if (iframeElement == null) {
- const iframes = document.getElementsByTagName('iframe')
- if (iframes.length > 0) {
- iframeElement = iframes[0]
- }
- }
-
- console.log('iframe element:', iframeElement)
- console.log('iframe contentWindow:', iframeElement ? iframeElement.contentWindow : null)
-
- if (iframeElement && iframeElement.contentWindow) {
- // 使用 eval 在 iframe 的上下文中执行 JavaScript
- iframeElement.contentWindow.eval(js)
- console.log('✓ Web platform: evalJS executed successfully')
- } else {
- console.warn('Web platform: Cannot access iframe contentWindow', {
- iframeElement: iframeElement,
- contentWindow: iframeElement ? iframeElement.contentWindow : null
- })
- }
- } catch (e: any) {
- console.error('Web platform: evalJS error:', e)
- }
- // #endif
- }
- // 添加标记点
- const addMarker = (marker: MapMarker): void => {
- const js = `window.leafletMap.addMarker(${JSON.stringify({
- id: marker.id,
- latitude: marker.latitude,
- longitude: marker.longitude,
- title: marker.title,
- iconPath: marker.iconPath,
- width: marker.width,
- height: marker.height
- })})`
- evalJS(js)
- }
- // 批量添加标记点
- const addMarkers = (markers: MapMarker[]): void => {
- const markersData = markers.map((marker) => ({
- id: marker.id,
- latitude: marker.latitude,
- longitude: marker.longitude,
- title: marker.title,
- iconPath: marker.iconPath,
- width: marker.width,
- height: marker.height
- }))
- const js = `window.leafletMap.addMarkers(${JSON.stringify(markersData)})`
- evalJS(js)
- }
- // 清除所有标记点
- const clearMarkers = (): void => {
- const js = 'window.leafletMap.clearMarkers()'
- evalJS(js)
- }
- // 移除单个标记点
- const removeMarker = (markerId: number): void => {
- const js = `window.leafletMap.removeMarker(${markerId})`
- evalJS(js)
- }
- // 添加折线
- const addPolyline = (points: any[], color: string | null, width: number | null): void => {
- const finalColor = color != null ? color : '#007aff'
- const finalWidth = width != null ? width : 4
-
- // 构造数据对象
- const data = {
- points: points,
- color: finalColor,
- width: finalWidth
- }
-
- const js = `window.leafletMap.addPolyline(${JSON.stringify(data)})`
- evalJS(js)
- }
- // 添加圆形
- const addCircle = (
- latitude: number,
- longitude: number,
- radius: number,
- strokeColor: string | null,
- fillColor: string | null
- ): void => {
- const finalStrokeColor = strokeColor != null ? strokeColor : '#007aff'
- const finalFillColor = fillColor != null ? fillColor : 'rgba(0, 122, 255, 0.2)'
-
- // 构造数据对象
- const data = {
- latitude: latitude,
- longitude: longitude,
- radius: radius,
- strokeColor: finalStrokeColor,
- fillColor: finalFillColor
- }
-
- const js = `window.leafletMap.addCircle(${JSON.stringify(data)})`
- evalJS(js)
- }
- // 添加多边形
- const addPolygon = (
- points: any[],
- strokeColor: string | null,
- fillColor: string | null
- ): void => {
- const finalStrokeColor = strokeColor != null ? strokeColor : '#007aff'
- const finalFillColor = fillColor != null ? fillColor : 'rgba(0, 122, 255, 0.2)'
-
- // 构造数据对象
- const data = {
- points: points,
- strokeColor: finalStrokeColor,
- fillColor: finalFillColor
- }
-
- const js = `window.leafletMap.addPolygon(${JSON.stringify(data)})`
- evalJS(js)
- }
- // 清除所有覆盖物
- const clearOverlays = (): void => {
- const js = 'window.leafletMap.clearOverlays()'
- evalJS(js)
- }
- // 设置地图中心
- const setCenter = (latitude: number, longitude: number, zoom: number | null): void => {
- if (zoom != null) {
- const js = `window.leafletMap.setCenter(${latitude}, ${longitude}, ${zoom})`
- evalJS(js)
- } else {
- // 使用当前缩放级别
- const js = `window.leafletMap.setCenter(${latitude}, ${longitude})`
- evalJS(js)
- }
- }
- // 设置缩放级别
- const setZoom = (zoom: number): void => {
- const js = `window.leafletMap.setZoom(${zoom})`
- evalJS(js)
- }
- // 切换图层
- const switchLayer = (layerType: MapLayerType): void => {
- const js = `window.leafletMap.switchLayer('${layerType}')`
- evalJS(js)
- emit('layerChange', layerType)
- }
- // 添加业务图层
- const addBusinessLayer = (layerCode: string, layerUrl: string, baseUrl: string, token: string | null): void => {
- const tokenParam = token != null ? token : ''
- const js = `window.leafletMap.addBusinessLayer('${layerCode}', '${layerUrl}', '${baseUrl}', '${tokenParam}')`
- evalJS(js)
- }
- // 移除业务图层
- const removeBusinessLayer = (layerCode: string): void => {
- const js = `window.leafletMap.removeBusinessLayer('${layerCode}')`
- evalJS(js)
- }
- // 暴露方法
- defineExpose({
- addMarker,
- addMarkers,
- clearMarkers,
- removeMarker,
- addPolyline,
- addCircle,
- addPolygon,
- clearOverlays,
- setCenter,
- setZoom,
- switchLayer,
- addBusinessLayer,
- removeBusinessLayer
- })
- </script>
- <style lang="scss">
- .leaflet-webview {
- width: 100%;
- height: 100%;
- }
- </style>
|