import amapFile from '../libs/amap-wx.130.js';
|
import { AMAP_KEY, _ROBOTAXI_HOST } from '../constants/api';
|
import Taro from '@tarojs/taro';
|
import request from './request';
|
import { getFenceCheck, getNearestSite } from '../robotaxi/api/home';
|
import { getOpenCity } from '../robotaxi/api/select-city';
|
|
// 辅助函数:计算两点之间的距离(单位:米)
|
export const getDistance = (lat1: number, lng1: number, lat2: number, lng2: number) => {
|
const radLat1 = lat1 * Math.PI / 180.0;
|
const radLat2 = lat2 * Math.PI / 180.0;
|
const a = radLat1 - radLat2;
|
const b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
|
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
|
s = s * 6378.137; // 地球半径
|
s = Math.round(s * 10000) / 10000;
|
return s * 1000;
|
};
|
|
// 格式化距离
|
export const formatDistance = (val: any) => {
|
if (!val) return '0m';
|
const d = Number(val);
|
if (Number.isNaN(d)) return val;
|
if (d >= 1000) {
|
return (d / 1000).toFixed(2) + 'km';
|
}
|
return d.toFixed(2) + 'm';
|
}
|
|
// 查找最近的标记点
|
export const findNearestMarker = (lat: number, lng: number, markers: any[]) => {
|
let minDistance = Infinity;
|
let targetMarker: any = null;
|
|
markers.forEach(marker => {
|
const distance = getDistance(lat, lng, marker.latitude, marker.longitude);
|
if (distance < minDistance) {
|
minDistance = distance;
|
targetMarker = marker;
|
}
|
});
|
|
return { targetMarker, minDistance };
|
};
|
|
// 解析路线数据为坐标点数组
|
export const parseRouteToPoints = (routeData: any) => {
|
const points: any[] = [];
|
if (routeData.paths && routeData.paths[0] && routeData.paths[0].steps) {
|
const steps = routeData.paths[0].steps;
|
for (let i = 0; i < steps.length; i++) {
|
const poLen = steps[i].polyline.split(';');
|
for (let j = 0; j < poLen.length; j++) {
|
points.push({
|
longitude: parseFloat(poLen[j].split(',')[0]),
|
latitude: parseFloat(poLen[j].split(',')[1])
|
})
|
}
|
}
|
}
|
return points;
|
};
|
|
// 格式化站点为标记点
|
export const formatSiteToMarker = (sites: any[]) => {
|
return sites.map((site, index) => ({
|
...site,
|
// id: index + 1,
|
iconPath: 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/a2b43790-9317-4b3b-b29f-d82cc10830e9.png',
|
width: 15,
|
height: 15,
|
anchor: { x: 0.5, y: 0.5 }
|
}));
|
};
|
|
// 获取位置的逆地理编码
|
export const getAddressByLocation = (longitude: number, latitude: number): Promise<any> => {
|
return new Promise((resolve, reject) => {
|
const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
|
myAmapFun.getRegeo({
|
location: `${longitude},${latitude}`,
|
success: (data) => {
|
if (data && data[0]) {
|
resolve(data[0]);
|
} else {
|
reject('No data found');
|
}
|
},
|
fail: (info) => {
|
reject(info);
|
}
|
});
|
});
|
};
|
|
// 格式化电子围栏数据
|
export const formatGeofencePolygons = (geofenceList: any[]) => {
|
if (!geofenceList || !Array.isArray(geofenceList)) {
|
return [];
|
}
|
return geofenceList.map(item => {
|
return {
|
points: item.points.map(p => ({
|
latitude: p.lat,
|
longitude: p.lng
|
})),
|
strokeWidth: 2,
|
strokeColor: '#A1B1FF',
|
fillColor: '#6d7fd933',
|
zIndex: 1
|
}
|
});
|
};
|
|
// 提取多边形所有顶点
|
export const extractPointsFromPolygons = (polygons: any[]) => {
|
const points: any[] = [];
|
if (!polygons || !Array.isArray(polygons)) return points;
|
|
polygons.forEach(polygon => {
|
if (polygon.points && Array.isArray(polygon.points)) {
|
points.push(...polygon.points);
|
}
|
});
|
return points;
|
};
|
|
// 转换 adcode 为 cityCode
|
export const convertAdcodeToCityCode = (adcode: string, cityName: string, province: string) => {
|
let cityCode = adcode;
|
if (adcode && typeof adcode === 'string' && adcode.length === 6) {
|
if (cityName === province) {
|
// 直辖市:前2位 + 0000
|
cityCode = adcode.substring(0, 2) + '0000';
|
} else {
|
// 普通城市:前4位 + 00
|
cityCode = adcode.substring(0, 4) + '00';
|
}
|
}
|
return cityCode;
|
};
|
|
// 处理 Banner 点击跳转
|
export const handleBannerJump = (item: any) => {
|
if (!item) return;
|
const { jumpType, jumpUrl } = item;
|
// jumpType: 1小程序内页面 2小程序地址 3网页地址
|
if (jumpType === 1) {
|
if (jumpUrl) {
|
Taro.navigateTo({
|
url: jumpUrl
|
});
|
}
|
} else if (jumpType === 2) {
|
if (jumpUrl) {
|
Taro.navigateToMiniProgram({
|
appId: jumpUrl,
|
// path: '',
|
success(res) {
|
console.log('打开小程序成功', res);
|
},
|
fail(err) {
|
console.error('打开小程序失败', err);
|
Taro.showToast({ title: '跳转失败', icon: 'none' });
|
}
|
});
|
}
|
} else if (jumpType === 3) {
|
if (jumpUrl) {
|
Taro.navigateTo({
|
url: `/pages/webpage/index?pageUrl=${encodeURIComponent(jumpUrl)}`
|
});
|
}
|
}
|
};
|
|
// 防抖函数
|
export const debounce = (fn: Function, wait: number) => {
|
let timer: any = null;
|
return function (this: any, ...args: any[]) {
|
if (timer) clearTimeout(timer);
|
timer = setTimeout(() => {
|
fn.apply(this, args);
|
}, wait);
|
}
|
}
|
|
// 节流函数
|
export const throttle = (fn: Function, wait: number) => {
|
let _lastTime: any = null;
|
return function (this: any, ...args: any[]) {
|
let _nowTime = +new Date();
|
if (!_lastTime || _nowTime - _lastTime > wait) {
|
fn.apply(this, args);
|
_lastTime = _nowTime;
|
}
|
}
|
}
|
|
// 预下载图片,返回本地临时路径(WeApp),失败或非WeApp环境返回原始URL
|
export const preloadImageToTemp = async (url: string): Promise<string> => {
|
try {
|
if (typeof Taro.getEnv !== 'function' || Taro.getEnv() !== Taro.ENV_TYPE.WEAPP) {
|
return url;
|
}
|
const res = await Taro.downloadFile({ url });
|
if (res?.statusCode === 200 && res.tempFilePath) {
|
return res.tempFilePath;
|
}
|
const info = await Taro.getImageInfo({ src: url });
|
if (info?.path) {
|
return info.path;
|
}
|
} catch (e) {
|
}
|
return url;
|
}
|
|
// 获取地图数据(围栏检查+附近站点)
|
export const fetchMapData = async (lat: number, lng: number) => {
|
try {
|
// 1. 先检查是否在运营区域内
|
const fenceRes: any = await getFenceCheck({ latitude: lat, longitude: lng }).catch(e => {
|
return { status: 'inside' }; // 默认兜底
|
});
|
|
const isInside = fenceRes.status === 'inside';
|
const electronicAreaId = fenceRes?.electronicAreaId || '';
|
|
// 2. 只有在运营区域内才获取附近站点
|
let sites: any = { points: [] };
|
if (isInside) {
|
sites = await getNearestSite({ latitude: lat, longitude: lng }).catch(e => {
|
return { points: [] };
|
});
|
// 兼容处理
|
if (!sites || !sites.points) {
|
sites = { points: [] };
|
}
|
}
|
|
const newMarkers = formatSiteToMarker(sites.points);
|
|
return { isInside, electronicAreaId, newMarkers, sites: sites.points };
|
} catch (error) {
|
console.error('fetchMapData error', error);
|
return { isInside: true, electronicAreaId: '', newMarkers: [], sites: [] };
|
}
|
}
|
|
// 获取步行规划路线
|
export const getWalkingRoute = (origin: string, destination: string): Promise<{ points: any[], duration: number, distance: number }> => {
|
return new Promise((resolve, reject) => {
|
const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
|
myAmapFun.getWalkingRoute({
|
origin: origin,
|
destination: destination,
|
success: (data) => {
|
const points = parseRouteToPoints(data);
|
let duration = 0;
|
let distance = 0;
|
if (data.paths && data.paths[0]) {
|
duration = Number(data.paths[0].duration);
|
distance = Number(data.paths[0].distance);
|
}
|
resolve({ points, duration, distance });
|
},
|
fail: (info) => {
|
reject(info);
|
}
|
});
|
});
|
};
|
|
// 获取城市中心经纬度
|
export const getCityLocation = (cityName: string): Promise<{ latitude: number, longitude: number }> => {
|
return new Promise((resolve, reject) => {
|
const myAmapFun = new amapFile.AMapWX({ key: AMAP_KEY });
|
myAmapFun.getInputtips({
|
keywords: cityName,
|
city: cityName,
|
success: (data) => {
|
if (data && data.tips && data.tips.length > 0) {
|
// 优先取第一个有坐标的结果
|
const tip = data.tips.find(t => t.location && t.location.length > 0);
|
if (tip) {
|
const locationArr = tip.location.split(',');
|
if (locationArr.length === 2) {
|
resolve({
|
longitude: Number(locationArr[0]),
|
latitude: Number(locationArr[1])
|
});
|
return;
|
}
|
}
|
reject('No valid location found');
|
} else {
|
reject('No data found');
|
}
|
},
|
fail: (info) => {
|
reject(info);
|
}
|
});
|
});
|
};
|
|
// 获取包含 ID 的城市信息
|
export const getCityInfoWithId = async (cityCode: string, cityName: string) => {
|
try {
|
const res: any = await getOpenCity();
|
|
let cityId = 0;
|
if (Array.isArray(res.cities)) {
|
const target = res.cities.find(item => item.adcode === cityCode);
|
if (target) {
|
cityId = target.id;
|
}
|
}
|
return {
|
name: cityName,
|
code: cityCode,
|
id: cityId
|
};
|
} catch (error) {
|
console.error('getOpenCity error', error);
|
return {
|
name: cityName,
|
code: cityCode,
|
id: 0
|
};
|
}
|
};
|
|
// 格式化秒数为时钟时间(分钟:秒)
|
export const formatDurationToClock = (sec: number) => {
|
const safeSeconds = Math.max(0, sec);
|
const m = Math.floor(safeSeconds / 60);
|
const s = safeSeconds % 60;
|
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
|
};
|
|
// 格式化日期时间
|
export const formatDateTime = (date: Date) => {
|
const y = date.getFullYear();
|
const mo = String(date.getMonth() + 1).padStart(2, '0');
|
const d = String(date.getDate()).padStart(2, '0');
|
const h = String(date.getHours()).padStart(2, '0');
|
const mi = String(date.getMinutes()).padStart(2, '0');
|
const s = String(date.getSeconds()).padStart(2, '0');
|
return `${y}-${mo}-${d} ${h}:${mi}:${s}`;
|
};
|
|
// 清除订单行程存储
|
export const clearOrderTripStorage = () => {
|
Taro.removeStorageSync('orderId');
|
Taro.removeStorageSync('tripStatus');
|
};
|
|
/**
|
* 通过 IP 获取当前位置信息(用于定位授权失败时的兜底)
|
*/
|
export const getLocationByIP = (): Promise<any> => {
|
return new Promise(async (resolve, reject) => {
|
try {
|
// 1. 获取当前 network IP
|
// 微信小程序没有直接获取公网 IP 的接口,通常由后端从请求头中获取,或者通过第三方接口获取
|
// 这里模拟调用服务端提供的 IP 定位接口,假设接口会自动识别请求来源 IP
|
const res: any = await request({
|
url: _ROBOTAXI_HOST + '/map/base/v1/mini-program/point/getPointByIp',
|
method: 'GET',
|
});
|
|
if (res && res.point && typeof res.point.latitude === 'number' && typeof res.point.longitude === 'number') {
|
resolve({
|
longitude: res.point.longitude,
|
latitude: res.point.latitude,
|
city: res.city || '',
|
adcode: res.adcode || ''
|
});
|
} else {
|
// 如果后端接口不可用或未返回数据,尝试使用第三方接口作为备选获取 IP
|
Taro.request({
|
url: 'https://api.ipify.org?format=json',
|
success: (ipRes) => {
|
const ip = ipRes.data?.ip;
|
if (ip) {
|
// 拿着获取到的 IP 去请求服务端接口
|
request({
|
url: _ROBOTAXI_HOST + '/map/base/v1/mini-program/point/getPointByIp',
|
method: 'POST',
|
payload: { ip }
|
}).then((businessRes: any) => {
|
if (businessRes && businessRes.point && typeof businessRes.point.latitude === 'number' && typeof businessRes.point.longitude === 'number') {
|
resolve({
|
longitude: businessRes.point.longitude,
|
latitude: businessRes.point.latitude,
|
city: businessRes.city || '',
|
adcode: businessRes.adcode || ''
|
});
|
} else {
|
reject('Business IP location failed');
|
}
|
}).catch(reject);
|
} else {
|
reject('Failed to get network IP');
|
}
|
},
|
fail: reject
|
});
|
}
|
} catch (error) {
|
reject(error);
|
}
|
});
|
};
|