This commit is contained in:
zlwu committed 2026-07-04 08:21:06 +08:00
1 parent 8e70047f4c
commit b4a49183db
33 files changed
+11558 -5889

No files matched your search

+168
View File
@@ -0,0 +1,168 @@
declare global {
interface Window {
AMap?: AMapGlobal
_AMapSecurityConfig?: { securityJsCode: string }
}
}
export interface AMapLngLat {
lng: number
lat: number
getLng?: () => number
getLat?: () => number
}
export interface AMapPoi {
name?: string
address?: string
location?: AMapLngLat
district?: string
}
export interface AMapGlobal {
plugin: (names: string | string[], callback: () => void) => void
Map: new (container: HTMLElement | string, opts: Record<string, unknown>) => AMapMap
Marker: new (opts: Record<string, unknown>) => AMapMarker
AutoComplete: new (opts: Record<string, unknown>) => AMapAutoComplete
PlaceSearch: new (opts: Record<string, unknown>) => AMapPlaceSearch
Geocoder: new (opts?: Record<string, unknown>) => AMapGeocoder
}
export interface AMapMap {
on: (event: string, handler: (event: { lnglat: AMapLngLat }) => void) => void
destroy: () => void
resize: () => void
setCenter: (center: [number, number]) => void
setZoom: (zoom: number) => void
add: (overlay: unknown) => void
remove: (overlay: unknown) => void
}
export interface AMapMarker {
setPosition: (lnglat: [number, number] | AMapLngLat) => void
getPosition: () => AMapLngLat
on: (event: string, handler: () => void) => void
}
export interface AMapAutoComplete {
on: (event: string, handler: (event: { poi?: AMapPoi }) => void) => void
off: (event: string, handler: (event: { poi?: AMapPoi }) => void) => void
}
export interface AMapPlaceSearch {
search: (
keyword: string,
callback: (status: string, result: { poiList?: { pois?: AMapPoi[] } }) => void
) => void
}
export interface AMapGeocoder {
getAddress: (
lnglat: [number, number] | AMapLngLat,
callback: (status: string, result: { regeocode?: { formattedAddress?: string } }) => void
) => void
getLocation: (
address: string,
callback: (
status: string,
result: { geocodes?: Array<{ location?: { lng: number; lat: number } }> }
) => void
) => void
}
const AMAP_SCRIPT_ID = 'amap-js-sdk-v2'
const DEFAULT_PLUGINS = ['AMap.AutoComplete', 'AMap.PlaceSearch', 'AMap.Geocoder']
let loadPromise: Promise<AMapGlobal> | null = null
let pluginsReadyPromise: Promise<void> | null = null
export function getAmapKey() {
return String(import.meta.env.VITE_AMAP_KEY || '').trim()
}
export function getAmapSecurityCode() {
return String(import.meta.env.VITE_AMAP_SECURITY_CODE || '').trim()
}
export function isAmapConfigured() {
return !!getAmapKey()
}
function injectSecurityConfig() {
const securityJsCode = getAmapSecurityCode()
if (!securityJsCode) return
window._AMapSecurityConfig = { securityJsCode }
}
export function loadAmap(): Promise<AMapGlobal> {
const key = getAmapKey()
if (!key) {
return Promise.reject(new Error('未配置 VITE_AMAP_KEY'))
}
if (window.AMap) {
return Promise.resolve(window.AMap)
}
if (loadPromise) return loadPromise
injectSecurityConfig()
loadPromise = new Promise((resolve, reject) => {
const existing = document.getElementById(AMAP_SCRIPT_ID) as HTMLScriptElement | null
if (existing) {
existing.addEventListener('load', () => {
if (window.AMap) resolve(window.AMap)
else reject(new Error('高德地图 SDK 加载失败'))
})
existing.addEventListener('error', () => reject(new Error('高德地图 SDK 加载失败')))
return
}
const script = document.createElement('script')
script.id = AMAP_SCRIPT_ID
script.type = 'text/javascript'
script.async = true
script.src = `https://webapi.amap.com/maps?v=2.0&key=${encodeURIComponent(key)}`
script.onload = () => {
if (window.AMap) resolve(window.AMap)
else reject(new Error('高德地图 SDK 加载失败'))
}
script.onerror = () => reject(new Error('高德地图 SDK 加载失败'))
document.head.appendChild(script)
})
return loadPromise
}
export function loadAmapPlugins(plugins: string[] = DEFAULT_PLUGINS): Promise<void> {
if (pluginsReadyPromise) return pluginsReadyPromise
pluginsReadyPromise = loadAmap().then(
AMap =>
new Promise((resolve, reject) => {
const timer = window.setTimeout(() => {
pluginsReadyPromise = null
reject(new Error('高德地图插件加载超时'))
}, 15000)
AMap.plugin(plugins, () => {
window.clearTimeout(timer)
resolve()
})
})
)
return pluginsReadyPromise
}
export function resolveLngLat(lnglat: AMapLngLat): [number, number] {
const lng = typeof lnglat.getLng === 'function' ? lnglat.getLng() : lnglat.lng
const lat = typeof lnglat.getLat === 'function' ? lnglat.getLat() : lnglat.lat
return [lng, lat]
}
export function formatPoiAddress(poi?: AMapPoi) {
if (!poi) return ''
const parts = [poi.district, poi.address, poi.name].filter(Boolean)
return parts.join('').trim() || poi.name || poi.address || ''
}
+20
View File
@@ -0,0 +1,20 @@
/** 构建高德 URI 导航链接(手机点击可唤起高德 App) */
export function buildAmapNavigationUrl(
lng?: number | null,
lat?: number | null,
name?: string
): string | null {
if (lng == null || lat == null || Number.isNaN(Number(lng)) || Number.isNaN(Number(lat))) {
return null
}
const lngText = Number(lng).toFixed(6)
const latText = Number(lat).toFixed(6)
const toName = encodeURIComponent((name || '目的地').trim() || '目的地')
return `https://uri.amap.com/navigation?to=${lngText},${latText},${toName}&mode=car&coordinate=gaode&callnative=1`
}
export function appendAmapNavLine(lines: string[], label: string, url: string | null) {
if (url) {
lines.push(`${label}:${url}`)
}
}