From d1a8d835fae20bcb332192991064aafbeedc7d04 Mon Sep 17 00:00:00 2001 From: cirry <812852553@qq.com> Date: Sun, 21 Jun 2026 22:20:12 +0800 Subject: [PATCH] =?UTF-8?q?=E9=AB=98=E5=BE=B7=E5=9C=B0=E5=9B=BE=E6=8E=A5?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 9 + package-lock.json | 7 + package.json | 1 + src/App.vue | 29 +++- src/composables/useAmap.ts | 124 ++++++++++++++ src/config/amap.ts | 22 +++ src/main.ts | 2 +- src/router/index.ts | 5 + src/views/MapView.vue | 332 +++++++++++++++++++++++++++++++++++++ src/vite-env.d.ts | 99 +++++++++++ tsconfig.app.json | 1 + 11 files changed, 628 insertions(+), 3 deletions(-) create mode 100644 .env create mode 100644 src/composables/useAmap.ts create mode 100644 src/config/amap.ts create mode 100644 src/views/MapView.vue diff --git a/.env b/.env new file mode 100644 index 0000000..38050b0 --- /dev/null +++ b/.env @@ -0,0 +1,9 @@ +# ============================================================ +# 高德地图配置 +# 请在 https://console.amap.com/dev/key/app 申请 +# ============================================================ +# 高德 JSAPI Key(用于地图展示、定位、搜索等前端功能) +VITE_AMAP_JSAPI_KEY=f71bdfbf074620937b6b127128f49086 + +# 高德 Web Service Key(用于服务端 API,如地理编码、路径规划等) +VITE_AMAP_WEB_KEY=6a30265609e6099d02eddc4e3085de5c diff --git a/package-lock.json b/package-lock.json index eb9b6d6..f8d23a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "microapp-vue3", "version": "0.0.0", "dependencies": { + "@amap/amap-jsapi-loader": "^1.0.1", "@micro-zoe/micro-app": "^1.0.0-rc.31", "vue": "^3.5.34", "vue-router": "^4.6.4" @@ -21,6 +22,12 @@ "vue-tsc": "^3.2.8" } }, + "node_modules/@amap/amap-jsapi-loader": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz", + "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==", + "license": "MIT" + }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", diff --git a/package.json b/package.json index 5d4509f..9bf6625 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@amap/amap-jsapi-loader": "^1.0.1", "@micro-zoe/micro-app": "^1.0.0-rc.31", "vue": "^3.5.34", "vue-router": "^4.6.4" diff --git a/src/App.vue b/src/App.vue index 2b380e3..3e3c5c5 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,6 +5,7 @@
@@ -18,12 +19,28 @@ diff --git a/src/composables/useAmap.ts b/src/composables/useAmap.ts new file mode 100644 index 0000000..004e898 --- /dev/null +++ b/src/composables/useAmap.ts @@ -0,0 +1,124 @@ +// ============================================================ +// 高德地图 Composable — 封装地图加载与实例管理 +// ============================================================ + +import { ref, shallowRef, onUnmounted } from 'vue' +import AMapLoader from '@amap/amap-jsapi-loader' +import { AMAP_JSAPI_KEY, AMAP_VERSION, AMAP_PLUGINS } from '@/config/amap' + +/** 全局加载状态:避免重复加载 JSAPI 脚本 */ +let amapPromise: Promise | null = null + +/** 高德地图 JSAPI 全局对象缓存 */ +let AMapGlobal: typeof AMap | null = null + +/** + * 加载高德地图 JSAPI(单例模式,全局只加载一次) + * 返回 AMap 全局对象 + */ +export async function loadAMap(): Promise { + if (AMapGlobal) return AMapGlobal + + if (!amapPromise) { + amapPromise = AMapLoader.load({ + key: AMAP_JSAPI_KEY, + version: AMAP_VERSION, + plugins: [...AMAP_PLUGINS], + }) + .then((amap) => { + AMapGlobal = amap + return amap + }) + .catch((err) => { + amapPromise = null // 失败后允许重试 + throw new Error(`高德地图 JSAPI 加载失败: ${err.message}`) + }) + } + + return amapPromise +} + +/** + * 地图 Composable — 管理地图实例的生命周期 + * + * @example + * ```vue + * + * + * + * ``` + */ +export function useAmap(options: AMap.MapOptions = {}) { + const containerRef = ref(null) + const mapInstance = shallowRef(null) + const loading = ref(false) + const error = ref(null) + + /** 初始化地图 */ + async function initMap(): Promise { + if (!containerRef.value) { + error.value = '地图容器不存在' + return null + } + + loading.value = true + error.value = null + + try { + const AMap = await loadAMap() + + if (mapInstance.value) { + mapInstance.value.destroy() + } + + const defaultOptions: AMap.MapOptions = { + center: [116.397428, 39.90923], // 默认:北京天安门 + zoom: 11, + viewMode: '3D', // 3D 模式 + resizeEnable: true, + } + + mapInstance.value = new AMap.Map(containerRef.value, { + ...defaultOptions, + ...options, + }) + + return mapInstance.value + } catch (err: any) { + error.value = err.message || '地图初始化失败' + return null + } finally { + loading.value = false + } + } + + /** 销毁地图实例 */ + function destroyMap(): void { + if (mapInstance.value) { + mapInstance.value.destroy() + mapInstance.value = null + } + } + + // 组件卸载时自动销毁地图 + onUnmounted(() => { + destroyMap() + }) + + return { + containerRef, // 模板绑定到地图容器 div + mapInstance, // 地图实例 + loading, // 加载状态 + error, // 错误信息 + initMap, // 初始化方法 + destroyMap, // 销毁方法 + } +} diff --git a/src/config/amap.ts b/src/config/amap.ts new file mode 100644 index 0000000..ef18ef2 --- /dev/null +++ b/src/config/amap.ts @@ -0,0 +1,22 @@ +// ============================================================ +// 高德地图配置 +// 使用 Vite 环境变量,必须以 VITE_ 前缀暴露给客户端 +// ============================================================ + +/** 高德地图 JSAPI Key — 用于前端地图展示和交互 */ +export const AMAP_JSAPI_KEY = import.meta.env.VITE_AMAP_JSAPI_KEY as string + +/** 高德地图 Web Service Key — 用于服务端 API 调用 */ +export const AMAP_WEB_KEY = import.meta.env.VITE_AMAP_WEB_KEY as string + +/** 高德地图 JSAPI 版本 */ +export const AMAP_VERSION = '2.0' + +/** 需要加载的高德地图插件列表 */ +export const AMAP_PLUGINS = [ + 'AMap.Geocoder', // 地理编码/逆地理编码 + 'AMap.AutoComplete', // 输入提示 + 'AMap.PlaceSearch', // 搜索服务 + 'AMap.Geolocation', // 定位 + 'AMap.MarkerClusterer', // 点聚合 +] as const diff --git a/src/main.ts b/src/main.ts index cdd03c4..d888d3b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -44,7 +44,7 @@ function unmount(): void { if (window.__MICRO_APP_ENVIRONMENT__) { // ✅ 运行在微前端环境中 — 导出生命周期钩子 // micro-app 会通过 window[`micro-app-${appName}`] 找到并调用 mount/unmount - window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount } + ;(window as Record)[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount } console.log( `[microapp-vue3] 检测到微前端环境,应用名称: ${window.__MICRO_APP_NAME__}` diff --git a/src/router/index.ts b/src/router/index.ts index d202fb2..1ab2f4f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -10,6 +10,11 @@ const routes = [ path: '/about', name: 'about', component: () => import('@/views/About.vue') + }, + { + path: '/map', + name: 'map', + component: () => import('@/views/MapView.vue') } ] diff --git a/src/views/MapView.vue b/src/views/MapView.vue new file mode 100644 index 0000000..20d29a5 --- /dev/null +++ b/src/views/MapView.vue @@ -0,0 +1,332 @@ + + + + + diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 10b63a4..365b24f 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,5 +1,104 @@ /// +// ============================================================ +// Vite 环境变量类型声明 +// ============================================================ + +interface ImportMetaEnv { + readonly VITE_AMAP_JSAPI_KEY: string + readonly VITE_AMAP_WEB_KEY: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} + +// ============================================================ +// 高德地图 AMap 全局类型声明 +// ============================================================ + +/** 高德地图 JSAPI v2 命名空间 */ +declare namespace AMap { + interface MapOptions { + center?: [number, number] + zoom?: number + viewMode?: '2D' | '3D' + resizeEnable?: boolean + pitch?: number + [key: string]: any + } + + class Map { + constructor(container: HTMLElement | string, opts?: MapOptions) + setCenter(center: [number, number]): void + setZoom(zoom: number): void + setZoomAndCenter(zoom: number, center: [number, number]): void + resize(): void + add(overlay: any): void + remove(overlay: any): void + clearMap(): void + destroy(): void + on(event: string, handler: (...args: any[]) => void): void + off(event: string, handler: (...args: any[]) => void): void + } + + class Geocoder { + constructor(opts?: Record) + } + class AutoComplete { + constructor(opts?: Record) + } + class PlaceSearch { + constructor(opts?: Record) + } + /** 高德定位插件 — IP + 基站定位,精度低于浏览器 GPS */ + interface GeolocationOptions { + /** 是否使用高精度定位,默认 true */ + enableHighAccuracy?: boolean + /** 超时时间(毫秒),默认 Infinity */ + timeout?: number + /** 定位成功后是否调整地图视野到定位精度范围,默认 false */ + zoomToAccuracy?: boolean + /** 定位按钮的停靠位置:LT | RT | LB | RB */ + position?: 'LT' | 'RT' | 'LB' | 'RB' + /** 定位按钮偏移量 [x, y] */ + offset?: [number, number] + /** 是否显示定位按钮,默认 true */ + showButton?: boolean + /** 定位成功后是否将定位点作为地图中心,默认 true */ + panToLocation?: boolean + [key: string]: any + } + + interface GeolocationResult { + position: { lng: number; lat: number } + accuracy: number + formattedAddress: string + addressComponent: { + province: string + city: string + district: string + township: string + street: string + streetNumber: string + } + message?: string + } + + class Geolocation { + constructor(opts?: AMap.GeolocationOptions) + /** 获取当前位置 */ + getCurrentPosition( + callback: (status: 'complete' | 'error', result: AMap.GeolocationResult) => void + ): void + /** 取消定位请求 */ + cancelWatch(): void + } + class MarkerClusterer { + constructor(map: Map, markers: any[], opts?: Record) + } +} + declare module '*.vue' { import type { DefineComponent } from 'vue' const component: DefineComponent diff --git a/tsconfig.app.json b/tsconfig.app.json index c95c949..fd57ae5 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -7,6 +7,7 @@ "@/*": ["./src/*"] }, "baseUrl": ".", + "ignoreDeprecations": "6.0", /* Linting */ "noUnusedLocals": true,