当前位置: 首页 > news >正文

vue3: tmap (腾讯地图)using typescript

项目结构:

<!--*                   ___====-_  _-====___*             _--^^^#####//      \\#####^^^--_*          _-^##########// (    ) \\##########^-_*         -############//  |\^^/|  \\############-*       _/############//   (@::@)   \############\_*      /#############((     \\//     ))#############\*     -###############\\    (oo)    //###############-*    -#################\\  / VV \  //#################-*   -###################\\/      \//###################-*  _#/|##########/\######(   /\   )######/\##########|\#_*  |/ |#/\#/\#/\/  \#/\##\  |  |  /##/\#/  \/\#/\#/\#| \|*  `  |/  V  V  `   V  \#\| |  | |/#/  V   '  V  V  \|  '*     `   `  `      `   / | |  | | \   '      '  '   '*                      (  | |  | |  )*                     __\ | |  | | /__*                    (vvv(VVV)(VVV)vvv)**      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~**                神兽保佑            永无BUG** @Author: geovindu* @Date: 2025-05-26 14:23:15* @LastEditors: geovindu* @LastEditTime: 2025-05-26 14:29:48* @FilePath: \vue\vuepdfpreview\src\viewer\tmap.vue* @Description: geovindu* @lib,packpage:** @IDE: vscode* @jslib: node 20 vue.js 3.0* @OS: windows10* @database: mysql 8.0  sql server 2019 postgreSQL 16* Copyright (c) geovindu 2025 by geovindu@163.com, All Rights Reserved.--><template><div id="app"><h1>深圳酒店地图(腾讯版)</h1><div v-if="loading" class="loading-message">加载中...</div><div v-else-if="error" class="error-message">加载失败: {{ error.message }}</div><TencentMapMarker v-else :hotels="hotels" :ak="tencentMapAK" /></div></template><script lang="ts" setup>import { ref, onMounted } from 'vue';import TencentMapMarker from '../components/TencentMapMarker.vue';// 腾讯地图API密钥(需要替换为你自己的)const tencentMapAK = ref('HROBZ-RETK7-A6TXK-HSLIA-YAVYZ-CVBVX');const hotels = ref<any[]>([]);const loading = ref(true);const error = ref<Error | null>(null);// 从JSON文件加载酒店数据const loadHotelData = async () => {try {const response = await fetch('hotels.json');if (!response.ok) {throw new Error(`HTTP错误! 状态码: ${response.status}`);}const data = await response.json();hotels.value = data;console.log('酒店数据加载成功:', data);} catch (err: any) {console.error('加载酒店数据失败:', err);error.value = err;} finally {loading.value = false;}};onMounted(() => {loadHotelData();});</script><style>#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;padding: 0 20px;}h1 {color: #333;}.loading-message, .error-message {padding: 20px;margin: 20px;background-color: #f5f5f5;border-radius: 8px;}.error-message {color: #f56c6c;}</style> 
<!--*                   ___====-_  _-====___*             _--^^^#####//      \\#####^^^--_*          _-^##########// (    ) \\##########^-_*         -############//  |\^^/|  \\############-*       _/############//   (@::@)   \############\_*      /#############((     \\//     ))#############\*     -###############\\    (oo)    //###############-*    -#################\\  / VV \  //#################-*   -###################\\/      \//###################-*  _#/|##########/\######(   /\   )######/\##########|\#_*  |/ |#/\#/\#/\/  \#/\##\  |  |  /##/\#/  \/\#/\#/\#| \|*  `  |/  V  V  `   V  \#\| |  | |/#/  V   '  V  V  \|  '*     `   `  `      `   / | |  | | \   '      '  '   '*                      (  | |  | |  )*                     __\ | |  | | /__*                    (vvv(VVV)(VVV)vvv)**      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~**                神兽保佑            永无BUG** @Author: geovindu* @Date: 2025-05-26 14:23:15* @LastEditors: geovindu* @LastEditTime: 2025-05-29 17:38:46* @FilePath: \vue\vuepdfpreview\src\components\TencentMapMarker.vue* @Description: geovindu* @lib,packpage:** @IDE: vscode* @jslib: node 20 vue.js 3.0* @OS: windows10* @database: mysql 8.0  sql server 2019 postgreSQL 16* Copyright (c) geovindu 2025 by geovindu@163.com, All Rights Reserved.-->
<template><div class="map-container" ref="mapContainer" style="height: 600px;"></div>
</template><script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch, defineComponent } from 'vue';interface Hotel {name: string;content: string;center: string;type: number;icon: string;
}const props = defineProps<{hotels: Hotel[];ak: string;
}>();const mapContainer = ref<HTMLElement | null>(null);
let map: TMap.Map | null = null;
let markerLayer: TMap.MultiMarker | null = null;
let infoWindow: TMap.InfoWindow | null = null;
let currentHotel = ref<Hotel | null>(null);// 加载腾讯地图API
const loadTMapScript = (ak: string) => {return new Promise<void>((resolve, reject) => {if (window.TMap) {resolve();return;}const script = document.createElement('script');script.src = `https://map.qq.com/api/gljs?v=1.0&key=${ak}`;script.async = true;script.onload = () => {if (window.TMap) {resolve();} else {reject(new Error('腾讯地图API加载失败,但脚本已加载'));}};script.onerror = (err) => reject(new Error('加载腾讯地图API失败,请检查网络连接或API密钥'));document.head.appendChild(script);});
};// 初始化地图
const initMap = async () => {if (!mapContainer.value || !props.ak) {console.error('地图容器或API密钥未设置');return;}try {await loadTMapScript(props.ak);map = new TMap.Map(mapContainer.value, {center: new TMap.LatLng(22.543099, 114.057868),zoom: 12});// 创建信息窗口(不关联map,在显示时动态关联)infoWindow = new TMap.InfoWindow({enableCustom: true,map: map,position: new TMap.LatLng(39.984104, 116.307503),offset: { y: -70, x: -5 }});initMarkerLayer();console.log('腾讯地图初始化完成');} catch (error: any) {console.error('地图初始化失败:', error.message);}
};// 初始化标记图层
const initMarkerLayer = () => {if (!map) return;markerLayer = new TMap.MultiMarker({id: 'hotel-markers',map: map,styles: {'hotel-marker': new TMap.MarkerStyle({width: 32,height: 32,anchor: { x: 16, y: 32 },src: 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png'})},geometries: []});// 标记点击事件markerLayer.on('click', (event) => {const geometry = event.geometry;if (!geometry || !geometry.position || !(geometry.position instanceof TMap.LatLng)) {console.error('无效的标记位置:', geometry);return;}if (infoWindow) {//console.log('信息窗口是否打开:', infoWindow.isOpen());//infoWindow.open();// 更新当前酒店数据currentHotel.value = {name: geometry.properties?.name || '酒店信息',content: geometry.properties?.content || '暂无描述',center: '',type: 0,icon: ''};// 创建唯一ID的DOM容器const containerId = 'info-window-content-' + Date.now();const container = document.createElement('div');container.id = containerId;document.body.appendChild(container);// 设置信息窗口内容infoWindow.setContent(`<div class="info_card"><div class="title"><span class="title_name">${currentHotel.value.name}</span><button class="close_btn" id="close-btn-${containerId}">×</button></div><div class="content">${currentHotel.value.content}</div></div>`);// 使用 setTimeout 确保DOM已渲染/* */setTimeout(() => {// 添加关闭按钮事件监听const closeBtn = document.getElementById(`close-btn-${containerId}`);if (closeBtn) {closeBtn.addEventListener('click', () => {if (infoWindow) infoWindow.close();});}// 关联地图并显示infoWindow.setMap(map);infoWindow.setPosition(geometry.position);infoWindow.open();}, 0);}});updateMarkers();
};// 更新标记
const updateMarkers = () => {if (!markerLayer || !props.hotels || props.hotels.length === 0) return;const geometries = props.hotels.map((hotel, index) => {try {const [lng, lat] = hotel.center.split(',').map(Number);return {id: `hotel-${index}`,styleId: 'hotel-marker',position: new TMap.LatLng(lat, lng),properties: {name: hotel.name,content: hotel.content,type: hotel.type}};} catch (e) {console.error(`酒店数据解析错误 (${hotel.name}):`, e);return null;}}).filter(Boolean) as TMap.MultiMarkerGeometry[];markerLayer.setGeometries(geometries);
};onMounted(() => {initMap();
});watch(() => props.hotels, () => {if (markerLayer) updateMarkers();
});onUnmounted(() => {if (infoWindow) {infoWindow.close();infoWindow.setMap(null);}if (markerLayer) markerLayer.setMap(null);if (map) {map.destroy();map = null;}markerLayer = null;infoWindow = null;
});
</script><style scoped>
.map-container {width: 100%;height: 100%;
}/* 信息窗口样式 */
.info_card {width: 240px;background-color: white;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);overflow: hidden;font-family: Arial, sans-serif;
}.title {height: 36px;line-height: 36px;background-color: #f5f5f5;padding: 0 12px;position: relative;
}.title_name {font-weight: bold;color: #333;font-size: 15px;
}.close_btn {position: absolute;right: 10px;top: 8px;width: 20px;height: 20px;background: none;border: none;font-size: 18px;color: #999;cursor: pointer;padding: 0;
}.close_btn:hover {color: #333;
}.content {padding: 12px;font-size: 14px;color: #666;line-height: 1.5;
}
</style>

输出:

相关文章:

  • vr中风--数据处理模型搭建与训练
  • vr中风--数据处理模型搭建与训练2
  • 【ARM】【FPGA】【硬件开发】Chapter.1 AXI4总线协议
  • C# 打印PDF的常用方法
  • Oracle MOVE ONLINE 实现原理
  • 高效开发,升级软件,硬件也要专业
  • 软考-系统架构设计师-第十五章 信息系统架构设计理论与实践
  • 如何将图像插入 PDF:最佳工具比较
  • 替代 WPS 的新思路?快速将 Word 转为图片 PDF
  • [Go] Option选项设计模式 — — 编程方式基础入门
  • 缓存架构方案:Caffeine + Redis 双层缓存架构深度解析
  • WPF 按钮点击音效实现
  • CPP中CAS std::chrono 信号量与Any类的手动实现
  • 虚幻基础:模型
  • C primer plus (第六版)第六章 编程练习第10题
  • PCA主成分分析与Python应用
  • 【conda报错】InvalidArchiveError
  • 深入解析Java8核心新特性(Optional、新的日期时间API、接口增强)
  • Kafka核心技术解析与最佳实践指南
  • RPG17.蓝图函数库与轻重攻击连击
  • 西安品牌网站建设服务商/近期时事新闻
  • 标书制作培训机构/泽成seo网站排名
  • php网站中水印怎么做的/在线seo诊断
  • 知名做网站费用/百度入口网页版
  • 投资企业网站备案要前置认证/交易平台官网
  • 烟台网站建设.com/网络营销策划方案范文