Explorar o código

修改一张图跳转链接

mork hai 2 semanas
pai
achega
280d022dec
Modificáronse 1 ficheiros con 160 adicións e 170 borrados
  1. 160 170
      src/views/map/OneMap.vue

+ 160 - 170
src/views/map/OneMap.vue

@@ -1,199 +1,161 @@
 <template>
   <div class="gis-page">
     <div class="card">
-      <!-- <div class="card-head">空间一张图</div> -->
       <div class="card-body">
         <div class="gis-container" ref="iframeContainer">
-          <iframe 
-            ref="iframeRef" 
+          <!-- iframe 加载完成前隐藏,避免白屏闪出 -->
+          <iframe
+            v-show="isIframeReady"
+            ref="iframeRef"
             :key="iframeKey"
-            :src="iframeSrc" 
-            frameborder="0" 
-            allowfullscreen 
-            width="100%" 
-            height="100%" 
-            style=""
+            :src="iframeSrc"
+            frameborder="0"
+            allowfullscreen
+            width="100%"
+            height="100%"
             @load="onIframeLoad"
           ></iframe>
+          <!-- 加载遮罩(iframe 隐藏时显示) -->
+          <div v-if="!isIframeReady" class="iframe-loading">
+            <div class="iframe-loading-spinner"></div>
+            <div class="iframe-loading-text">{{ loadingText }}</div>
+            <div v-if="retryCount > 0" class="iframe-loading-retry">
+              已自动重试 {{ retryCount }} 次
+              <el-button type="primary" link size="small" @click="reloadIframe(true)">手动重试</el-button>
+            </div>
+          </div>
         </div>
       </div>
     </div>
   </div>
 </template>
+
 <script setup lang="ts">
 import CONFIG from '@/utils/config'
-import { ref, onMounted, watch } from 'vue'
-import { ElLoading } from "element-plus";
-import type { LoadingInstance } from 'element-plus'
+import { ref, onMounted, onUnmounted } from 'vue'
 
 const iframeRef = ref<HTMLIFrameElement | null>(null)
-const iframeContainer = ref<HTMLElement | null>(null)
-const loadingInstance = ref<LoadingInstance | null>(null)
+// const iframeContainer = ref<HTMLElement | null>(null)
 const iframeKey = ref(0)
 const iframeSrc = ref(CONFIG.ONEMAP_URL)
 const isIframeReady = ref(false)
