提交
This commit is contained in:
1 parent
8e70047f4c
commit
b4a49183db
33 files changed
+11558
-5889
No files matched your search
@@ -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 || ''
|
||||
}
|
||||
@@ -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}`)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user