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('your key'); 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>
/* * * ┏┓ ┏┓ * ┏┛┻━━━┛┻┓ * ┃ ┃ * ┃ ━ ┃ * ┃ > < ┃ * ┃ ┃ * ┃... ⌒ ... ┃ * ┃ ┃ * ┗━┓ ┏━┛ * ┃ ┃ * ┃ ┃ * ┃ ┃ * ┃ ┃ 神兽保佑 * ┃ ┃ 代码无bug * ┃ ┃ * ┃ ┗━━━┓ * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━┳┓┏┛ * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛ * * @Author: geovindu * @Date: 2025-05-26 12:03:18 * @LastEditors: geovindu * @LastEditTime: 2025-05-29 17:44:28 * @FilePath: \vue\vuepdfpreview\src\router\index.ts * @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. */ import { createRouter, createWebHistory} from 'vue-router'; import App from '../App.vue'; // 正确导入路由类型 import type { RouteRecordRaw } from 'vue-router'; import pdfview from '../viewer/pdfview.vue'; import pdfviewjs from '../viewer/pdfviewjs.vue'; import pdfviewprev from '../viewer/pdfprev.vue'; import tmap from '../viewer/tmap.vue'; const routes: RouteRecordRaw[] = [ { path: '/', name: 'Home', component: App }, { path: '/pdf', name: 'pdf', component: pdfview }, { path: '/pdfjs', name: 'pdfjs', component: pdfviewjs } , { path: '/pdfprev', name: 'pdfprev', component: pdfviewprev }, { path: '/tmap', name: 'tmap', component: tmap } // 添加其他路由 ]; const router = createRouter({ history: createWebHistory(), routes }); export default router;
<!-- * 江城子 . 程序员之歌 * * 十年生死两茫茫,写程序,到天亮。 * 千行代码,Bug何处藏。 * 纵使上线又怎样,朝令改,夕断肠。 * * 领导每天新想法,天天改,日日忙。 * 相顾无言,惟有泪千行。 * 每晚灯火阑珊处,夜难寐,加班狂。 * * * @Author: geovindu * @Date: 2025-05-09 10:54:24 * @LastEditors: geovindu * @LastEditTime: 2025-05-26 14:20:41 * @FilePath: \vue\vuepdfpreview\src\App.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. --> <!-- * |~~~~~~~| * | | * | | * | | * | | * | | * |~.\\\_\~~~~~~~~~~~~~~xx~~~ ~~~~~~~~~~~~~~~~~~~~~/_//;~| * | \ o \_ ,XXXXX), _..-~ o / | * | ~~\ ~-. XXXXX`)))), _.--~~ .-~~~ | * ~~~~~~~`\ ~\~~~XXX' _/ ';)) |~~~~~~..-~ _.-~ ~~~~~~~ * `\ ~~--`_\~\, ;;;\)__.---.~~~ _.-~ * ~-. `:;;/;; \ _..-~~ * ~-._ `'' /-~-~ * `\ / / * | , | | * | ' / | * \/; | * ;; | * `; . | * |~~~-----.....| * | \ \ * | /\~~--...__ | * (| `\ __-\| * || \_ /~ | * |) \~-' | * | | \ ' * | | \ : * \ | | | * | ) ( ) * \ /; /\ | * | |/ | * | | | * \ .' || * | | | | * ( | | | * | \ \ | * || o `.)| * |`\\) | * | | * | | * * @Author: geovindu * @Date: 2025-05-09 10:54:24 * @LastEditors: geovindu * @LastEditTime: 2025-05-09 14:55:13 * @FilePath: \vue\vuepdfpreview\src\App.vue * @Description: geovindu * @lib,packpage: * npm install vue-pdf-embed * npm install vue3-pdfjs * npm install pdfjs-dist@2.16.105 * @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> <header> <div class="wrapper"> <div class="pdf-container"> <nav> <RouterLink to="/">home</RouterLink> <RouterLink to="/pdf">pdf</RouterLink> <RouterLink to="/pdfjs">pdfjs</RouterLink> <RouterLink to="/pdfprev">pdfprev</RouterLink> <RouterLink to="/tmap">tmap</RouterLink> </nav> <RouterView /> </div> </div> </header> </template> <script setup lang="ts"> import { RouterLink, RouterView } from 'vue-router' //import HelloWorld from './components/HelloWorld.vue' //import PDFView from './components/vuepdfjs.vue' // 可以 //import PDFView from './components/pdfPreview.vue' // 可以 //import PDFView from "./components/pdfjs.vue" //可以 //import pdfUrl from "./pdfs/01.pdf" //const pdfUrl = "./pdfs/09.pdf" </script> <style scoped> .logo { height: 6em; padding: 1.5em; will-change: filter; transition: filter 300ms; } .logo:hover { filter: drop-shadow(0 0 2em #646cffaa); } .logo.vue:hover { filter: drop-shadow(0 0 2em #42b883aa); } </style>
/* * ┌───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ * │Esc│ │ F1│ F2│ F3│ F4│ │ F5│ F6│ F7│ F8│ │ F9│F10│F11│F12│ │P/S│S L│P/B│ ┌┐ ┌┐ ┌┐ * └───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘ └┘ └┘ └┘ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐ * │~ `│! 1│@ 2│# 3│$ 4│% 5│^ 6│& 7│* 8│( 9│) 0│_ -│+ =│ BacSp │ │Ins│Hom│PUp│ │N L│ / │ * │ - │ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤ * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │{ [│} ]│ | \ │ │Del│End│PDn│ │ 7 │ 8 │ 9 │ │ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘ ├───┼───┼───┤ + │ * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │: ;│" '│ Enter │ │ 4 │ 5 │ 6 │ │ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ ┌───┐ ├───┼───┼───┼───┤ * │ Shift │ Z │ X │ C │ V │ B │ N │ M │< ,│> .│? /│ Shift │ │ ↑ │ │ 1 │ 2 │ 3 │ │ * ├─────┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤ E││ * │ Ctrl│ │Alt │ Space │ Alt│ │ │Ctrl│ │ ← │ ↓ │ → │ │ 0 │ . │←─┘│ * └─────┴────┴────┴───────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘ * * @Author: geovindu * @Date: 2025-05-12 09:52:10 * @LastEditors: geovindu * @LastEditTime: 2025-05-12 17:12:18 * @FilePath: \vue\vuepdfpreview\src\main.ts * @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. */ import { createApp } from 'vue' import router from './router'; // 导入路由配置 import VuePdfEmbed from "vue-pdf-embed" import { createLoadingTask } from "vue3-pdfjs" import './style.css' import App from './App.vue' createApp(App) .use(router) // 使用路由 .mount('#app')
https://lbs.qq.com/webDemoCenter/glAPI/glMarker/markerAdd
https://lbs.qq.com/webDemoCenter/glAPI/glServiceLib/ipLocation
地图api地址:
v1:
https://wemap.qq.com/Vis/JavascriptAPI/APIDoc/map
v2:
https://lbs.qq.com/webApi/javascriptV2/jsGuide/jsOverview
同样是v2的:
https://lbs.qq.com/javascript_v2/doc/map.html
哲学管理(学)人生, 文学艺术生活, 自动(计算机学)物理(学)工作, 生物(学)化学逆境, 历史(学)测绘(学)时间, 经济(学)数学金钱(理财), 心理(学)医学情绪, 诗词美容情感, 美学建筑(学)家园, 解构建构(分析)整合学习, 智商情商(IQ、EQ)运筹(学)生存.---Geovin Du(涂聚文)