vue集成高德地图API工具类封装
import axios, { AxiosInstance, AxiosResponse } from 'axios';// 高德地图 API 响应基础结构
interface AMapResponse {status: string;info: string;infocode: string;
}// 逆地理编码响应结构
interface RegeoResponse extends AMapResponse {regeocode: {formatted_address: string;addressComponent: {province: string;city: string;district: string;township: string;citycode: string;adcode: string;};pois?: Array<{ id: string; name: string; address: string }>;roads?: Array<{ id: string; name: string }>;roadinters?: Array<{ direction: string; distance: string }>;};
}// 地理编码响应结构
interface GeoResponse extends AMapResponse {geocodes: Array<{location: string;formatted_address: string;level: string;city: string;district: string;adcode: string;}>;
}// POI 搜索响应结构
interface POISearchResponse extends AMapResponse {pois: Array<{id: string;name: string;type: string;typecode: string;address: string;location: string;pname: string;cityname: string;adname: string;}>;
}// 输入提示响应结构
interface InputTipsResponse extends AMapResponse {tips: Array<{id: string;name: string;district: string;location: string;}>;
}// 距离计算响应结构
interface DistanceResponse extends AMapResponse {results: Array<{distance: string;duration: string;}>;
}// 工具类配置接口
interface AMapConfig {apiKey: string;baseUrl?: string;
}// 逆地理编码选项
interface RegeoOptions {radius?: number;extensions?: 'base' | 'all';poiType?: string;roadLevel?: number;batch?: boolean;
}// POI 搜索选项
interface POISearchOptions {page?: number;offset?: number;extensions?: 'base' | 'all';types?: string;
}class AMapService {private axiosInstance: AxiosInstance;private apiKey: string;private baseUrl: string;/*** 构造函数* @param config - 配置对象,包含 API Key 和可选的基础 URL*/constructor(config: AMapConfig) {this.apiKey = config.apiKey;this.baseUrl = config.baseUrl || 'https://restapi.amap.com/v3';// 初始化 Axios 实例this.axiosInstance = axios.create({baseURL: this.baseUrl,timeout: 10000,headers: {'Content-Type': 'application/json',},});}/*** 发起通用请求* @param endpoint - API 端点* @param params - 请求参数* @returns 请求结果* @private*/private async _request<T>(endpoint: string, params: Record<string, any> = {}): Promise<T> {const baseParams = {key: this.apiKey,output: 'JSON',};// 合并参数并移除空值const queryParams = { ...baseParams, ...params };Object.keys(queryParams).forEach((key) => {if (queryParams[key] === undefined || queryParams[key] === null) {delete queryParams[key];}});try {const response: AxiosResponse<T> = await this.axiosInstance.get(endpoint, { params: queryParams });const data = response.data;if (data.status !== '1') {throw new Error(`高德API错误: ${data.info} (错误码: ${data.infocode})`);}return data;} catch (error) {console.error(`高德地图请求失败 (${endpoint}):`, error);throw error;}}/*** 逆地理编码 - 根据经纬度获取地址信息* @param longitude - 经度* @param latitude - 纬度* @param options - 额外选项* @returns 地址信息*/async regeoCode(longitude: number, latitude: number, options: RegeoOptions = {}): Promise<{province: string;city: string;district: string;township: string;citycode: string;adcode: string;formattedAddress: string;pois: Array<{ id: string; name: string; address: string }>;roads: Array<{ id: string; name: string }>;roadinters: Array<{ direction: string; distance: string }>;rawData: RegeoResponse;}> {const params = {location: `${longitude},${latitude}`,radius: options.radius || 1000,extensions: options.extensions || 'base',poitype: options.poiType,roadlevel: options.roadLevel || 0,batch: options.batch || false,};const data = await this._request<RegeoResponse>('geocode/regeo', params);if (data.regeocode) {const addressComponent = data.regeocode.addressComponent;return {province: addressComponent.province,city: addressComponent.city || addressComponent.province, // 处理直辖市district: addressComponent.district,township: addressComponent.township,citycode: addressComponent.citycode,adcode: addressComponent.adcode,formattedAddress: data.regeocode.formatted_address,pois: data.regeocode.pois || [],roads: data.regeocode.roads || [],roadinters: data.regeocode.roadinters || [],rawData: data,};}throw new Error('未找到地址信息');}/*** 地理编码 - 根据地址描述获取经纬度* @param address - 地址描述* @param city - 城市限定(可选)* @returns 经纬度信息*/async geoCode(address: string, city: string | null = null): Promise<{longitude: number;latitude: number;formattedAddress: string;level: string;city: string;district: string;adcode: string;rawData: GeoResponse['geocodes'][0];}> {const params = { address, city };const data = await this._request<GeoResponse>('geocode/geo', params);if (data.geocodes && data.geocodes.length > 0) {const geocode = data.geocodes[0];const [longitude, latitude] = geocode.location.split(',').map(Number);return {longitude,latitude,formattedAddress: geocode.formatted_address,level: geocode.level,city: geocode.city,district: geocode.district,adcode: geocode.adcode,rawData: geocode,};}throw new Error('未找到对应的地理位置');}/*** 关键字搜索 POI(兴趣点)* @param keywords - 关键字* @param city - 城市限定* @param options - 额外选项* @returns POI 列表*/async searchPOI(keywords: string, city: string | null = null, options: POISearchOptions = {}): Promise<Array<{id: string;name: string;type: string;typecode: string;address: string;location: { longitude: number; latitude: number };pname: string;cityname: string;adname: string;rawData: POISearchResponse['pois'][0];}>> {const params = {keywords,city: city || '全国',page: options.page || 1,offset: options.offset || 20,extensions: options.extensions || 'base',types: options.types,};const data = await this._request<POISearchResponse>('place/text', params);if (data.pois && data.pois.length > 0) {return data.pois.map((poi) => ({id: poi.id,name: poi.name,type: poi.type,typecode: poi.typecode,address: poi.address,location: {longitude: parseFloat(poi.location.split(',')[0]),latitude: parseFloat(poi.location.split(',')[1]),},pname: poi.pname,cityname: poi.cityname,adname: poi.adname,rawData: poi,}));}return [];}/*** 输入提示(自动完成)* @param keywords - 关键词* @param city - 城市限定* @returns 提示列表*/async inputTips(keywords: string, city: string | null = null): Promise<Array<{id: string;name: string;district: string;location: string;}>> {const params = {keywords,city: city || '全国',type: 'all',};const data = await this._request<InputTipsResponse>('assistant/inputtips', params);return data.tips || [];}/*** 批量逆地理编码(最多 20 个点)* @param locations - 经纬度数组 [{longitude, latitude}, ...]* @returns 批量结果*/async batchRegeoCode(locations: Array<{ longitude: number; latitude: number }>): Promise<RegeoResponse['regeocode'][]> {if (!Array.isArray(locations) || locations.length === 0) {throw new Error('位置数组不能为空');}if (locations.length > 20) {throw new Error('批量查询最多支持 20 个点');}const locationStr = locations.map((loc) => `${loc.longitude},${loc.latitude}`).join('|');const data = await this._request<RegeoResponse>('geocode/regeo', {location: locationStr,batch: true,});return data.regeocodes || [];}/*** 计算两点间距离* @param lng1 - 起点经度* @param lat1 - 起点纬度* @param lng2 - 终点经度* @param lat2 - 终点纬度* @returns 距离信息(米)*/async calculateDistance(lng1: number, lat1: number, lng2: number, lat2: number): Promise<{distance: number;duration: number;}> {const data = await this._request<DistanceResponse>('distance', {origins: `${lng1},${lat1}`,destination: `${lng2},${lat2}`,type: 1, // 直线距离});if (data.results && data.results.length > 0) {return {distance: parseInt(data.results[0].distance),duration: parseInt(data.results[0].duration),};}throw new Error('距离计算失败');}
}// 单例模式
let amapInstance: AMapService | null = null;/*** 初始化高德地图服务* @param apiKey - API Key* @returns 服务实例*/
export function initAMapService(apiKey: string): AMapService {if (!amapInstance) {amapInstance = new AMapService({ apiKey });}return amapInstance;
}/*** 获取高德地图服务实例* @returns 服务实例*/
export function getAMapService(): AMapService {if (!amapInstance) {throw new Error('请先调用 initAMapService 初始化');}return amapInstance;
}export default AMapService;
使用说明
- 申请 API Key:在高德地图开放平台(https://lbs.amap.com/)注册并申请 Web 服务 API Key。
- 安装依赖:
- 确保项目中已安装 axios 和 TypeScript:npm install axios typescript.
- 在 tsconfig.json 中启用 esModuleInterop 和 strict 选项以确保类型安全。
- 初始化服务:
typescript
import { initAMapService } from './AMapService';const amapService = initAMapService('您的API_KEY');
- 功能调用示例:
- 逆地理编码:
typescript
const addressInfo = await amapService.regeoCode(116.480881, 39.989410, { extensions: 'all' }); console.log(addressInfo.formattedAddress, addressInfo.addressComponent);
- 地理编码:
typescript
const geoInfo = await amapService.geoCode('北京市朝阳区望京街', '北京'); console.log(geoInfo.longitude, geoInfo.latitude);
- POI 搜索:
typescript
const pois = await amapService.searchPOI('咖啡', '北京', { page: 1, offset: 10 }); console.log(pois);
- 输入提示:
typescript
const tips = await amapService.inputTips('故宫', '北京'); console.log(tips);
- 批量逆地理编码:
typescript
const locations = [{ longitude: 116.480881, latitude: 39.989410 },{ longitude: 116.481026, latitude: 39.989614 }, ]; const batchResult = await amapService.batchRegeoCode(locations); console.log(batchResult);
- 距离计算:
typescript
const distanceInfo = await amapService.calculateDistance(116.480881, 39.989410, 116.481026, 39.989614); console.log(distanceInfo.distance, distanceInfo.duration);
- 逆地理编码: