2024-11-07 23:07:37 +08:00

410 lines
13 KiB
JavaScript
Vendored
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class LocationPicker {
constructor() {
this.map = null;
this.marker = null;
this.geolocation = null;
this.localSearch = null;
this.currentPoint = null;
this.loadNum = 0;
this.config = {
theme: 'light', // 主题风格light|dark
key: null, // 百度地图 API Key
title: null, // 页面标题,如:选择打卡地点
label: null, // 搜索列表标签,如:附近的地点
placeholder: null, // 搜索框占位符,如:搜索附近的地点
point: null, // 初始坐标116.404,39.915
noresult: null, // 无搜索结果提示,如:附近没有找到地点
radius: 300, // 搜索半径单位300
zoom: 16, // 地图缩放级别
errtip: null, // 定位失败提示
errclose: false, // 定位失败是否关闭页面
channel: null, // 回传数据通道
selectclose: false, // 选择地点是否关闭页面
};
this.init();
}
async init() {
// 先初始化参数
this.initParams();
// 如果没有 key直接返回
if (!this.config.key) {
console.error('未提供百度地图 API Key');
return;
}
try {
// 等待地图 JS 加载完成
await this.loadBaiduMapScript();
// 初始化地图
this.initMap();
} catch (error) {
console.error('加载百度地图失败:', error);
}
}
initParams() {
// 获取当前URL的查询参数
const urlParams = new URLSearchParams(window.location.search);
// 遍历 config 对象的所有属性
Object.keys(this.config).forEach(key => {
// 从 URL 参数中获取值
const value = urlParams.get(key);
if (value !== null) {
// 根据参数类型进行转换
switch (key) {
case 'radius':
// 转换为数字
this.config[key] = parseInt(value) || 300;
break;
case 'point':
// 转换为坐标数组
const [lng, lat] = value.replace(/[|-]/, ',').split(',').map(parseFloat);
if (lng && lat) {
this.config[key] = {lng, lat};
}
break;
default:
// 字符串类型直接赋值
this.config[key] = value;
}
}
});
// 设置主题风格
document.documentElement.classList.add(`theme-${this.config.theme}`);
document.body.style.backgroundColor = "#ffffff";
// 设置标题
if (this.config.title) {
document.title = this.config.title;
}
// 设置搜索框占位符
if (this.config.placeholder) {
document.getElementById('search-input').placeholder = this.config.placeholder;
}
// 设置label
if (this.config.label) {
document.getElementById('address-label').innerText = this.config.label;
}
}
initMap() {
// 初始化地图
this.map = new BMap.Map('map-container');
// 创建定位控件
const locationControl = new BMap.GeolocationControl({
anchor: BMAP_ANCHOR_BOTTOM_RIGHT,
showAddressBar: false,
enableAutoLocation: false,
locationIcon: new BMap.Icon("empty.svg", new BMap.Size(0, 0))
});
// 监听定位事件
locationControl.addEventListener("locationSuccess", (e) => {
// 定位成功事件
this.updateCurrentPoint(e.point);
});
locationControl.addEventListener("locationError", (e) => {
// 定位失败事件
console.error('定位失败:', e.message);
this.locationError();
});
// 添加定位控件到地图
this.map.addControl(locationControl);
// 初始化本地搜索,移除地图渲染
this.localSearch = new BMap.LocalSearch(this.map, {
renderOptions: {
autoViewport: false // 关闭自动视野调整
}
});
// 设置地图中心点
if (this.config.point) {
const {lng, lat} = this.config.point;
this.config.point = new BMap.Point(lng, lat);
// 设置地图中心点和缩放级别
this.map.centerAndZoom(this.config.point, this.config.zoom);
// 创建圆形区域
const circle = new BMap.Circle(this.config.point, this.config.radius, {
fillColor: "#333333",
fillOpacity: 0.1,
strokeColor: "#333333",
strokeWeight: 1,
strokeOpacity: 0.3
});
this.map.addOverlay(circle);
}
// 绑定事件
this.bindEvents();
// 初始化时自动定位
this.getCurrentLocation();
}
bindEvents() {
const searchInput = document.getElementById('search-input');
// 监听回车键
searchInput.addEventListener('keyup', (e) => {
if (e.key === 'Enter') {
searchInput.blur();
}
});
// 监听失去焦点
searchInput.addEventListener('blur', () => {
this.searchAddress();
});
}
getCurrentLocation() {
this.loaderShow();
this.geolocation = new BMap.Geolocation();
this.geolocation.getCurrentPosition((result) => {
this.loaderHide();
if (result && result.point) {
this.updateCurrentPoint(result.point)
} else {
console.error('定位失败');
this.locationError();
}
}, {enableHighAccuracy: true});
}
updateCurrentPoint(point) {
this.currentPoint = point;
this.map.centerAndZoom(this.currentPoint, this.config.zoom);
this.updateMarker(this.currentPoint);
this.searchNearby();
}
updateMarker(point) {
if (this.marker) {
this.marker.setPosition(point);
} else {
this.marker = new BMap.Marker(point);
this.map.addOverlay(this.marker);
}
}
searchAddress() {
const keyword = document.getElementById('search-input').value;
this.searchNearby(keyword ? [keyword] : []);
}
searchNearby(keywords = [], retryCount = 0) {
// 当前位置未获取
if (this.currentPoint === null) {
return;
}
// 清除之前的搜索结果
this.localSearch.clearResults();
// 搜索附近的关键词
if (keywords.length === 0) {
keywords = ["写字楼", "公司", "银行", "餐馆", "商场", "超市", "学校", "医院", "公交站", "地铁站"]
}
// 定义一个随机数,用于判断定时器是否过期
this.searchRandom = Math.random();
// 设置搜索完成回调
this.loaderShow();
this.localSearch.setSearchCompleteCallback((results) => {
this.loaderHide();
if (this.localSearch.getStatus() !== BMAP_STATUS_SUCCESS) {
// 搜索失败
if (retryCount < 60) {
const tmpRand = this.searchRandom;
this.loaderShow();
setTimeout(() => {
this.loaderHide();
tmpRand === this.searchRandom && this.searchNearby(keywords, ++retryCount);
}, 1000)
return;
}
}
// 搜索结果
document.getElementById('address-list').style.display = 'block';
const array = [];
if (results instanceof Array) {
results.some(result => {
if (!result) {
return false;
}
for (let i = 0; i < result.getCurrentNumPois(); i++) {
const poi = result.getPoi(i);
poi._distance = this.config.point ? this.map.getDistance(this.config.point, poi.point) : null;
array.push(poi);
}
});
}
this.updatePoiList(array);
});
// 执行搜索
this.localSearch.searchNearby(keywords, this.currentPoint, this.config.radius);
}
updatePoiList(results) {
const poiList = document.getElementById('poi-list');
poiList.innerHTML = '';
// 如果没有搜索结果
if (results.length === 0 && this.config.noresult) {
poiList.innerHTML = '<li>' + this.config.noresult + '</li>';
return;
}
// 按距离排序(如果有距离信息)
results.sort((a, b) => {
if (a._distance && b._distance) {
return a._distance - b._distance;
}
return 0;
});
results = results.slice(0, 20);
// 创建列表项
results.forEach(poi => {
const li = document.createElement('li');
const distance = poi._distance ? `<div class="address-distance">${this.convertDistance(Math.round(poi._distance))}</div>` : '';
li.innerHTML = `
<div class="address-name">${poi.title}</div>
<div class="address-detail">${poi.address || ""}${distance}</div>
`;
li.addEventListener('click', () => {
const point = poi.point;
this.updateMarker(point);
this.map.setCenter(point);
//
if (typeof requireModuleJs === "function") {
const eeui = requireModuleJs("eeui");
eeui.setVariate("location::" + this.config.channel, JSON.stringify(poi))
}
if (this.config.selectclose) {
this.closePage();
}
});
poiList.appendChild(li);
});
// 列表更新后,重新将当前标记点居中显示
setTimeout(() => {
if (this.marker) {
this.map.setCenter(this.marker.getPosition());
}
}, 100); // 添加小延时确保DOM已更新
}
convertDistance(d) {
if (d > 1000) {
return (d / 1000).toFixed(1) + 'km';
}
return d.toFixed(0) + 'm';
}
locationError() {
if (this.config.errtip) {
alert(this.config.errtip);
}
if (this.config.errclose) {
this.closePage();
}
}
loaderShow() {
this.loadNum++;
this.loaderJudge();
}
loaderHide() {
setTimeout(() => {
this.loadNum--;
this.loaderJudge();
}, 100)
}
loaderJudge() {
if (this.loadNum > 0) {
document.querySelector('.loading').classList.add('show');
} else if (this.loadNum <= 0) {
document.querySelector('.loading').classList.remove('show');
}
}
closePage() {
try {
// 方法1: 如果是在 eeui 环境中
if (typeof requireModuleJs === "function") {
const eeui = requireModuleJs("eeui");
eeui.closePage();
}
// 方法2: 如果是从其他页面打开的,可以关闭当前窗口
window.close();
// 方法3: 如果是在 iOS WKWebView 中
try {
window.webkit.messageHandlers.closeWindow.postMessage(null);
} catch (e) {}
// 方法4: 如果是在 Android WebView 中
try {
window.android.closeWindow();
} catch (e) {}
// 方法5: 如果以上方法都失败,返回上一页
window.history.back();
} catch (e) {
console.error('关闭页面失败', e);
}
}
loadBaiduMapScript() {
return new Promise((resolve, reject) => {
// 如果已经加载过,直接返回
if (window.BMap) {
resolve();
return;
}
// 创建script标签
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://api.map.baidu.com/api?v=3.0&ak=${this.config.key}&callback=initBaiduMap`;
// 添加回调函数
window.initBaiduMap = () => {
resolve();
delete window.initBaiduMap;
};
// 处理加载错误
script.onerror = () => {
reject(new Error('百度地图脚本加载失败'));
};
// 添加到页面
document.body.appendChild(script);
});
}
}
// 初始化
document.addEventListener('DOMContentLoaded', () => {
new LocationPicker();
});