-const loadStartTime = ref(0)
-const minLoadTime = 3000 // 最小加载时间(毫秒),确保页面有足够时间渲染
-const maxLoadTime = 15000 // 最大等待时间(毫秒)
-
-// 关闭 loading 的方法
-const closeLoading = () => {
-  if (loadingInstance.value) {
-    loadingInstance.value.close()
-    loadingInstance.value = null
+const loadingText = ref('加载地图中...')
+const retryCount = ref(0)
+
+const maxLoadTime = 15000
+const maxRetryCount = 2
+
+let maxTimeoutTimer: number | null = null
+let manualTimeoutTimer: number | null = null
+
+const closeMaxTimeoutTimer = () => {
+  if (maxTimeoutTimer) {
+    clearTimeout(maxTimeoutTimer)
+    maxTimeoutTimer = null
   }
 }
 
-// 检查 iframe 是否真正渲染完成
-const checkIframeFullyRendered = (): boolean => {
-  const iframe = iframeRef.value
-  if (!iframe) return false
-  
-  try {
-    const doc = iframe.contentDocument || (iframe.contentWindow as Window)?.document
-    if (!doc) return false
-    
-    // 检查文档状态
-    if (doc.readyState !== 'complete') {
-      return false
-    }
-    
-    // 检查是否有实际内容
-    if (!doc.body || !doc.body.innerHTML || doc.body.innerHTML.trim().length < 100) {
-      return false
-    }
-    
-    // 检查是否有可渲染的元素
-    const hasVisibleContent = doc.body.querySelector('div, svg, canvas, img') !== null
-    if (!hasVisibleContent) {
-      return false
-    }
-    
-    return true
-  } catch (e) {
-    // 跨域情况下,无法访问 document,说明 iframe 已加载但有跨域限制
-    return true
+const closeManualTimeoutTimer = () => {
+  if (manualTimeoutTimer) {
+    clearTimeout(manualTimeoutTimer)
+    manualTimeoutTimer = null
   }
 }
 
-// 等待 iframe 完全加载的定时器
-let renderCheckTimer: number | null = null
-let maxTimeoutTimer: number | null = null
+const onIframeLoad = () => {
+  closeMaxTimeoutTimer()
 
-// 轮询检查 iframe 是否渲染完成
-const startRenderCheck = () => {
-  // 清除之前的定时器
-  if (renderCheckTimer) clearInterval(renderCheckTimer)
-  if (maxTimeoutTimer) clearTimeout(maxTimeoutTimer)
+  const iframe = iframeRef.value
+  // const tempUser = sessionStorage.getItem('tempUser')
+  // if (tempUser && iframe?.contentWindow) {
+  //   try {
+  //     const user = JSON.parse(tempUser)
+  //     iframe.contentWindow.postMessage(
+  //       {
+  //         type: 'AUTO_LOGIN',
+  //         username: user.username,
+  //         password: user.password
+  //       },
+  //       CONFIG.ONEMAP_URL
+  //     )
+  //   } catch (err) {
+  //   }
+  // }
   
-  // 轮询检查
-  renderCheckTimer = window.setInterval(() => {
-    const elapsed = Date.now() - loadStartTime.value
-    
-    // 至少等待最小加载时间
-    if (elapsed < minLoadTime) return
-    
-    // 检查是否渲染完成
-    if (checkIframeFullyRendered()) {
-      isIframeReady.value = true
-      closeLoading()
-      if (renderCheckTimer) clearInterval(renderCheckTimer)
-    } else {
+  if (iframe?.contentWindow) {
+    try {
+      iframe.contentWindow.postMessage(
+        {
+          type: 'AUTO_LOGIN',
+          token: localStorage.getItem('token'),
+        },
+        CONFIG.ONEMAP_URL
+      )
+    } catch (err) {
+      
     }
-  }, 500) // 每 500ms 检查一次
-  
-  // 设置最大超时
-  maxTimeoutTimer = window.setTimeout(() => {
-    isIframeReady.value = true
-    closeLoading()
-    if (renderCheckTimer) clearInterval(renderCheckTimer)
-  }, maxLoadTime)
-}
-
-// iframe onload 事件处理
-const onIframeLoad = () => {
-  // 发送登录信息
-  const iframe = iframeRef.value
-  const tempUser = sessionStorage.getItem('tempUser')
-  if (tempUser && iframe) {
-    const user = JSON.parse(tempUser)
-    iframe.contentWindow?.postMessage(
-      {
-        type: 'AUTO_LOGIN',
-        username: user.username,
-        password: user.password
-      },
-      CONFIG.ONEMAP_URL
-    )
   }
-  
-  // 开始轮询检查渲染状态
-  startRenderCheck()
+
+  // 文档 load 完成先显示 iframe,瓦片继续后台加载
+  isIframeReady.value = true
 }
 
-const reloadIframe = () => {
-  // 重置状态
+/**
+ * 刷新 iframe
+ * @param hardRefresh true 强制销毁 DOM(解决缓存异常时使用)
+ */
+const reloadIframe = (hardRefresh = false) => {
   isIframeReady.value = false
-  loadStartTime.value = Date.now()
-  
-  // 通过改变 key 强制 iframe 重新渲染
-  iframeKey.value++
-  
-  // 同时更新 src 确保重新加载(添加时间戳避免缓存)
-  iframeSrc.value = CONFIG.ONEMAP_URL + (CONFIG.ONEMAP_URL.includes('?') ? '&' : '?') + 't=' + Date.now()
+  closeMaxTimeoutTimer()
+  closeManualTimeoutTimer()
+
+  const timestamp = 't=' + Date.now()
+  const sep = CONFIG.ONEMAP_URL.includes('?') ? '&' : '?'
+  const newSrc = CONFIG.ONEMAP_URL + sep + timestamp
+
+  if (hardRefresh) {
+    iframeKey.value++
+  }
+  iframeSrc.value = newSrc
+  loadingText.value = '加载地图中...'
+
+  // 超时兜底:防止 load 事件因跨域资源卡死而永不触发
+  maxTimeoutTimer = window.setTimeout(() => {
+    // 超时后还没 ready,尝试自动重试(最多 maxRetryCount 次)
+    if (retryCount.value < maxRetryCount) {
+      retryCount.value++
+      loadingText.value = `加载超时,第 ${retryCount.value} 次重试...`
+      reloadIframe(true)
+    } else {
+      // 重试耗尽,强制显示 iframe(即使是空白也让用户看到手动重试按钮)
+      isIframeReady.value = true
+    }
+  }, maxLoadTime)
 }
 
-// 监听子页面发送的消息
+// 接收子页面消息(如果未来子页面支持 IFRAME_READY 消息可直接复用)
 const handleMessage = (event: MessageEvent) => {
-  // 验证消息来源
-  const expectedOrigin = new URL(CONFIG.ONEMAP_URL).origin
-  if (event.origin !== expectedOrigin) {
-    return
-  }
-  
-  // 如果子页面发送加载完成的消息,立即关闭 loading
-  if (event.data && event.data.type === 'IFRAME_READY') {
-    isIframeReady.value = true
-    closeLoading()
-    if (renderCheckTimer) clearInterval(renderCheckTimer)
-    if (maxTimeoutTimer) clearTimeout(maxTimeoutTimer)
+  try {
+    const expectedOrigin = new URL(CONFIG.ONEMAP_URL).origin
+    if (event.origin !== expectedOrigin) return
+    if (event.data?.type === 'IFRAME_READY') {
+      isIframeReady.value = true
+      closeMaxTimeoutTimer()
+    }
+  } catch (e) {
   }
 }
 
 onMounted(() => {
-  // 只在容器存在时显示加载
-  if (iframeContainer.value) {
-    loadingInstance.value = ElLoading.service({
-      target: iframeContainer.value,
-      lock: true,
-      text: 'Loading',
-      background: 'rgba(0, 0, 0, 0.7)',
-    })
-  }
-  
-  // 记录开始时间
-  loadStartTime.value = Date.now()
-  
-  // 强制重新加载 iframe
-  reloadIframe()
-  
-  // 监听子页面消息
+  reloadIframe(false)
   window.addEventListener('message', handleMessage)
-  
-  // 页面刷新前的清理
-  window.addEventListener('beforeunload', () => {
-    closeLoading()
-    if (renderCheckTimer) clearInterval(renderCheckTimer)
-    if (maxTimeoutTimer) clearTimeout(maxTimeoutTimer)
-  })
 })
 
-// 监听 key 变化,重置加载状态
-watch(iframeKey, () => {
-  loadStartTime.value = Date.now()
+onUnmounted(() => {
+  closeMaxTimeoutTimer()
+  closeManualTimeoutTimer()
+  window.removeEventListener('message', handleMessage)
 })
-
-
 </script>
 
 <style scoped>
@@ -208,22 +170,58 @@ watch(iframeKey, () => {
   border-radius: 10px;
 }
 
-.card-head {
-  padding: 12px 16px;
-  border-bottom: 1px solid #e8ecef;
-  font-weight: 600;
-  font-size: 14px;
-}
-
 .card-body {
   padding: 0;
 }
 
 .gis-container {
+  position: relative;
   display: flex;
-  height: calc(100vh - (170px - 108px));
-  /* height: calc(100vh - (170px - 46px));
-  border-radius: 10px; */
+  height: calc(100vh - (170px - 102px));
+  /* 深底色盖住 iframe load 前可能出现的空白闪烁 */
+  background: #1a2a3a;
+  overflow: hidden;
+}
+
+/* iframe 加载中的遮罩层 */
+.iframe-loading {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 16px;
+  background: #1a2a3a;
+  z-index: 10;
+}
+
+.iframe-loading-spinner {
+  width: 40px;
+  height: 40px;
+  border: 3px solid rgba(255, 255, 255, 0.2);
+  border-top-color: #409eff;
+  border-radius: 50%;
+  animation: spin 0.8s linear infinite;
+}
+
+@keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+.iframe-loading-text {
+  color: #c0ccda;
+  font-size: 14px;
+}
+
+.iframe-loading-retry {
+  color: #909399;
+  font-size: 13px;
+  display: flex;
+  align-items: center;
+  gap: 4px;
 }
 
 @media (max-width: 768px) {
@@ -231,13 +229,5 @@ watch(iframeKey, () => {
     flex-direction: column;
     height: auto;
   }
-  .gis-map {
-    height: 300px;
-  }
-  .gis-sidebar {
-    width: 100%;
-    border-left: none;
-    border-top: 1px solid #e8ecef;
-  }
 }
 </style>