diff --git a/public/tools/map/empty.svg b/public/tools/map/empty.svg
new file mode 100644
index 000000000..c898711eb
--- /dev/null
+++ b/public/tools/map/empty.svg
@@ -0,0 +1,3 @@
+
+
\ No newline at end of file
diff --git a/public/tools/map/index.html b/public/tools/map/index.html
new file mode 100644
index 000000000..8c1a0c575
--- /dev/null
+++ b/public/tools/map/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/tools/map/main.js b/public/tools/map/main.js
new file mode 100644
index 000000000..5757475b7
--- /dev/null
+++ b/public/tools/map/main.js
@@ -0,0 +1,409 @@
+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 = '' + this.config.noresult + '';
+ 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 ? `${this.convertDistance(Math.round(poi._distance))}
` : '';
+ li.innerHTML = `
+ ${poi.title}
+ ${poi.address || ""}${distance}
+ `;
+ 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();
+});
diff --git a/public/tools/map/style.css b/public/tools/map/style.css
new file mode 100644
index 000000000..e9dee2029
--- /dev/null
+++ b/public/tools/map/style.css
@@ -0,0 +1,175 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html.theme-dark {
+ -webkit-filter: invert(100%) hue-rotate(180deg) contrast(90%) !important;
+ filter: invert(100%) hue-rotate(180deg) contrast(90%) !important;
+}
+
+body {
+ font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
+ font-size: 14px;
+ color: #303133;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.search-box {
+ padding: 10px;
+ background: #fff;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ position: relative;
+ z-index: 100;
+}
+
+.search-input-wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #EEF1F2;
+ border-radius: 6px;
+ height: 40px;
+}
+
+.search-icon {
+ margin: 0 10px 0 12px;
+ width: 20px;
+ height: 20px;
+ pointer-events: none;
+}
+
+#search-input {
+ flex: 1;
+ width: 0;
+ padding: 8px 8px 8px 0;
+ font-size: 15px;
+ border: 0;
+ background-color: transparent;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ outline: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-user-select: text;
+}
+
+#map-container {
+ flex: 1;
+ min-height: 240px;
+}
+
+.address-list {
+ display: none;
+ min-height: 300px;
+ overflow-y: auto;
+ background: #fff;
+}
+
+.address-list h3 {
+ font-size: 18px;
+ font-weight: bold;
+ padding: 12px 14px 10px;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ background-color: #ffffff;
+}
+
+#poi-list {
+ list-style: none;
+}
+
+#poi-list li {
+ padding: 10px 20px;
+ cursor: pointer;
+ position: relative;
+}
+
+#poi-list li::after {
+ content: "";
+ display: block;
+ height: 0;
+ clear: both;
+}
+
+#poi-list li::before {
+ content: "";
+ position: absolute;
+ left: 20px;
+ right: 0;
+ bottom: 0;
+ height: 1px;
+ background-color: #eee;
+ transform: scaleY(0.5);
+}
+
+#poi-list li:hover {
+ background: #f5f5f5;
+}
+
+.address-name {
+ font-size: 16px;
+ font-weight: 500;
+ margin-bottom: 5px;
+ line-height: 1.5;
+}
+
+.address-detail {
+ font-size: 13px;
+ color: #666;
+ line-height: 1.5;
+}
+.address-distance {
+ font-size: 13px;
+ color: #999;
+ float: right;
+ padding-left: 12px;
+}
+.loading {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 1000;
+ background: rgba(0, 0, 0, 0.6);
+ padding: 15px;
+ border-radius: 10px;
+ display: none;
+}
+
+.loading-spinner {
+ width: 30px;
+ height: 30px;
+ border: 2px solid transparent;
+ border-top-color: #fff;
+ border-left-color: #fff;
+ border-radius: 50%;
+ animation: spin 0.8s linear infinite;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+.loading.show {
+ display: block;
+}
diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js
index a68977951..7df93ad0d 100644
--- a/resources/assets/js/store/actions.js
+++ b/resources/assets/js/store/actions.js
@@ -970,16 +970,14 @@ export default {
},
/**
- * 获取定位(App)
+ * 打开地图选位置(App)
* @param dispatch
- * @param objects
+ * @param objects {{key: string, point: string}}
* @returns {Promise}
*/
openAppMapPage({dispatch}, objects) {
return new Promise(resolve => {
const params = {
- // key: "xxxxxx",
- // point: "116.404,39.925",
title: $A.L("签到地点"),
label: $A.L("选择附近地点"),
placeholder: $A.L("搜索地点"),
@@ -988,7 +986,7 @@ export default {
selectclose: "true",
channel: $A.randomString(6)
}
- const url = $A.urlAddParams($A.eeuiAppRewriteUrl('../public/tools/map/index.html'), Object.assign(params, objects))
+ const url = $A.urlAddParams($A.mainUrl("tools/map/index.html"), Object.assign(params, objects))
dispatch('openAppChildPage', {
pageType: 'app',
pageTitle: params.title,
diff --git a/resources/assets/statics/public/tools/map/main.js b/resources/assets/statics/public/tools/map/main.js
index 08c177175..5757475b7 100644
--- a/resources/assets/statics/public/tools/map/main.js
+++ b/resources/assets/statics/public/tools/map/main.js
@@ -7,6 +7,7 @@ class LocationPicker {
this.currentPoint = null;
this.loadNum = 0;
this.config = {
+ theme: 'light', // 主题风格,light|dark
key: null, // 百度地图 API Key
title: null, // 页面标题,如:选择打卡地点
label: null, // 搜索列表标签,如:附近的地点
@@ -20,8 +21,7 @@ class LocationPicker {
channel: null, // 回传数据通道
selectclose: false, // 选择地点是否关闭页面
};
- this.init().then(() => {
- });
+ this.init();
}
async init() {
@@ -73,6 +73,11 @@ class LocationPicker {
}
});
+ // 设置主题风格
+ document.documentElement.classList.add(`theme-${this.config.theme}`);
+ document.body.style.backgroundColor = "#ffffff";
+
+
// 设置标题
if (this.config.title) {
document.title = this.config.title;
diff --git a/resources/assets/statics/public/tools/map/style.css b/resources/assets/statics/public/tools/map/style.css
index 1f6b32cd9..e9dee2029 100644
--- a/resources/assets/statics/public/tools/map/style.css
+++ b/resources/assets/statics/public/tools/map/style.css
@@ -4,6 +4,11 @@
box-sizing: border-box;
}
+html.theme-dark {
+ -webkit-filter: invert(100%) hue-rotate(180deg) contrast(90%) !important;
+ filter: invert(100%) hue-rotate(180deg) contrast(90%) !important;
+}
+
body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px;