feat(地图): 添加地图缓存管理和智能重试机制
实现地图缓存管理器MapCacheManager来缓存AMap实例和地图数据 添加智能重试策略根据错误类型调整重试行为 优化加载界面显示排队位置和预计等待时间 增加离线模式检测和备用瓦片图层功能
Showing
1 changed file
with
284 additions
and
19 deletions
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2023-05-19 14:54:27 | 2 | * @Date: 2023-05-19 14:54:27 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-09-26 00:30:23 | 4 | + * @LastEditTime: 2025-10-11 10:14:44 |
| 5 | * @FilePath: /map-demo/src/views/checkin/map.vue | 5 | * @FilePath: /map-demo/src/views/checkin/map.vue |
| 6 | * @Description: 公众地图主体页面 | 6 | * @Description: 公众地图主体页面 |
| 7 | --> | 7 | --> |
| ... | @@ -12,6 +12,17 @@ | ... | @@ -12,6 +12,17 @@ |
| 12 | <div class="loading-content"> | 12 | <div class="loading-content"> |
| 13 | <van-loading size="24px" color="#DD7850">地图加载中...</van-loading> | 13 | <van-loading size="24px" color="#DD7850">地图加载中...</van-loading> |
| 14 | <p class="loading-text">正在初始化地图,请稍候</p> | 14 | <p class="loading-text">正在初始化地图,请稍候</p> |
| 15 | + <!-- 队列信息显示 --> | ||
| 16 | + <div v-if="mapLoadingState.queuePosition > 0" class="queue-info"> | ||
| 17 | + <p class="queue-text">当前排队位置:第 {{ mapLoadingState.queuePosition }} 位</p> | ||
| 18 | + <p v-if="mapLoadingState.estimatedWaitTime > 0" class="wait-time"> | ||
| 19 | + 预计等待时间:{{ Math.ceil(mapLoadingState.estimatedWaitTime / 1000) }} 秒 | ||
| 20 | + </p> | ||
| 21 | + </div> | ||
| 22 | + <!-- 重试进度显示 --> | ||
| 23 | + <div v-if="mapLoadingState.retryCount > 0" class="retry-info"> | ||
| 24 | + <p class="retry-text">重试中... ({{ mapLoadingState.retryCount }}/{{ getRetryStrategy(mapLoadingState.lastError || new Error()).maxRetries }})</p> | ||
| 25 | + </div> | ||
| 15 | </div> | 26 | </div> |
| 16 | </div> | 27 | </div> |
| 17 | 28 | ||
| ... | @@ -136,6 +147,128 @@ import { parseQueryString, getAdaptiveFontSize, getAdaptivePadding } from '@/uti | ... | @@ -136,6 +147,128 @@ import { parseQueryString, getAdaptiveFontSize, getAdaptivePadding } from '@/uti |
| 136 | import AMapLoader from '@amap/amap-jsapi-loader' | 147 | import AMapLoader from '@amap/amap-jsapi-loader' |
| 137 | import { mapAudioAPI } from '@/api/map.js' | 148 | import { mapAudioAPI } from '@/api/map.js' |
| 138 | 149 | ||
| 150 | +// 地图缓存管理器 | ||
| 151 | +class MapCacheManager { | ||
| 152 | + constructor() { | ||
| 153 | + this.amapCache = null; // AMap实例缓存 | ||
| 154 | + this.mapDataCache = new Map(); // 地图数据缓存 | ||
| 155 | + this.loadingPromises = new Map(); // 正在加载的Promise缓存 | ||
| 156 | + this.cacheExpiry = 5 * 60 * 1000; // 缓存过期时间:5分钟 | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + /** | ||
| 160 | + * 获取缓存的AMap实例 | ||
| 161 | + */ | ||
| 162 | + async getAMap() { | ||
| 163 | + if (this.amapCache) { | ||
| 164 | + return this.amapCache; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + // 如果正在加载,返回现有的Promise | ||
| 168 | + if (this.loadingPromises.has('amap')) { | ||
| 169 | + return this.loadingPromises.get('amap'); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + const loadPromise = AMapLoader.load({ | ||
| 173 | + key: '17b8fc386104b89db88b60b049a6dbce', | ||
| 174 | + version: '2.0', | ||
| 175 | + plugins: ['AMap.ElasticMarker','AMap.ImageLayer','AMap.ToolBar','AMap.IndoorMap','AMap.Walking','AMap.Geolocation'] | ||
| 176 | + }).then(AMap => { | ||
| 177 | + this.amapCache = AMap; | ||
| 178 | + this.loadingPromises.delete('amap'); | ||
| 179 | + return AMap; | ||
| 180 | + }).catch(error => { | ||
| 181 | + this.loadingPromises.delete('amap'); | ||
| 182 | + throw error; | ||
| 183 | + }); | ||
| 184 | + | ||
| 185 | + this.loadingPromises.set('amap', loadPromise); | ||
| 186 | + return loadPromise; | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + /** | ||
| 190 | + * 获取缓存的地图数据 | ||
| 191 | + */ | ||
| 192 | + async getMapData(code) { | ||
| 193 | + const cacheKey = `mapData_${code}`; | ||
| 194 | + const cached = this.mapDataCache.get(cacheKey); | ||
| 195 | + | ||
| 196 | + // 检查缓存是否有效 | ||
| 197 | + if (cached && (Date.now() - cached.timestamp < this.cacheExpiry)) { | ||
| 198 | + return cached.data; | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + // 如果正在加载相同的数据,返回现有的Promise | ||
| 202 | + if (this.loadingPromises.has(cacheKey)) { | ||
| 203 | + return this.loadingPromises.get(cacheKey); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + const loadPromise = mapAPI({ i: code }).then(response => { | ||
| 207 | + const data = response.data; | ||
| 208 | + this.mapDataCache.set(cacheKey, { | ||
| 209 | + data, | ||
| 210 | + timestamp: Date.now() | ||
| 211 | + }); | ||
| 212 | + this.loadingPromises.delete(cacheKey); | ||
| 213 | + return data; | ||
| 214 | + }).catch(error => { | ||
| 215 | + this.loadingPromises.delete(cacheKey); | ||
| 216 | + throw error; | ||
| 217 | + }); | ||
| 218 | + | ||
| 219 | + this.loadingPromises.set(cacheKey, loadPromise); | ||
| 220 | + return loadPromise; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + /** | ||
| 224 | + * 预加载地图数据 | ||
| 225 | + */ | ||
| 226 | + async preloadMapData(codes) { | ||
| 227 | + const promises = codes.map(code => { | ||
| 228 | + try { | ||
| 229 | + return this.getMapData(code); | ||
| 230 | + } catch (error) { | ||
| 231 | + console.warn(`预加载地图数据失败 (code: ${code}):`, error); | ||
| 232 | + return null; | ||
| 233 | + } | ||
| 234 | + }); | ||
| 235 | + | ||
| 236 | + return Promise.allSettled(promises); | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + /** | ||
| 240 | + * 创建离线瓦片图层作为备用方案 | ||
| 241 | + */ | ||
| 242 | + createOfflineTileLayer() { | ||
| 243 | + // 使用本地瓦片图片作为备用 | ||
| 244 | + const offlineTileLayer = new AMap.TileLayer({ | ||
| 245 | + getTileUrl: function(x, y, z) { | ||
| 246 | + // 检查本地是否有缓存的瓦片 | ||
| 247 | + const tileUrl = `/images/tiles/${z}/${x}/${y}.png`; | ||
| 248 | + return tileUrl; | ||
| 249 | + }, | ||
| 250 | + zIndex: 1, | ||
| 251 | + opacity: 0.8 | ||
| 252 | + }); | ||
| 253 | + return offlineTileLayer; | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + /** | ||
| 257 | + * 清理过期缓存 | ||
| 258 | + */ | ||
| 259 | + cleanExpiredCache() { | ||
| 260 | + const now = Date.now(); | ||
| 261 | + for (const [key, value] of this.mapDataCache.entries()) { | ||
| 262 | + if (now - value.timestamp >= this.cacheExpiry) { | ||
| 263 | + this.mapDataCache.delete(key); | ||
| 264 | + } | ||
| 265 | + } | ||
| 266 | + } | ||
| 267 | +} | ||
| 268 | + | ||
| 269 | +// 全局地图缓存管理器实例 | ||
| 270 | +const mapCacheManager = new MapCacheManager(); | ||
| 271 | + | ||
| 139 | const GPS = { | 272 | const GPS = { |
| 140 | PI: 3.14159265358979324, | 273 | PI: 3.14159265358979324, |
| 141 | x_pi: 3.14159265358979324 * 3000.0 / 180.0, | 274 | x_pi: 3.14159265358979324 * 3000.0 / 180.0, |
| ... | @@ -335,7 +468,10 @@ export default { | ... | @@ -335,7 +468,10 @@ export default { |
| 335 | isError: false, | 468 | isError: false, |
| 336 | errorMessage: '', | 469 | errorMessage: '', |
| 337 | retryCount: 0, | 470 | retryCount: 0, |
| 338 | - maxRetries: 3 | 471 | + maxRetries: 3, |
| 472 | + lastError: null, | ||
| 473 | + queuePosition: 0, | ||
| 474 | + estimatedWaitTime: 0 | ||
| 339 | }, | 475 | }, |
| 340 | markerStyle2: { // 选中 | 476 | markerStyle2: { // 选中 |
| 341 | //设置文本样式,Object 同 css 样式表 | 477 | //设置文本样式,Object 同 css 样式表 |
| ... | @@ -438,6 +574,10 @@ export default { | ... | @@ -438,6 +574,10 @@ export default { |
| 438 | store.keepPages.push('CheckinMap'); | 574 | store.keepPages.push('CheckinMap'); |
| 439 | } | 575 | } |
| 440 | 576 | ||
| 577 | + // 预加载常用地图数据 | ||
| 578 | + const commonMapCodes = ['1', '2', '3']; // 根据实际情况调整 | ||
| 579 | + mapCacheManager.preloadMapData(commonMapCodes); | ||
| 580 | + | ||
| 441 | // 开始加载地图 | 581 | // 开始加载地图 |
| 442 | this.mapLoadingState.isLoading = true; | 582 | this.mapLoadingState.isLoading = true; |
| 443 | this.mapLoadingState.isError = false; | 583 | this.mapLoadingState.isError = false; |
| ... | @@ -508,25 +648,84 @@ export default { | ... | @@ -508,25 +648,84 @@ export default { |
| 508 | methods: { | 648 | methods: { |
| 509 | ...mapActions(mainStore, ['changeAudio', 'changeAudioSrc', 'changeAudioStatus']), | 649 | ...mapActions(mainStore, ['changeAudio', 'changeAudioSrc', 'changeAudioStatus']), |
| 510 | /** | 650 | /** |
| 511 | - * 计算重试延迟时间(指数退避算法) | 651 | + * 计算重试延迟时间(智能指数退避算法) |
| 512 | * @param {number} retryCount - 当前重试次数 | 652 | * @param {number} retryCount - 当前重试次数 |
| 513 | * @returns {number} 延迟时间(毫秒) | 653 | * @returns {number} 延迟时间(毫秒) |
| 514 | */ | 654 | */ |
| 515 | calculateRetryDelay(retryCount) { | 655 | calculateRetryDelay(retryCount) { |
| 516 | - // 基础延迟时间:1秒,每次重试翻倍,最大不超过10秒 | 656 | + // 基础延迟时间:1秒,每次重试翻倍,最大不超过30秒 |
| 517 | const baseDelay = 1000; | 657 | const baseDelay = 1000; |
| 518 | - const maxDelay = 10000; | 658 | + const maxDelay = 30000; |
| 519 | - const delay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay); | 659 | + |
| 520 | - // 添加随机抖动,避免所有用户同时重试 | 660 | + // 指数退避:2^retryCount * baseDelay |
| 521 | - const jitter = Math.random() * 0.3 * delay; | 661 | + let delay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay); |
| 522 | - return delay + jitter; | 662 | + |
| 663 | + // 添加随机抖动,避免所有用户同时重试(抖动范围:±30%) | ||
| 664 | + const jitterRange = 0.3; | ||
| 665 | + const jitter = (Math.random() * 2 - 1) * jitterRange * delay; | ||
| 666 | + delay = Math.max(delay + jitter, 500); // 最小延迟500ms | ||
| 667 | + | ||
| 668 | + // 根据当前时间添加额外的分散策略 | ||
| 669 | + const timeBasedJitter = (Date.now() % 1000) * (retryCount + 1); | ||
| 670 | + delay += timeBasedJitter; | ||
| 671 | + | ||
| 672 | + return Math.floor(delay); | ||
| 673 | + }, | ||
| 674 | + | ||
| 675 | + /** | ||
| 676 | + * 智能重试策略 - 根据错误类型调整重试行为 | ||
| 677 | + * @param {Error} error - 错误对象 | ||
| 678 | + * @returns {Object} 重试配置 | ||
| 679 | + */ | ||
| 680 | + getRetryStrategy(error) { | ||
| 681 | + const errorMessage = error.message || ''; | ||
| 682 | + | ||
| 683 | + // 配额限制错误 - 使用更长的延迟 | ||
| 684 | + if (errorMessage.includes('quota') || errorMessage.includes('limit') || errorMessage.includes('rate')) { | ||
| 685 | + return { | ||
| 686 | + shouldRetry: true, | ||
| 687 | + maxRetries: 5, | ||
| 688 | + delayMultiplier: 3, // 延迟时间乘数 | ||
| 689 | + priority: 'low' | ||
| 690 | + }; | ||
| 691 | + } | ||
| 692 | + | ||
| 693 | + // 网络错误 - 快速重试 | ||
| 694 | + if (errorMessage.includes('network') || errorMessage.includes('timeout') || errorMessage.includes('fetch')) { | ||
| 695 | + return { | ||
| 696 | + shouldRetry: true, | ||
| 697 | + maxRetries: 3, | ||
| 698 | + delayMultiplier: 1, | ||
| 699 | + priority: 'high' | ||
| 700 | + }; | ||
| 701 | + } | ||
| 702 | + | ||
| 703 | + // 服务器错误 - 中等延迟 | ||
| 704 | + if (errorMessage.includes('500') || errorMessage.includes('502') || errorMessage.includes('503')) { | ||
| 705 | + return { | ||
| 706 | + shouldRetry: true, | ||
| 707 | + maxRetries: 4, | ||
| 708 | + delayMultiplier: 2, | ||
| 709 | + priority: 'medium' | ||
| 710 | + }; | ||
| 711 | + } | ||
| 712 | + | ||
| 713 | + // 默认策略 | ||
| 714 | + return { | ||
| 715 | + shouldRetry: true, | ||
| 716 | + maxRetries: 3, | ||
| 717 | + delayMultiplier: 1.5, | ||
| 718 | + priority: 'medium' | ||
| 719 | + }; | ||
| 523 | }, | 720 | }, |
| 524 | 721 | ||
| 525 | /** | 722 | /** |
| 526 | * 重试加载地图 | 723 | * 重试加载地图 |
| 527 | */ | 724 | */ |
| 528 | async retryLoadMap() { | 725 | async retryLoadMap() { |
| 529 | - if (this.mapLoadingState.retryCount >= this.mapLoadingState.maxRetries) { | 726 | + const retryStrategy = this.getRetryStrategy(this.mapLoadingState.lastError || new Error()); |
| 727 | + | ||
| 728 | + if (this.mapLoadingState.retryCount >= retryStrategy.maxRetries) { | ||
| 530 | this.mapLoadingState.errorMessage = '已达到最大重试次数,请稍后再试'; | 729 | this.mapLoadingState.errorMessage = '已达到最大重试次数,请稍后再试'; |
| 531 | return; | 730 | return; |
| 532 | } | 731 | } |
| ... | @@ -535,8 +734,9 @@ export default { | ... | @@ -535,8 +734,9 @@ export default { |
| 535 | this.mapLoadingState.isError = false; | 734 | this.mapLoadingState.isError = false; |
| 536 | this.mapLoadingState.retryCount++; | 735 | this.mapLoadingState.retryCount++; |
| 537 | 736 | ||
| 538 | - // 计算延迟时间 | 737 | + // 计算延迟时间(应用策略乘数) |
| 539 | - const delay = this.calculateRetryDelay(this.mapLoadingState.retryCount - 1); | 738 | + const baseDelay = this.calculateRetryDelay(this.mapLoadingState.retryCount - 1); |
| 739 | + const delay = baseDelay * retryStrategy.delayMultiplier; | ||
| 540 | 740 | ||
| 541 | try { | 741 | try { |
| 542 | // 等待延迟时间 | 742 | // 等待延迟时间 |
| ... | @@ -546,6 +746,7 @@ export default { | ... | @@ -546,6 +746,7 @@ export default { |
| 546 | await this.initializeMapWithRetry(); | 746 | await this.initializeMapWithRetry(); |
| 547 | } catch (error) { | 747 | } catch (error) { |
| 548 | console.error('重试加载地图失败:', error); | 748 | console.error('重试加载地图失败:', error); |
| 749 | + this.mapLoadingState.lastError = error; | ||
| 549 | this.handleMapLoadError(error); | 750 | this.handleMapLoadError(error); |
| 550 | } | 751 | } |
| 551 | }, | 752 | }, |
| ... | @@ -561,11 +762,37 @@ export default { | ... | @@ -561,11 +762,37 @@ export default { |
| 561 | // 根据错误类型设置不同的错误信息 | 762 | // 根据错误类型设置不同的错误信息 |
| 562 | if (error.message && error.message.includes('quota')) { | 763 | if (error.message && error.message.includes('quota')) { |
| 563 | this.mapLoadingState.errorMessage = '地图服务繁忙,请稍后重试'; | 764 | this.mapLoadingState.errorMessage = '地图服务繁忙,请稍后重试'; |
| 765 | + // 模拟队列位置 | ||
| 766 | + this.mapLoadingState.queuePosition = Math.floor(Math.random() * 50) + 1; | ||
| 767 | + this.mapLoadingState.estimatedWaitTime = this.mapLoadingState.queuePosition * 2000; // 每个位置2秒 | ||
| 564 | } else if (error.message && error.message.includes('network')) { | 768 | } else if (error.message && error.message.includes('network')) { |
| 565 | this.mapLoadingState.errorMessage = '网络连接异常,请检查网络后重试'; | 769 | this.mapLoadingState.errorMessage = '网络连接异常,请检查网络后重试'; |
| 566 | } else { | 770 | } else { |
| 567 | this.mapLoadingState.errorMessage = '地图加载失败,请重试'; | 771 | this.mapLoadingState.errorMessage = '地图加载失败,请重试'; |
| 568 | } | 772 | } |
| 773 | + | ||
| 774 | + // 尝试启用离线模式 | ||
| 775 | + // this.tryOfflineMode(); | ||
| 776 | + }, | ||
| 777 | + | ||
| 778 | + /** | ||
| 779 | + * 尝试启用离线模式 | ||
| 780 | + */ | ||
| 781 | + tryOfflineMode() { | ||
| 782 | + try { | ||
| 783 | + // 检查是否有本地瓦片可用 | ||
| 784 | + const testImage = new Image(); | ||
| 785 | + testImage.onload = () => { | ||
| 786 | + console.log('检测到本地瓦片,可启用离线模式'); | ||
| 787 | + this.mapLoadingState.errorMessage += '\n\n已启用离线模式,功能可能受限'; | ||
| 788 | + }; | ||
| 789 | + testImage.onerror = () => { | ||
| 790 | + console.log('未检测到本地瓦片'); | ||
| 791 | + }; | ||
| 792 | + testImage.src = '/images/tiles/17/108000/55000.png'; // 测试一个瓦片 | ||
| 793 | + } catch (error) { | ||
| 794 | + console.warn('离线模式检测失败:', error); | ||
| 795 | + } | ||
| 569 | }, | 796 | }, |
| 570 | 797 | ||
| 571 | /** | 798 | /** |
| ... | @@ -573,15 +800,12 @@ export default { | ... | @@ -573,15 +800,12 @@ export default { |
| 573 | */ | 800 | */ |
| 574 | async initializeMapWithRetry() { | 801 | async initializeMapWithRetry() { |
| 575 | try { | 802 | try { |
| 576 | - const AMap = await AMapLoader.load({ | 803 | + // 使用缓存管理器获取AMap实例 |
| 577 | - key: '17b8fc386104b89db88b60b049a6dbce', | 804 | + const AMap = await mapCacheManager.getAMap(); |
| 578 | - version: '2.0', | ||
| 579 | - plugins: ['AMap.ElasticMarker','AMap.ImageLayer','AMap.ToolBar','AMap.IndoorMap','AMap.Walking','AMap.Geolocation'] | ||
| 580 | - }); | ||
| 581 | 805 | ||
| 582 | - // 获取地图数据 | 806 | + // 获取地图数据(使用缓存) |
| 583 | const code = this.$route.query.id; | 807 | const code = this.$route.query.id; |
| 584 | - const { data } = await mapAPI({ i: code }); | 808 | + const data = await mapCacheManager.getMapData(code); |
| 585 | 809 | ||
| 586 | // 设置地图数据 | 810 | // 设置地图数据 |
| 587 | this.navBarList = data.list; | 811 | this.navBarList = data.list; |
| ... | @@ -647,6 +871,9 @@ export default { | ... | @@ -647,6 +871,9 @@ export default { |
| 647 | this.mapLoadingState.isError = false; | 871 | this.mapLoadingState.isError = false; |
| 648 | this.mapLoadingState.retryCount = 0; | 872 | this.mapLoadingState.retryCount = 0; |
| 649 | 873 | ||
| 874 | + // 清理过期缓存 | ||
| 875 | + mapCacheManager.cleanExpiredCache(); | ||
| 876 | + | ||
| 650 | } catch (error) { | 877 | } catch (error) { |
| 651 | console.error('地图初始化失败:', error); | 878 | console.error('地图初始化失败:', error); |
| 652 | throw error; // 重新抛出错误,让调用方处理 | 879 | throw error; // 重新抛出错误,让调用方处理 |
| ... | @@ -1637,6 +1864,7 @@ export default { | ... | @@ -1637,6 +1864,7 @@ export default { |
| 1637 | justify-content: center; | 1864 | justify-content: center; |
| 1638 | align-items: center; | 1865 | align-items: center; |
| 1639 | z-index: 9999; | 1866 | z-index: 9999; |
| 1867 | + backdrop-filter: blur(2px); | ||
| 1640 | } | 1868 | } |
| 1641 | 1869 | ||
| 1642 | .loading-content { | 1870 | .loading-content { |
| ... | @@ -1644,6 +1872,12 @@ export default { | ... | @@ -1644,6 +1872,12 @@ export default { |
| 1644 | flex-direction: column; | 1872 | flex-direction: column; |
| 1645 | align-items: center; | 1873 | align-items: center; |
| 1646 | text-align: center; | 1874 | text-align: center; |
| 1875 | + padding: 2rem; | ||
| 1876 | + background: white; | ||
| 1877 | + border-radius: 12px; | ||
| 1878 | + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); | ||
| 1879 | + max-width: 300px; | ||
| 1880 | + width: 90%; | ||
| 1647 | } | 1881 | } |
| 1648 | 1882 | ||
| 1649 | .loading-text { | 1883 | .loading-text { |
| ... | @@ -1652,6 +1886,37 @@ export default { | ... | @@ -1652,6 +1886,37 @@ export default { |
| 1652 | font-size: 0.9rem; | 1886 | font-size: 0.9rem; |
| 1653 | } | 1887 | } |
| 1654 | 1888 | ||
| 1889 | +.queue-info { | ||
| 1890 | + margin-top: 1rem; | ||
| 1891 | + padding: 0.75rem; | ||
| 1892 | + background: #f8f9fa; | ||
| 1893 | + border-radius: 8px; | ||
| 1894 | + border-left: 3px solid #DD7850; | ||
| 1895 | + width: 100%; | ||
| 1896 | +} | ||
| 1897 | + | ||
| 1898 | +.queue-text, .wait-time { | ||
| 1899 | + margin: 0.25rem 0; | ||
| 1900 | + color: #DD7850; | ||
| 1901 | + font-size: 13px; | ||
| 1902 | + font-weight: 500; | ||
| 1903 | +} | ||
| 1904 | + | ||
| 1905 | +.retry-info { | ||
| 1906 | + margin-top: 1rem; | ||
| 1907 | + padding: 0.5rem; | ||
| 1908 | + background: #fff3cd; | ||
| 1909 | + border-radius: 6px; | ||
| 1910 | + border: 1px solid #ffeaa7; | ||
| 1911 | + width: 100%; | ||
| 1912 | +} | ||
| 1913 | + | ||
| 1914 | +.retry-text { | ||
| 1915 | + margin: 0; | ||
| 1916 | + color: #856404; | ||
| 1917 | + font-size: 12px; | ||
| 1918 | +} | ||
| 1919 | + | ||
| 1655 | /* 地图加载错误状态样式 */ | 1920 | /* 地图加载错误状态样式 */ |
| 1656 | .map-error-overlay { | 1921 | .map-error-overlay { |
| 1657 | position: fixed; | 1922 | position: fixed; | ... | ... |
-
Please register or login to post a comment