perf: 签到新增高德和腾讯地图

This commit is contained in:
kuaifan 2025-07-28 06:22:20 +08:00
parent 8c9c1c5afa
commit 4c075b4d11
8 changed files with 1505 additions and 62 deletions

View File

@ -481,8 +481,13 @@ class SystemController extends AbstractController
'face_remark',
'face_retip',
'locat_remark',
'locat_map_type',
'locat_bd_lbs_key',
'locat_bd_lbs_point', // 格式:{"lng":116.404, "lat":39.915, "radius":500}
'locat_amap_key',
'locat_amap_point', // 格式:{"lng":116.404, "lat":39.915, "radius":500}
'locat_tencent_key',
'locat_tencent_point', // 格式:{"lng":116.404, "lat":39.915, "radius":500}
'manual_remark',
'modes',
'key',
@ -500,14 +505,25 @@ class SystemController extends AbstractController
}
if (is_array($all['modes'])) {
if (in_array('locat', $all['modes'])) {
if (empty($all['locat_bd_lbs_key'])) {
return Base::retError('请填写百度地图AK');
$mapTypes = [
'baidu' => ['key' => 'locat_bd_lbs_key', 'point' => 'locat_bd_lbs_point', 'msg' => '请填写百度地图AK'],
'amap' => ['key' => 'locat_amap_key', 'point' => 'locat_amap_point', 'msg' => '请填写高德地图Key'],
'tencent' => ['key' => 'locat_tencent_key', 'point' => 'locat_tencent_point', 'msg' => '请填写腾讯地图Key'],
];
$type = $all['locat_map_type'];
if (!isset($mapTypes[$type])) {
return Base::retError('请选择地图类型');
}
if (!is_array($all['locat_bd_lbs_point'])) {
$conf = $mapTypes[$type];
if (empty($all[$conf['key']])) {
return Base::retError($conf['msg']);
}
if (!is_array($all[$conf['point']])) {
return Base::retError('请选择允许签到位置');
}
$all['locat_bd_lbs_point']['radius'] = intval($all['locat_bd_lbs_point']['radius']);
if (empty($all['locat_bd_lbs_point']['lng']) || empty($all['locat_bd_lbs_point']['lat']) || empty($all['locat_bd_lbs_point']['radius'])) {
$all[$conf['point']]['radius'] = intval($all[$conf['point']]['radius']);
$point = $all[$conf['point']];
if (empty($point['lng']) || empty($point['lat']) || empty($point['radius'])) {
return Base::retError('请选择有效的签到位置');
}
}
@ -539,7 +555,10 @@ class SystemController extends AbstractController
$setting['face_remark'] = $setting['face_remark'] ?: Doo::translate('考勤机');
$setting['face_retip'] = $setting['face_retip'] ?: 'open';
$setting['locat_remark'] = $setting['locat_remark'] ?: Doo::translate('定位签到');
$setting['locat_map_type'] = $setting['locat_map_type'] ?: 'baidu';
$setting['locat_bd_lbs_point'] = is_array($setting['locat_bd_lbs_point']) ? $setting['locat_bd_lbs_point'] : ['radius' => 500];
$setting['locat_amap_point'] = is_array($setting['locat_amap_point']) ? $setting['locat_amap_point'] : ['radius' => 500];
$setting['locat_tencent_point'] = is_array($setting['locat_tencent_point']) ? $setting['locat_tencent_point'] : ['radius' => 500];
$setting['manual_remark'] = $setting['manual_remark'] ?: Doo::translate('手动签到');
$setting['time'] = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
$setting['advance'] = intval($setting['advance']) ?: 120;

View File

@ -0,0 +1,286 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
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;
}
#map-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
<script>
class AmapPickup {
constructor(containerId) {
this.containerId = containerId;
this.map = null;
this.circle = null;
this.marker = null;
this.centerPoint = null;
this.isFirstClick = true;
this.params = {
theme: 'light', // 主题风格light|dark
key: null, // 高德地图 API Key
point: null, // 初始坐标116.404,39.915
radius: 300, // 圆形半径
zoom: 16, // 地图缩放级别
}
this.init();
}
async init() {
// 先初始化参数
this.initParams();
// 如果没有 key直接返回
if (!this.params.key) {
console.error('未提供高德地图 API Key');
return;
}
try {
// 等待地图 JS 加载完成
await this.loadMapScript();
// 初始化地图
this.initMap();
} catch (error) {
console.error('加载高德地图失败:', error);
}
}
initParams() {
// 获取当前URL的查询参数
const urlParams = new URLSearchParams(window.location.search);
// 遍历 params 对象的所有属性
Object.keys(this.params).forEach(key => {
// 从 URL 参数中获取值
const value = urlParams.get(key);
if (value !== null) {
// 根据参数类型进行转换
switch (key) {
case 'radius':
// 转换为数字
this.params[key] = parseInt(value) || 300;
break;
case 'point':
// 转换为坐标数组
const [lng, lat] = value.replace(/[|-]/, ',').split(',').map(parseFloat);
if (lng && lat) {
this.params[key] = {lng, lat};
}
break;
default:
// 字符串类型直接赋值
this.params[key] = value;
}
}
});
// 设置主题风格
if (!['dark', 'light'].includes(this.params.theme)) {
this.params.theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.classList.add(`theme-${this.params.theme}`);
document.body.style.backgroundColor = "#ffffff";
}
// 初始化地图
initMap() {
this.map = new AMap.Map(this.containerId, {
zoom: this.params.zoom,
center: this.params.point ? [this.params.point.lng, this.params.point.lat] : [116.404, 39.915],
enableMapClick: true
});
if (this.params.point) {
this.centerPoint = [this.params.point.lng, this.params.point.lat];
this.handleMapClick({lnglat: {lng: this.params.point.lng, lat: this.params.point.lat}});
} else {
this.centerPoint = [116.404, 39.915];
}
this.bindEvents();
}
bindEvents() {
this.map.on('click', (e) => this.handleMapClick(e));
// 监听父页面消息
window.addEventListener('message', (event) => {
if (event.data.action === 'update_radius') {
const newRadius = parseInt(event.data.radius);
if (newRadius && newRadius >= 50 && newRadius <= 5000) {
this.params.radius = newRadius;
if (this.circle) {
this.updateMarkerAndCircle();
this.updateInfoPanel();
}
}
}
});
}
handleMapClick(e) {
this.centerPoint = [e.lnglat.lng, e.lnglat.lat];
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.params.radius = this.circle.getRadius();
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
}
createMarkerAndCircle() {
this.createMarker();
this.createCircle();
}
createMarker() {
if (this.marker) {
this.map.remove(this.marker);
}
this.marker = new AMap.Marker({
position: this.centerPoint,
map: this.map
});
}
createCircle() {
if (this.circle) {
this.map.remove(this.circle);
}
this.circle = new AMap.Circle({
center: this.centerPoint,
radius: this.params.radius,
strokeColor: "#FF0000",
strokeWeight: 2,
strokeOpacity: 0.5,
fillColor: "#FF0000",
fillOpacity: 0.2,
map: this.map
});
}
updateMarkerAndCircle() {
if (this.marker) {
this.map.remove(this.marker);
}
if (this.circle) {
this.map.remove(this.circle);
}
this.createMarker();
this.createCircle();
}
updateInfoPanel() {
const currentRadius = this.circle ? this.circle.getRadius().toFixed(0) : this.params.radius.toString();
const data = {
longitude: this.centerPoint[0].toFixed(6),
latitude: this.centerPoint[1].toFixed(6),
radius: currentRadius
}
// 发送消息给父页面
window.parent.postMessage(Object.assign(data, {
action: "amap_lbs_select_point",
}), "*");
}
// 获取当前选择的数据
getData() {
if (!this.circle) return null;
return {
center: {
lng: this.centerPoint[0],
lat: this.centerPoint[1]
},
radius: this.circle.getRadius()
};
}
// 设置中心点和半径
setData(lng, lat, radius) {
this.centerPoint = [lng, lat];
this.params.radius = radius || this.params.radius;
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
this.map.setCenter(this.centerPoint);
}
loadMapScript() {
return new Promise((resolve, reject) => {
// 如果已经加载过,直接返回
if (window.AMap) {
resolve();
return;
}
// 创建script标签
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://webapi.amap.com/maps?v=1.4.15&key=${this.params.key}&callback=initAmapMap`;
// 添加回调函数
window.initAmapMap = () => {
resolve();
delete window.initAmapMap;
};
// 处理加载错误
script.onerror = () => {
reject(new Error('高德地图脚本加载失败'));
};
// 添加到页面
document.body.appendChild(script);
});
}
}
// 页面加载完成后初始化
window.onload = function () {
window.pickup = new AmapPickup("map-container");
};
</script>
</head>
<body>
<div id="map-container"></div>
</body>
</html>

View File

@ -29,6 +29,8 @@
right: 0;
bottom: 0;
}
</style>
<script>
class Pickup {
@ -133,6 +135,20 @@
bindEvents() {
this.map.addEventListener("click", (e) => this.handleMapClick(e));
// 监听父页面消息
window.addEventListener('message', (event) => {
if (event.data.action === 'update_radius') {
const newRadius = parseInt(event.data.radius);
if (newRadius && newRadius >= 50 && newRadius <= 5000) {
this.params.radius = newRadius;
if (this.circle) {
this.updateMarkerAndCircle();
this.updateInfoPanel();
}
}
}
});
}
handleMapClick(e) {
@ -191,13 +207,16 @@
}
updateInfoPanel() {
const currentRadius = this.circle ? this.circle.getRadius().toFixed(0) : this.params.radius.toString();
const data = {
longitude: this.centerPoint.lng.toFixed(6),
latitude: this.centerPoint.lat.toFixed(6),
radius: this.circle ? this.circle.getRadius().toFixed(0) : '-'
radius: currentRadius
}
// 发送消息给父页面
window.parent.postMessage(Object.assign(data, {
action: "bd_lbs_select_point",
action: "baidu_lbs_select_point",
}), "*");
}

View File

@ -0,0 +1,297 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
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;
}
#map-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
<script>
class TencentPickup {
constructor(containerId) {
this.containerId = containerId;
this.map = null;
this.circle = null;
this.marker = null;
this.centerPoint = null;
this.isFirstClick = true;
this.params = {
theme: 'light', // 主题风格light|dark
key: null, // 腾讯地图 API Key
point: null, // 初始坐标116.404,39.915
radius: 300, // 圆形半径
zoom: 16, // 地图缩放级别
}
this.init();
}
async init() {
// 先初始化参数
this.initParams();
// 如果没有 key直接返回
if (!this.params.key) {
console.error('未提供腾讯地图 API Key');
return;
}
try {
// 等待地图 JS 加载完成
await this.loadMapScript();
// 初始化地图
this.initMap();
} catch (error) {
console.error('加载腾讯地图失败:', error);
}
}
initParams() {
// 获取当前URL的查询参数
const urlParams = new URLSearchParams(window.location.search);
// 遍历 params 对象的所有属性
Object.keys(this.params).forEach(key => {
// 从 URL 参数中获取值
const value = urlParams.get(key);
if (value !== null) {
// 根据参数类型进行转换
switch (key) {
case 'radius':
// 转换为数字
this.params[key] = parseInt(value) || 300;
break;
case 'point':
// 转换为坐标数组
const [lng, lat] = value.replace(/[|-]/, ',').split(',').map(parseFloat);
if (lng && lat) {
this.params[key] = {lng, lat};
}
break;
default:
// 字符串类型直接赋值
this.params[key] = value;
}
}
});
// 设置主题风格
if (!['dark', 'light'].includes(this.params.theme)) {
this.params.theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.classList.add(`theme-${this.params.theme}`);
document.body.style.backgroundColor = "#ffffff";
}
// 初始化地图
initMap() {
const center = this.params.point ?
new TMap.LatLng(this.params.point.lat, this.params.point.lng) :
new TMap.LatLng(39.915, 116.404);
this.map = new TMap.Map(this.containerId, {
center: center,
zoom: this.params.zoom
});
if (this.params.point) {
this.centerPoint = center;
this.handleMapClick({latLng: center});
} else {
this.centerPoint = center;
}
this.bindEvents();
}
bindEvents() {
this.map.on('click', (e) => this.handleMapClick(e));
// 监听父页面消息
window.addEventListener('message', (event) => {
if (event.data.action === 'update_radius') {
const newRadius = parseInt(event.data.radius);
if (newRadius && newRadius >= 50 && newRadius <= 5000) {
this.params.radius = newRadius;
if (this.circle) {
this.updateMarkerAndCircle();
this.updateInfoPanel();
}
}
}
});
}
handleMapClick(e) {
this.centerPoint = e.latLng;
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
}
createMarkerAndCircle() {
this.createMarker();
this.createCircle();
}
createMarker() {
if (this.marker) {
this.marker.setMap(null);
}
// 使用MultiMarker但不设置自定义样式使用默认样式
this.marker = new TMap.MultiMarker({
map: this.map,
geometries: [{
id: 'marker1',
position: this.centerPoint
}]
});
}
createCircle() {
if (this.circle) {
this.circle.setMap(null);
}
// 使用MultiCircle但不设置自定义样式使用默认样式
this.circle = new TMap.MultiCircle({
map: this.map,
geometries: [{
id: 'circle1',
center: this.centerPoint,
radius: this.params.radius
}]
});
}
updateMarkerAndCircle() {
if (this.marker) {
this.marker.updateGeometries([{
id: 'marker1',
position: this.centerPoint
}]);
}
if (this.circle) {
this.circle.updateGeometries([{
id: 'circle1',
center: this.centerPoint,
radius: this.params.radius
}]);
}
}
updateInfoPanel() {
const data = {
longitude: this.centerPoint.lng.toFixed(6),
latitude: this.centerPoint.lat.toFixed(6),
radius: this.params.radius.toString()
}
// 发送消息给父页面
window.parent.postMessage(Object.assign(data, {
action: "tencent_lbs_select_point",
}), "*");
}
// 获取当前选择的数据
getData() {
if (!this.circle) return null;
return {
center: {
lng: this.centerPoint.lng,
lat: this.centerPoint.lat
},
radius: this.params.radius
};
}
// 设置中心点和半径
setData(lng, lat, radius) {
this.centerPoint = new TMap.LatLng(lat, lng);
this.params.radius = radius || this.params.radius;
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
this.map.setCenter(this.centerPoint);
}
loadMapScript() {
return new Promise((resolve, reject) => {
// 如果已经加载过,直接返回
if (window.TMap) {
resolve();
return;
}
// 创建script标签
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://map.qq.com/api/gljs?v=1.exp&key=${this.params.key}&callback=initTencentMap`;
// 添加回调函数
window.initTencentMap = () => {
resolve();
delete window.initTencentMap;
};
// 处理加载错误
script.onerror = () => {
reject(new Error('腾讯地图脚本加载失败'));
};
// 添加到页面
document.body.appendChild(script);
});
}
}
// 页面加载完成后初始化
window.onload = function () {
window.pickup = new TencentPickup("map-container");
};
</script>
</head>
<body>
<div id="map-container"></div>
</body>
</html>

View File

@ -125,20 +125,71 @@
<FormItem :label="$L('签到备注')" prop="locat_remark">
<Input :maxlength="30" v-model="formData.locat_remark"/>
</FormItem>
<FormItem :label="$L('百度地图AK')" prop="locat_bd_lbs_key">
<Input :maxlength="100" v-model="formData.locat_bd_lbs_key"/>
<div class="form-tip">{{$L('获取AK流程')}}: <a href="https://lbs.baidu.com/faq/search?id=299&title=677" target="_blank">https://lbs.baidu.com/faq/search?id=299&title=677</a></div>
</FormItem>
<FormItem :label="$L('允许签到位置')" prop="locat_bd_allow_point">
<ETooltip v-if="formData.locat_bd_lbs_point.lng" :content="$L('点击修改')">
<div class="form-tip">
<a href="javascript:void(0)" @click="openBdSelect">
{{ $L(`经度:${formData.locat_bd_lbs_point.lng},纬度:${formData.locat_bd_lbs_point.lat},半径:${formData.locat_bd_lbs_point.radius}`) }}
</a>
</div>
</ETooltip>
<a v-else href="javascript:void(0)" @click="openBdSelect">{{$L('点击设置')}}</a>
<FormItem :label="$L('地图类型')" prop="locat_map_type">
<RadioGroup v-model="formData.locat_map_type">
<Radio label="baidu">{{ $L('百度地图') }}</Radio>
<Radio label="amap">{{ $L('高德地图') }}</Radio>
<Radio label="tencent">{{ $L('腾讯地图') }}</Radio>
</RadioGroup>
<div class="form-tip">{{$L('仅支持移动端App')}}</div>
</FormItem>
<!-- 百度地图配置 -->
<template v-if="formData.locat_map_type === 'baidu'">
<FormItem :label="$L('百度地图AK')" prop="locat_bd_lbs_key">
<Input :maxlength="100" v-model="formData.locat_bd_lbs_key"/>
<div class="form-tip">{{$L('获取AK流程')}}: <a href="https://lbs.baidu.com/faq/search?id=299&title=677" target="_blank">https://lbs.baidu.com/faq/search?id=299&title=677</a></div>
</FormItem>
<FormItem :label="$L('允许签到位置')" prop="locat_bd_lbs_point">
<template v-if="formData.locat_bd_lbs_point.lng">
<div class="form-tip">
<a href="javascript:void(0)" @click="openMapSelect">
{{ $L(`经度:${formData.locat_bd_lbs_point.lng},纬度:${formData.locat_bd_lbs_point.lat},半径:${formData.locat_bd_lbs_point.radius}`) }}
</a>
</div>
<div class="form-tip" @click="openMapSelect">{{$L('点击修改允许签到位置')}}</div>
</template>
<a v-else href="javascript:void(0)" @click="openMapSelect">{{$L('点击设置')}}</a>
</FormItem>
</template>
<!-- 高德地图配置 -->
<template v-if="formData.locat_map_type === 'amap'">
<FormItem :label="$L('高德地图Key')" prop="locat_amap_key">
<Input :maxlength="100" v-model="formData.locat_amap_key"/>
<div class="form-tip">{{$L('获取Key流程')}}: <a href="https://lbs.amap.com/api/javascript-api/guide/abc/prepare" target="_blank">https://lbs.amap.com/api/javascript-api/guide/abc/prepare</a></div>
</FormItem>
<FormItem :label="$L('允许签到位置')" prop="locat_amap_point">
<template v-if="formData.locat_amap_point.lng">
<div class="form-tip">
<a href="javascript:void(0)" @click="openMapSelect">
{{ $L(`经度:${formData.locat_amap_point.lng},纬度:${formData.locat_amap_point.lat},半径:${formData.locat_amap_point.radius}`) }}
</a>
</div>
<div class="form-tip" @click="openMapSelect">{{$L('点击修改允许签到位置')}}</div>
</template>
<a v-else href="javascript:void(0)" @click="openMapSelect">{{$L('点击设置')}}</a>
</FormItem>
</template>
<!-- 腾讯地图配置 -->
<template v-if="formData.locat_map_type === 'tencent'">
<FormItem :label="$L('腾讯地图Key')" prop="locat_tencent_key">
<Input :maxlength="100" v-model="formData.locat_tencent_key"/>
<div class="form-tip">{{$L('获取Key流程')}}: <a href="https://lbs.qq.com/dev/console/application/mine" target="_blank">https://lbs.qq.com/dev/console/application/mine</a></div>
</FormItem>
<FormItem :label="$L('允许签到位置')" prop="locat_tencent_point">
<template v-if="formData.locat_tencent_point.lng">
<div class="form-tip">
<a href="javascript:void(0)" @click="openMapSelect">
{{ $L(`经度:${formData.locat_tencent_point.lng},纬度:${formData.locat_tencent_point.lat},半径:${formData.locat_tencent_point.radius}`) }}
</a>
</div>
<div class="form-tip" @click="openMapSelect">{{$L('点击修改允许签到位置')}}</div>
</template>
<a v-else href="javascript:void(0)" @click="openMapSelect">{{$L('点击设置')}}</a>
</FormItem>
</template>
</div>
</div>
</template>
@ -171,38 +222,140 @@
<TeamManagement v-if="allUserShow" checkin-mode/>
</DrawerOverlay>
<!--百度选择签到位置-->
<!--地图选择签到位置-->
<Modal
v-model="bdSelectShow"
v-model="mapSelectShow"
:title="$L('允许签到位置')"
:mask-closable="false"
width="800">
:styles="{
width: '90%',
maxWidth: '1000px'
}">
<div>
<div v-if="bdSelectPoint.radius" class="bd-select-point-tip">{{ $L(`签到半径${bdSelectPoint.radius}`) }}</div>
<div v-else class="bd-select-point-tip">{{ $L('请点击地图选择签到位置') }}</div>
<IFrame v-if="bdSelectShow" class="bd-select-point-iframe" :src="bdSelectUrl" @on-message="onBdMessage"/>
<div class="map-select-container">
<div class="map-select-iframe-container">
<IFrame v-if="mapSelectShow" ref="mapSelectIframe" class="map-select-point-iframe" :src="mapSelectUrl" @on-message="onMapMessage"/>
</div>
<div class="map-radius-control">
<div class="radius-control-header">
<h4>{{ $L('签到半径设置') }}</h4>
</div>
<div class="radius-control-body">
<Input :value="mapSelectPoint.radius" @on-change="onRadiusChange" @on-blur="onRadiusBlur">
<span slot="prepend">{{ $L('半径') }}</span>
<span slot="append">{{ $L('米') }}</span>
</Input>
<div class="location-info">
<div class="info-item">
<span class="info-label">{{ $L('经度') }}</span>
<span class="info-value">{{ mapSelectPoint.lng || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">{{ $L('纬度') }}</span>
<span class="info-value">{{ mapSelectPoint.lat || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">{{ $L('半径') }}</span>
<span class="info-value">{{ mapSelectPoint.radius || '-' }} {{ $L('米') }}</span>
</div>
</div>
<div class="radius-control-tip">
<template v-if="formData.locat_map_type === 'baidu'">
{{ $L('点击地图选择中心位置,拖拽圆形边缘调整半径,或在上方输入框直接设置半径值') }}
</template>
<template v-else>
{{ $L('点击地图选择中心位置,在上方输入框中设置签到半径值') }}
</template>
</div>
</div>
</div>
</div>
</div>
<div slot="footer" class="adaption">
<Button type="default" @click="bdSelectShow=false">{{$L('关闭')}}</Button>
<Button type="primary" @click="onBdSelect">{{$L('确定')}}</Button>
<Button type="default" @click="mapSelectShow=false">{{$L('关闭')}}</Button>
<Button type="primary" @click="onMapSelect">{{$L('确定')}}</Button>
</div>
</Modal>
</div>
</template>
<style lang="scss" scoped>
.bd-select-point-tip {
font-size: 16px;
text-align: center;
margin-bottom: 12px;
margin-top: -12px;
}
.bd-select-point-iframe {
width: 100%;
.map-select-container {
display: flex;
gap: 20px;
height: 500px;
@media (max-width: 768px) {
flex-direction: column;
height: 700px;
.map-radius-control {
width: 100%;
border-left: 0;
padding-left: 0;
}
}
}
.map-select-iframe-container {
flex: 1;
}
.map-select-point-iframe {
width: 100%;
height: 100%;
border: 0;
border-radius: 12px;
}
.map-radius-control {
width: 280px;
border-left: 1px solid #e8e8e8;
padding-left: 20px;
display: flex;
flex-direction: column;
}
.radius-control-header {
margin-bottom: 15px;
}
.radius-control-header h4 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #333;
}
.radius-control-body {
flex: 1;
display: flex;
flex-direction: column;
}
.location-info {
margin: 15px 0;
padding: 12px;
background: #f8f9fa;
border-radius: 6px;
}
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
}
.info-item:last-child {
margin-bottom: 0;
}
.info-label {
color: #666;
}
.info-value {
color: #333;
font-weight: 500;
}
.radius-control-tip {
background: #f0f8ff;
padding: 12px;
border-radius: 6px;
border-left: 3px solid #007cff;
font-size: 13px;
color: #333;
line-height: 1.4;
margin-top: auto;
}
</style>
<script>
import DrawerOverlay from "../../../../components/DrawerOverlay";
@ -227,16 +380,22 @@ export default {
face_retip: '',
manual_remark: '',
locat_remark: '',
locat_bd_lbs_point: {},
locat_map_type: 'baidu', //
locat_bd_lbs_key: '', // AK
locat_bd_lbs_point: {}, //
locat_amap_key: '', // Key
locat_amap_point: {}, //
locat_tencent_key: '', // Key
locat_tencent_point: {}, //
},
ruleData: {},
allUserShow: false,
exportShow: false,
bdSelectShow: false,
bdSelectPoint: {},
bdSelectUrl: '',
mapSelectShow: false,
mapSelectPoint: {},
mapSelectUrl: '',
}
},
@ -291,37 +450,98 @@ export default {
});
},
openBdSelect() {
if (!this.formData.locat_bd_lbs_key) {
$A.messageError('请先填写百度地图AK');
return;
openMapSelect() {
const mapType = this.formData.locat_map_type;
let mapKey = '';
let currentPoint = {};
// keypoint
switch (mapType) {
case 'baidu':
mapKey = this.formData.locat_bd_lbs_key;
currentPoint = this.formData.locat_bd_lbs_point;
if (!mapKey) {
$A.messageError('请先填写百度地图AK');
return;
}
break;
case 'amap':
mapKey = this.formData.locat_amap_key;
currentPoint = this.formData.locat_amap_point;
if (!mapKey) {
$A.messageError('请先填写高德地图Key');
return;
}
break;
case 'tencent':
mapKey = this.formData.locat_tencent_key;
currentPoint = this.formData.locat_tencent_point;
if (!mapKey) {
$A.messageError('请先填写腾讯地图Key');
return;
}
break;
default:
$A.messageError('请选择地图类型');
return;
}
const url = $A.urlAddParams($A.mainUrl('tools/map/select.html'), {
key: this.formData.locat_bd_lbs_key,
point: this.formData.locat_bd_lbs_point.lng + ',' + this.formData.locat_bd_lbs_point.lat,
radius: this.formData.locat_bd_lbs_point.radius,
const selectPage = `select_${mapType}.html`;
const url = $A.urlAddParams($A.mainUrl(`tools/map/${selectPage}`), {
key: mapKey,
point: currentPoint.lng + ',' + currentPoint.lat,
radius: currentPoint.radius,
})
this.$store.dispatch('userUrl', url).then(newUrl => {
this.bdSelectUrl = newUrl;
this.bdSelectPoint = this.formData.locat_bd_lbs_point;
this.bdSelectShow = true;
this.mapSelectUrl = newUrl;
this.mapSelectPoint = currentPoint;
this.mapSelectShow = true;
});
},
onBdMessage(data) {
if (data.action !== 'bd_lbs_select_point') {
onMapMessage(data) {
const expectedAction = `${this.formData.locat_map_type}_lbs_select_point`;
if (data.action !== expectedAction) {
return;
}
this.bdSelectPoint = {
lng: data.longitude,
lat: data.latitude,
radius: data.radius,
this.mapSelectPoint = {
lng: parseFloat(data.longitude),
lat: parseFloat(data.latitude),
radius: parseInt(data.radius),
}
},
onBdSelect() {
this.formData.locat_bd_lbs_point = this.bdSelectPoint;
this.bdSelectShow = false;
onRadiusChange({target}) {
const value = parseInt(target.value);
if (value && value >= 50 && value <= 5000) {
this.mapSelectPoint.radius = value;
const iframe = this.$refs.mapSelectIframe;
iframe?.postMessage({
action: 'update_radius',
radius: value
})
}
},
onRadiusBlur({target}) {
target.value = this.mapSelectPoint.radius;
},
onMapSelect() {
const mapType = this.formData.locat_map_type;
switch (mapType) {
case 'baidu':
this.formData.locat_bd_lbs_point = this.mapSelectPoint;
break;
case 'amap':
this.formData.locat_amap_point = this.mapSelectPoint;
break;
case 'tencent':
this.formData.locat_tencent_point = this.mapSelectPoint;
break;
}
this.mapSelectShow = false;
},
}
}

View File

@ -0,0 +1,286 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
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;
}
#map-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
<script>
class AmapPickup {
constructor(containerId) {
this.containerId = containerId;
this.map = null;
this.circle = null;
this.marker = null;
this.centerPoint = null;
this.isFirstClick = true;
this.params = {
theme: 'light', // 主题风格light|dark
key: null, // 高德地图 API Key
point: null, // 初始坐标116.404,39.915
radius: 300, // 圆形半径
zoom: 16, // 地图缩放级别
}
this.init();
}
async init() {
// 先初始化参数
this.initParams();
// 如果没有 key直接返回
if (!this.params.key) {
console.error('未提供高德地图 API Key');
return;
}
try {
// 等待地图 JS 加载完成
await this.loadMapScript();
// 初始化地图
this.initMap();
} catch (error) {
console.error('加载高德地图失败:', error);
}
}
initParams() {
// 获取当前URL的查询参数
const urlParams = new URLSearchParams(window.location.search);
// 遍历 params 对象的所有属性
Object.keys(this.params).forEach(key => {
// 从 URL 参数中获取值
const value = urlParams.get(key);
if (value !== null) {
// 根据参数类型进行转换
switch (key) {
case 'radius':
// 转换为数字
this.params[key] = parseInt(value) || 300;
break;
case 'point':
// 转换为坐标数组
const [lng, lat] = value.replace(/[|-]/, ',').split(',').map(parseFloat);
if (lng && lat) {
this.params[key] = {lng, lat};
}
break;
default:
// 字符串类型直接赋值
this.params[key] = value;
}
}
});
// 设置主题风格
if (!['dark', 'light'].includes(this.params.theme)) {
this.params.theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.classList.add(`theme-${this.params.theme}`);
document.body.style.backgroundColor = "#ffffff";
}
// 初始化地图
initMap() {
this.map = new AMap.Map(this.containerId, {
zoom: this.params.zoom,
center: this.params.point ? [this.params.point.lng, this.params.point.lat] : [116.404, 39.915],
enableMapClick: true
});
if (this.params.point) {
this.centerPoint = [this.params.point.lng, this.params.point.lat];
this.handleMapClick({lnglat: {lng: this.params.point.lng, lat: this.params.point.lat}});
} else {
this.centerPoint = [116.404, 39.915];
}
this.bindEvents();
}
bindEvents() {
this.map.on('click', (e) => this.handleMapClick(e));
// 监听父页面消息
window.addEventListener('message', (event) => {
if (event.data.action === 'update_radius') {
const newRadius = parseInt(event.data.radius);
if (newRadius && newRadius >= 50 && newRadius <= 5000) {
this.params.radius = newRadius;
if (this.circle) {
this.updateMarkerAndCircle();
this.updateInfoPanel();
}
}
}
});
}
handleMapClick(e) {
this.centerPoint = [e.lnglat.lng, e.lnglat.lat];
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.params.radius = this.circle.getRadius();
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
}
createMarkerAndCircle() {
this.createMarker();
this.createCircle();
}
createMarker() {
if (this.marker) {
this.map.remove(this.marker);
}
this.marker = new AMap.Marker({
position: this.centerPoint,
map: this.map
});
}
createCircle() {
if (this.circle) {
this.map.remove(this.circle);
}
this.circle = new AMap.Circle({
center: this.centerPoint,
radius: this.params.radius,
strokeColor: "#FF0000",
strokeWeight: 2,
strokeOpacity: 0.5,
fillColor: "#FF0000",
fillOpacity: 0.2,
map: this.map
});
}
updateMarkerAndCircle() {
if (this.marker) {
this.map.remove(this.marker);
}
if (this.circle) {
this.map.remove(this.circle);
}
this.createMarker();
this.createCircle();
}
updateInfoPanel() {
const currentRadius = this.circle ? this.circle.getRadius().toFixed(0) : this.params.radius.toString();
const data = {
longitude: this.centerPoint[0].toFixed(6),
latitude: this.centerPoint[1].toFixed(6),
radius: currentRadius
}
// 发送消息给父页面
window.parent.postMessage(Object.assign(data, {
action: "amap_lbs_select_point",
}), "*");
}
// 获取当前选择的数据
getData() {
if (!this.circle) return null;
return {
center: {
lng: this.centerPoint[0],
lat: this.centerPoint[1]
},
radius: this.circle.getRadius()
};
}
// 设置中心点和半径
setData(lng, lat, radius) {
this.centerPoint = [lng, lat];
this.params.radius = radius || this.params.radius;
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
this.map.setCenter(this.centerPoint);
}
loadMapScript() {
return new Promise((resolve, reject) => {
// 如果已经加载过,直接返回
if (window.AMap) {
resolve();
return;
}
// 创建script标签
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://webapi.amap.com/maps?v=1.4.15&key=${this.params.key}&callback=initAmapMap`;
// 添加回调函数
window.initAmapMap = () => {
resolve();
delete window.initAmapMap;
};
// 处理加载错误
script.onerror = () => {
reject(new Error('高德地图脚本加载失败'));
};
// 添加到页面
document.body.appendChild(script);
});
}
}
// 页面加载完成后初始化
window.onload = function () {
window.pickup = new AmapPickup("map-container");
};
</script>
</head>
<body>
<div id="map-container"></div>
</body>
</html>

View File

@ -29,6 +29,8 @@
right: 0;
bottom: 0;
}
</style>
<script>
class Pickup {
@ -133,6 +135,20 @@
bindEvents() {
this.map.addEventListener("click", (e) => this.handleMapClick(e));
// 监听父页面消息
window.addEventListener('message', (event) => {
if (event.data.action === 'update_radius') {
const newRadius = parseInt(event.data.radius);
if (newRadius && newRadius >= 50 && newRadius <= 5000) {
this.params.radius = newRadius;
if (this.circle) {
this.updateMarkerAndCircle();
this.updateInfoPanel();
}
}
}
});
}
handleMapClick(e) {
@ -191,13 +207,16 @@
}
updateInfoPanel() {
const currentRadius = this.circle ? this.circle.getRadius().toFixed(0) : this.params.radius.toString();
const data = {
longitude: this.centerPoint.lng.toFixed(6),
latitude: this.centerPoint.lat.toFixed(6),
radius: this.circle ? this.circle.getRadius().toFixed(0) : '-'
radius: currentRadius
}
// 发送消息给父页面
window.parent.postMessage(Object.assign(data, {
action: "bd_lbs_select_point",
action: "baidu_lbs_select_point",
}), "*");
}

View File

@ -0,0 +1,297 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
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;
}
#map-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
<script>
class TencentPickup {
constructor(containerId) {
this.containerId = containerId;
this.map = null;
this.circle = null;
this.marker = null;
this.centerPoint = null;
this.isFirstClick = true;
this.params = {
theme: 'light', // 主题风格light|dark
key: null, // 腾讯地图 API Key
point: null, // 初始坐标116.404,39.915
radius: 300, // 圆形半径
zoom: 16, // 地图缩放级别
}
this.init();
}
async init() {
// 先初始化参数
this.initParams();
// 如果没有 key直接返回
if (!this.params.key) {
console.error('未提供腾讯地图 API Key');
return;
}
try {
// 等待地图 JS 加载完成
await this.loadMapScript();
// 初始化地图
this.initMap();
} catch (error) {
console.error('加载腾讯地图失败:', error);
}
}
initParams() {
// 获取当前URL的查询参数
const urlParams = new URLSearchParams(window.location.search);
// 遍历 params 对象的所有属性
Object.keys(this.params).forEach(key => {
// 从 URL 参数中获取值
const value = urlParams.get(key);
if (value !== null) {
// 根据参数类型进行转换
switch (key) {
case 'radius':
// 转换为数字
this.params[key] = parseInt(value) || 300;
break;
case 'point':
// 转换为坐标数组
const [lng, lat] = value.replace(/[|-]/, ',').split(',').map(parseFloat);
if (lng && lat) {
this.params[key] = {lng, lat};
}
break;
default:
// 字符串类型直接赋值
this.params[key] = value;
}
}
});
// 设置主题风格
if (!['dark', 'light'].includes(this.params.theme)) {
this.params.theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.classList.add(`theme-${this.params.theme}`);
document.body.style.backgroundColor = "#ffffff";
}
// 初始化地图
initMap() {
const center = this.params.point ?
new TMap.LatLng(this.params.point.lat, this.params.point.lng) :
new TMap.LatLng(39.915, 116.404);
this.map = new TMap.Map(this.containerId, {
center: center,
zoom: this.params.zoom
});
if (this.params.point) {
this.centerPoint = center;
this.handleMapClick({latLng: center});
} else {
this.centerPoint = center;
}
this.bindEvents();
}
bindEvents() {
this.map.on('click', (e) => this.handleMapClick(e));
// 监听父页面消息
window.addEventListener('message', (event) => {
if (event.data.action === 'update_radius') {
const newRadius = parseInt(event.data.radius);
if (newRadius && newRadius >= 50 && newRadius <= 5000) {
this.params.radius = newRadius;
if (this.circle) {
this.updateMarkerAndCircle();
this.updateInfoPanel();
}
}
}
});
}
handleMapClick(e) {
this.centerPoint = e.latLng;
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
}
createMarkerAndCircle() {
this.createMarker();
this.createCircle();
}
createMarker() {
if (this.marker) {
this.marker.setMap(null);
}
// 使用MultiMarker但不设置自定义样式使用默认样式
this.marker = new TMap.MultiMarker({
map: this.map,
geometries: [{
id: 'marker1',
position: this.centerPoint
}]
});
}
createCircle() {
if (this.circle) {
this.circle.setMap(null);
}
// 使用MultiCircle但不设置自定义样式使用默认样式
this.circle = new TMap.MultiCircle({
map: this.map,
geometries: [{
id: 'circle1',
center: this.centerPoint,
radius: this.params.radius
}]
});
}
updateMarkerAndCircle() {
if (this.marker) {
this.marker.updateGeometries([{
id: 'marker1',
position: this.centerPoint
}]);
}
if (this.circle) {
this.circle.updateGeometries([{
id: 'circle1',
center: this.centerPoint,
radius: this.params.radius
}]);
}
}
updateInfoPanel() {
const data = {
longitude: this.centerPoint.lng.toFixed(6),
latitude: this.centerPoint.lat.toFixed(6),
radius: this.params.radius.toString()
}
// 发送消息给父页面
window.parent.postMessage(Object.assign(data, {
action: "tencent_lbs_select_point",
}), "*");
}
// 获取当前选择的数据
getData() {
if (!this.circle) return null;
return {
center: {
lng: this.centerPoint.lng,
lat: this.centerPoint.lat
},
radius: this.params.radius
};
}
// 设置中心点和半径
setData(lng, lat, radius) {
this.centerPoint = new TMap.LatLng(lat, lng);
this.params.radius = radius || this.params.radius;
if (this.isFirstClick) {
this.createMarkerAndCircle();
this.isFirstClick = false;
} else {
this.updateMarkerAndCircle();
}
this.updateInfoPanel();
this.map.setCenter(this.centerPoint);
}
loadMapScript() {
return new Promise((resolve, reject) => {
// 如果已经加载过,直接返回
if (window.TMap) {
resolve();
return;
}
// 创建script标签
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://map.qq.com/api/gljs?v=1.exp&key=${this.params.key}&callback=initTencentMap`;
// 添加回调函数
window.initTencentMap = () => {
resolve();
delete window.initTencentMap;
};
// 处理加载错误
script.onerror = () => {
reject(new Error('腾讯地图脚本加载失败'));
};
// 添加到页面
document.body.appendChild(script);
});
}
}
// 页面加载完成后初始化
window.onload = function () {
window.pickup = new TencentPickup("map-container");
};
</script>
</head>
<body>
<div id="map-container"></div>
</body>
</html>