vue3: tmap (腾讯地图)using typescript

article/2025/8/24 14:52:57

项目结构:

<!--*                   ___====-_  _-====___*             _--^^^#####//      \\#####^^^--_*          _-^##########// (    ) \\##########^-_*         -############//  |\^^/|  \\############-*       _/############//   (@::@)   \############\_*      /#############((     \\//     ))#############\*     -###############\\    (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.hkcw.cn/article/QnvSEUjpWw.shtml

相关文章

【Linux】线程概念

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲Linux——线程概念&#xff1a; 线程的基本概念线程的优缺点线程与进程 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&am…

技术文档撰写指南:从结构到细节的全流程解析

在技术领域&#xff0c;一份优质的技术文档不仅是项目成果的载体&#xff0c;更是技术思想的可视化表达。本文结合《汽车导航系统电路设计及故障分析》课程设计说明书&#xff0c;拆解技术文档的核心要素&#xff0c;提供可复用的撰写范式&#xff0c;助力技术内容高效传播。 …

如何用AI设计海报,DeepSeek+即梦免费批量生成

大家好&#xff0c;这里是K姐。 一个帮助你把AI真正用起来的女子。 佳节将至&#xff0c;还在为节日海报而苦恼吗&#xff1f; 520刚过&#xff0c;端午节、六一儿童节、618就接踵而至&#xff0c;满街满屏的海报让人眼花缭乱。 做自媒体电商以及实体店的小伙伴现在已经一个…

全国一体化算力体系建设:破解算力困局,赋能数字经济新未来​

在数字经济蓬勃发展的当下&#xff0c;算力作为核心生产力&#xff0c;正面临着前所未有的挑战与机遇。从 GPT3.5 到 GPT4 的升级&#xff0c;算力需求呈现跳跃式增长&#xff0c;需要至少提高 3 到 5 倍的算力&#xff0c;国内算力供应出现断层&#xff0c;难以满足当前需求。…

window安装nginx

步骤1&#xff1a;下载Nginx for Windows​ 访问Nginx官网下载页面&#xff1a;https://nginx.org/en/download.html 在​​Stable version​​&#xff08;稳定版&#xff09;下找到Windows版本&#xff0c;点击下载.zip文件&#xff08;如 nginx-1.28.0.zip&#xff09; 步…

秋招Day11 - JVM - 垃圾回收

讲讲JVM的垃圾回收机制 垃圾回收是指JVM对内存中已经死亡的&#xff0c;不再使用的对象进行清除或回收。 常见的垃圾回收算法有标记-复制&#xff0c;标记-整理&#xff0c;标记-清除&#xff0c;分代收集算法等 一般的垃圾回收。过程是先使用可达性分析算法得出内存中哪些对…

Deepseek应用技巧-Dify安装和踩坑指南

前言&#xff1a;Dify的名号是非常大的&#xff0c;作为私有化AI部署中必不可少的一个组件&#xff0c;他的功能和COZE十分相似&#xff0c;可以进行工作流和智能体的搭建&#xff0c;有非常强大的功能&#xff0c;那本节就将来揭开Dify的神秘的面纱&#xff0c;首先看一下Dify…

[python] argparse怎么指定bool类型?

前述 最近在写脚本的时候想要实现一个if 操作&#xff0c;通过用户输入。确定要不要启用某个语句。 非常自然的就是使用python的argparse包&#xff0c;但是发现了一个陷阱&#xff0c;记录下。 陷阱 argparse.ArgumentParser() 可以指定输入类型&#xff0c;我可以设定为bo…

尚硅谷redis7 86 redis集群分片之3主3从集群搭建

86 redis集群分片之3主集群搭建 3主3从redis集群配置 找3台真实虚拟机,各自新建 mķdir -p /myredis/cluster 新建6个独立的redis实例服务 IP:192.168.111.175端口6381/端口6382 vim /myredis/cluster/redisCluster6381.conf bind 0.0.0.0 daemonize yes protected-mode no …

交集、差集、反选

1.交集&#xff1a;两个ROI相交的部分 dev_open_window (0, 0, 512, 512, black, WindowHandle) read_image (Image, clip) threshold (Image, Region, 0, 128) * 交集 intersection (Circle1, Circle2, RegionIntersection) 最终效果如下图所示&#xff1a; 2.差集&#xff1a…

Flutter GridView网格组件

目录 常用属性 GridView使用配置 GridView.count使用 GridView.extent使用 GridView.count Container 实现列表 GridView.extent Container 实现列表 GridView.builder使用 GridView网格布局在实际项目中用的也是非常多的&#xff0c;当我们想让可以滚动的元素使用矩阵…

The 2020 ICPC Asia Yinchuan Regional Programming Contest

A. Best Player 关于投影到坐标轴上&#xff0c;投影到x轴上&#xff0c;确实如果两个点的y值一样&#xff0c;会导致重影&#xff0c;但不能只看只看y轴的影响&#xff0c;还有要注意输出。 #include<bits/stdc.h> using namespace std; typedef long long ll; const l…

寄存器模型

8.layering sequence &#xff08;1&#xff09;概述 转化&#xff1a;高抽象级item&#xff0c;中间的sequence&#xff0c;低抽象级item。 &#xff08;2&#xff09;寄存器模型的示意图 &#xff08;3&#xff09;示例代码&#xff1a;bus packet sequence &#xff08;4&…

Python训练营打卡Day39

DAY 39 图像数据与显存 知识点回顾 1.图像数据的格式&#xff1a;灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 作业&#xff1a;今日代码较少&#xff0c;理解内容…

AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年5月29日第92弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀6-8个和值&#xff0c;可以做到100-300注左右。 (1)定…

【AI智能体】Coze 插件从使用到实战详解

目录 一、前言 二、Coze 插件介绍 2.1 什么是插件 2.1.1 插件主要功能 2.1.2 coze 插件常用类型 2.1.3 coze 插件优势 2.2 插件与工具 2.3 插件费用说明 2.4 使用限制 2.5 权限说明 2.6 coze添加插件入口 三、Coze插件创建与使用 3.1 前置准备 3.1.1 查看api列表 …

python实现一个示波器仿真,可以改参数同步效果

代码 import sys import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar from matplotlib.an…

【前端】使用grid布局封装断点式进度条

业务需求要求展示一个动态进度条&#xff0c;不同于第三方插件的进度条&#xff0c;这个ui设计的是断点式进度条&#xff0c;效果当然是美观大方&#xff0c;但是因为没有现成的第三方插件可以实现&#xff0c;这当然难不倒一个成熟的前端开发工程师。 根据ui设计可以看到进度条…

[ctfshow web入门] web78

信息收集 表面上没有任何过滤&#xff0c;自由发挥 if(isset($_GET[file])){$file $_GET[file];include($file); }else{highlight_file(__FILE__); }解题 这些解法都在web32解释过&#xff0c;不再详细解释&#xff0c;有需要点击超链接&#xff1a;[ctfshow web入门] web3…

小白的进阶之路系列之六----人工智能从初步到精通pytorch数据集与数据加载器

本文将介绍以下内容: 数据集与数据加载器 数据迁移 如何建立神经网络 数据集与数据加载器 处理数据样本的代码可能会变得混乱且难以维护;理想情况下,我们希望我们的数据集代码与模型训练代码解耦,以获得更好的可读性和模块化。PyTorch提供了两个数据原语:torch.utils…