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

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>

输出:

http://www.xdnf.cn/news/9880.html

相关文章:

  • sudo apt-get update错误解决
  • Spring Boot+Activiti7入坑指南初阶版
  • 表单请求为什么需要进行 URL 编码?—详解application/x-www-form-urlencoded的正确用法
  • 从零开始的数据结构教程(五)​​动态规划(DP)
  • 数据结构 --- 顺序表
  • 安卓学习笔记-数据存储
  • [嵌入式实验]实验二:LED控制
  • Dynamics 365 Business Central AI Sales Order Agent Copilot
  • Redis 延迟队列
  • 【东枫科技】KrakenSDR 天线阵列设置
  • 1.测试过程之需求分析和测试计划
  • 【LeetCode 热题 100】最小路径和 / 最长回文子串 / 最长公共子序列 / 编辑距离
  • Ubuntu 中安装 PostgreSQL 及常规操作指南
  • JAVA与C语言之间的差异(二)
  • 1614. 括号的最大嵌套深度【 力扣(LeetCode) 】
  • 摩尔信使MThings无法生成机器码的解决方法
  • 腾讯云国际站性能调优
  • 【静电模拟】使用打火机的电子部分模拟手指静电
  • 机器学习-线性回归基础
  • 【Elasticsearch】suggest
  • C++17常量
  • 【Python办公】将Excel表格转json(字典)数据-可自定义key和value
  • TeleAI发布TeleChat2.5及T1正式版,双双开源上线魔乐社区!
  • 实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.6 R语言解题
  • ubuntu mysql 8.0.42 基于二进制日志文件位置和GTID主从复制配置
  • 玛哈特校平机:金属板材加工的精整专家
  • 记一次 Starrocks be 内存异常宕机
  • Ubuntu20.04操作系统ssh开启oot账户登录
  • 大数据学习(125)-hive数据分析
  • HOW - 简历和求职面试宝典(七)