From 4a9ed730c6f8afbd613d8490860e93d0d30858d2 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Thu, 7 Nov 2024 22:51:12 +0800 Subject: [PATCH] no message --- public/tools/map/empty.svg | 3 + public/tools/map/index.html | 31 ++ public/tools/map/main.js | 409 ++++++++++++++++++ public/tools/map/style.css | 175 ++++++++ resources/assets/js/store/actions.js | 8 +- .../assets/statics/public/tools/map/main.js | 9 +- .../assets/statics/public/tools/map/style.css | 5 + 7 files changed, 633 insertions(+), 7 deletions(-) create mode 100644 public/tools/map/empty.svg create mode 100644 public/tools/map/index.html create mode 100644 public/tools/map/main.js create mode 100644 public/tools/map/style.css 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;