287 lines
10 KiB
HTML
287 lines
10 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>地图派单示例</title>
|
|
<!-- 使用高德测试 Key -->
|
|
<script src="https://webapi.amap.com/maps?v=2.0&key={$mapkey.amapkey|default=''}"></script>
|
|
<script src="__CDN__/assets/libs/jquery/dist/jquery.min.js"></script>
|
|
<style>
|
|
#map {height: 100vh; }
|
|
.info-window {
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
}
|
|
.worker-search-bar {
|
|
display: flex;
|
|
gap: 8px; /* 输入框和按钮的间距 */
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.worker-input {
|
|
flex: 1;
|
|
padding: 6px 10px;
|
|
font-size: 14px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
line-height: 14px;
|
|
}
|
|
|
|
.worker-btn {
|
|
padding: 6px 12px;
|
|
font-size: 14px;
|
|
background-color: #007bff;
|
|
color: #fff;
|
|
border: 1px solid #007bff;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
line-height: 14px;
|
|
}
|
|
|
|
.worker-btn:hover {
|
|
background-color: #0056b3;
|
|
border-color: #0056b3;
|
|
}
|
|
.worker-card{
|
|
background-color: #c9c5c542;
|
|
margin-bottom: 10px;
|
|
box-shadow: 0 2px 6px rgba(12, 72, 128, .1);
|
|
border-radius: 5px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="row" style="width: 100%">
|
|
<div id="map" class="col-md-9"></div>
|
|
|
|
<div id="list" class="col-md-3 pl-3">
|
|
<div class="worker-search-bar mb-2">
|
|
<input type="text" id="worker-search" class="worker-input" placeholder="搜索接单员姓名或电话">
|
|
<button id="reload" class="worker-btn">刷新</button>
|
|
</div>
|
|
<div id="worker-list" style="height: 100vh; overflow: auto;"></div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
$(function (){
|
|
window._AMapSecurityConfig = {
|
|
securityJsCode: "{$mapkey.amapsecurityjscode|default=''}",
|
|
}
|
|
|
|
const taskLocation = {:json_encode($center); };
|
|
let workerMarkers = [];
|
|
|
|
const map = new AMap.Map('map', {
|
|
zoom: 16,
|
|
center: taskLocation,
|
|
});
|
|
|
|
// 添加任务地点标记(红色)
|
|
const taskMarker = new AMap.Marker({
|
|
position: taskLocation,
|
|
title: '任务地点',
|
|
icon: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png',
|
|
label: {
|
|
content: '<span style="color: red; font-weight: bold;">任务点</span>',
|
|
offset: new AMap.Pixel(10, -10)
|
|
}
|
|
});
|
|
map.add(taskMarker);
|
|
function drawWorker(workers) {
|
|
// 清除旧的工人标记
|
|
if (workerMarkers.length > 0) {
|
|
map.remove(workerMarkers);
|
|
workerMarkers = [];
|
|
}
|
|
|
|
// 添加新的工人标记
|
|
workers.forEach((worker, index) => {
|
|
const position = addSlightOffset(worker.lat, worker.lng, index);
|
|
|
|
const marker = new AMap.Marker({
|
|
position,
|
|
title: worker.name,
|
|
icon: new AMap.Icon({
|
|
size: new AMap.Size(32, 32),
|
|
image: '/assets/img/worker.png',
|
|
imageSize: new AMap.Size(32, 32)
|
|
}),
|
|
label: {
|
|
content: `<span>${worker.name}</span>`,
|
|
offset: new AMap.Pixel(10, -10)
|
|
}
|
|
});
|
|
|
|
const infoWindow = new AMap.InfoWindow({
|
|
content: `
|
|
<div class="info-window">
|
|
<strong>${worker.name}</strong><br/>
|
|
电话:${worker.tel}<br/>
|
|
距离任务:${worker.distance.toFixed(2)} 米<br/>
|
|
正在进行订单数:${worker.doing_order}<br/>
|
|
完成订单数:${worker.finish_order}<br/>
|
|
星级:${'★'.repeat(worker.star)}<br/>
|
|
<button class="btn btn-sm btn-primary assign-btn mt-1" data-id="${worker.id}" data-name="${worker.name}">派单</button>
|
|
</div>`,
|
|
offset: new AMap.Pixel(0, -10)
|
|
});
|
|
|
|
marker.on('click', () => {
|
|
infoWindow.open(map, marker.getPosition());
|
|
});
|
|
|
|
map.add(marker);
|
|
workerMarkers.push(marker); // 存储到全局数组
|
|
});
|
|
}
|
|
//点击确定后执行回调赋值
|
|
var close = function (data) {
|
|
var index = parent.Layer.getFrameIndex(window.name);
|
|
var callback = parent.$("#layui-layer" + index).data("callback");
|
|
//再执行关闭
|
|
parent.Layer.close(index);
|
|
//再调用回传函数
|
|
if (typeof callback === 'function') {
|
|
callback.call(undefined, data);
|
|
}
|
|
};
|
|
|
|
function renderWorkerCards(workers) {
|
|
const listContainer = document.getElementById('worker-list');
|
|
listContainer.innerHTML = ''; // 清空旧内容
|
|
if (!workers || workers.length === 0) {
|
|
listContainer.innerHTML = `
|
|
<div class="col">
|
|
<div class="text-center" role="alert">
|
|
暂无可用接单员
|
|
</div>
|
|
</div>`;
|
|
return;
|
|
}
|
|
workers.forEach(worker => {
|
|
const maskedPhone = worker.tel.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2');
|
|
const starHtml = '★'.repeat(worker.star).padEnd(5, '☆');
|
|
const distanceKm = (worker.distance / 1000).toFixed(2);
|
|
|
|
const cardHtml = `
|
|
<div class="col worker-card" data-lat="${worker.lat}" data-lng="${worker.lng}">
|
|
<div class="card shadow-sm">
|
|
<div class="card-body py-2 px-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<strong class="mb-0">${worker.name}</strong>
|
|
<button class="btn btn-sm btn-primary assign-btn" data-id="${worker.id}" data-name="${worker.name}">派单</button>
|
|
</div>
|
|
<div>
|
|
<div>
|
|
<small class="text-muted d-block">电话:${maskedPhone}</small>
|
|
<small class="text-muted d-block">星级:
|
|
<span class="text-warning">${starHtml}</span>
|
|
</small>
|
|
</div>
|
|
<div>
|
|
<small class="text-muted d-block">距离:${distanceKm} 公里</small>
|
|
<small class="text-muted d-block">完成订单数:${worker.finish_order}</small>
|
|
<br>
|
|
<small class="text-muted d-block">进行中订单数:${worker.doing_order}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
|
|
listContainer.insertAdjacentHTML('beforeend', cardHtml);
|
|
});
|
|
}
|
|
|
|
function assignOrder(worker_id) {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const order_id = params.get('order_id');
|
|
|
|
const loadingIndex = layer.load(1, {shade: [0.1, '#fff']}); // 显示 loading
|
|
|
|
$.ajax({
|
|
url: '/admin/orders/dispatch/add',
|
|
method: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({ worker_id, order_id }),
|
|
success: function (res) {
|
|
layer.close(loadingIndex); // 关闭 loading
|
|
Toastr.info('派单成功');
|
|
close();
|
|
},
|
|
error: function () {
|
|
layer.close(loadingIndex); // 关闭 loading
|
|
Toastr.error('派单失败,请重试。');
|
|
}
|
|
});
|
|
}
|
|
function reload(){
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
|
const orderId = params.get('order_id');
|
|
const keyword = $('#worker-search').val().trim();
|
|
|
|
// 构造请求参数对象
|
|
const queryParams = new URLSearchParams();
|
|
if(orderId) queryParams.append('order_id', orderId);
|
|
if(keyword) queryParams.append('search', keyword); // 传给后端的搜索关键词参数名,跟后端约定
|
|
|
|
$.ajax({
|
|
url: "/admin/workers/worker/dispatchMapList?" + queryParams.toString(),
|
|
type: 'get',
|
|
dataType: 'json',
|
|
success: function (ret) {
|
|
drawWorker(ret.rows);
|
|
renderWorkerCards(ret.rows);
|
|
}
|
|
});
|
|
}
|
|
|
|
reload();
|
|
|
|
function addSlightOffset(lat, lng, index) {
|
|
const offset = 0.00005; // 约5米
|
|
return [
|
|
parseFloat(lng) + (index % 3 - 1) * offset,
|
|
parseFloat(lat) + (Math.floor(index / 3) - 1) * offset
|
|
];
|
|
}
|
|
// 委托方式绑定事件:监听 .assign-btn 点击
|
|
$(document).on('click', '.assign-btn', function () {
|
|
const worker_id = $(this).data('id');
|
|
const worker_name = $(this).data('name');
|
|
|
|
layer.confirm(
|
|
'确定要将订单派给 "' + worker_name + '" 吗?',
|
|
{icon: 3, title: '确认派单'},
|
|
function (index) {
|
|
assignOrder(worker_id);
|
|
}
|
|
);
|
|
});
|
|
// 事件委托,绑定点击工人卡片的事件
|
|
$(document).on('click', '.worker-card', function (e) {
|
|
// 避免点击“派单”按钮时触发此事件
|
|
if ($(e.target).closest('.assign-btn').length) {
|
|
return;
|
|
}
|
|
|
|
const lat = parseFloat($(this).data('lat'));
|
|
const lng = parseFloat($(this).data('lng'));
|
|
|
|
if (!isNaN(lat) && !isNaN(lng)) {
|
|
map.setCenter([lng, lat]); // 高德地图的坐标格式是 [经度, 纬度]
|
|
map.setZoom(17); // 可选:放大地图
|
|
}
|
|
});
|
|
|
|
$('#reload').click(function () {
|
|
reload();
|
|
});
|
|
})
|
|
</script>
|
|
</body>
|
|
</html>
|