This commit is contained in:
2025-11-20 15:10:08 +08:00
parent 5a77756c33
commit ca13d078a3
12 changed files with 216 additions and 203 deletions

View File

@ -4,11 +4,12 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>路径规划系统</title>
<!-- 仅保留必要CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script src="https://cdn.jsdelivr.net/npm/axios@1.6.8/dist/axios.min.js"></script>
<!-- Tailwind配置 -->
@ -38,25 +39,32 @@
.content-auto {
content-visibility: auto;
}
.map-height {
height: 100vh; /* 调整为占满视口高度 */
}
.sidebar-height {
height: 100vh; /* 调整为占满视口高度 */
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.custom-marker .fa {
font-size: 14px;
}
.input-error {
@apply border-danger focus:ring-danger/50 focus:border-danger;
}
.btn-disabled {
@apply bg-gray-300 text-gray-500 cursor-not-allowed hover:bg-gray-300;
}
@ -100,7 +108,8 @@
<!-- 起点移除默认value、默认无数据 -->
<div class="space-y-1">
<label class="block text-sm font-medium text-gray-700">起点 <span class="text-danger">*</span></label>
<label class="block text-sm font-medium text-gray-700">起点 <span
class="text-danger">*</span></label>
<div class="flex space-x-2">
<div class="flex-1 space-y-0.5">
<input type="text" id="startLat" placeholder="纬度"
@ -114,7 +123,8 @@
maxlength="10">
<span id="startLngError" class="text-danger text-xs hidden">请输入有效经度</span>
</div>
<button id="setStartBtn" class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
<button id="setStartBtn"
class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
title="在地图上选择起点">
<i class="fa fa-map-marker text-danger"></i>
</button>
@ -123,7 +133,8 @@
<!-- 终点 -->
<div class="space-y-1">
<label class="block text-sm font-medium text-gray-700">终点 <span class="text-danger">*</span></label>
<label class="block text-sm font-medium text-gray-700">终点 <span
class="text-danger">*</span></label>
<div class="flex space-x-2">
<div class="flex-1 space-y-0.5">
<input type="text" id="endLat" placeholder="纬度"
@ -137,7 +148,8 @@
maxlength="10">
<span id="endLngError" class="text-danger text-xs hidden">请输入有效经度</span>
</div>
<button id="setEndBtn" class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
<button id="setEndBtn"
class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
title="在地图上选择终点">
<i class="fa fa-flag text-success"></i>
</button>
@ -160,7 +172,8 @@
<!-- 交通方式 -->
<div class="space-y-1">
<label class="block text-sm font-medium text-gray-700">交通方式 <span class="text-danger">*</span></label>
<label class="block text-sm font-medium text-gray-700">交通方式 <span
class="text-danger">*</span></label>
<select id="profile"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary text-sm transition-all bg-white">
<option value="car">驾车</option>
@ -241,14 +254,14 @@
<script>
// 大整数转字符串处理(保留原逻辑)
axios.defaults.transformResponse = [
function(data) {
function (data) {
if (typeof data !== 'string') return data;
const bigIntRegex = /(\s*"[^"]*"\s*:\s*)(\d{16,})(\s*)/g;
return data.replace(bigIntRegex, (match, keyPart, bigInt, endPart) => {
return `${keyPart}"${bigInt}"${endPart}`;
});
},
function(parsedData) {
function (parsedData) {
try {
return JSON.parse(parsedData);
} catch (e) {
@ -265,7 +278,7 @@
let routeLine = null;
let waypointCount = 0;
const API_BASE_URL = "http://127.0.0.1:8848";
const DEFAULT_CENTER = { lat: 30.6570, lng: 104.0650 }; // 成都默认坐标
const DEFAULT_CENTER = {lat: 30.6570, lng: 104.0650}; // 成都默认坐标
// 地图初始化
function initMap() {
@ -353,7 +366,7 @@
}).addTo(map);
startMarker.on('dragend', (e) => {
const { lat, lng } = e.target.getLatLng();
const {lat, lng} = e.target.getLatLng();
setStartPoint(lat, lng);
});
}
@ -383,7 +396,7 @@
}).addTo(map);
endMarker.on('dragend', (e) => {
const { lat, lng } = e.target.getLatLng();
const {lat, lng} = e.target.getLatLng();
setEndPoint(lat, lng);
});
}
@ -436,7 +449,7 @@
waypointDiv.querySelector('.set-waypoint-btn').addEventListener('click', () => {
const id = parseInt(waypointDiv.dataset.id);
map.once('click', (e) => {
const { lat, lng } = e.latlng;
const {lat, lng} = e.latlng;
latInput.value = lat.toFixed(6);
lngInput.value = lng.toFixed(6);
latInput.dispatchEvent(new Event('input'));
@ -457,7 +470,7 @@
}).addTo(map);
waypointMarkers[id].on('dragend', (e) => {
const { lat, lng } = e.target.getLatLng();
const {lat, lng} = e.target.getLatLng();
latInput.value = lat.toFixed(6);
lngInput.value = lng.toFixed(6);
latInput.dispatchEvent(new Event('input'));
@ -480,7 +493,7 @@
// 地图点击处理
function handleMapClick(e) {
const { lat, lng } = e.latlng;
const {lat, lng} = e.latlng;
const startLat = document.getElementById('startLat').value;
const endLat = document.getElementById('endLat').value;
@ -632,7 +645,7 @@
const lat = parseFloat(item.querySelector('.waypoint-lat').value);
const lng = parseFloat(item.querySelector('.waypoint-lng').value);
if (!isNaN(lat) && !isNaN(lng) && validateCoord(lat, 'lat') && validateCoord(lng, 'lng')) {
waypoints.push({ lat, lng });
waypoints.push({lat, lng});
}
});
@ -644,8 +657,8 @@
try {
const response = await axios.post(
`${API_BASE_URL}/graphhopper/route`,
{ startLat, startLng, endLat, endLng, profile, waypoints },
{ headers: { 'Content-Type': 'application/json' } }
{startLat, startLng, endLat, endLng, profile, waypoints},
{headers: {'Content-Type': 'application/json'}}
);
if (response.data.code !== 200 || !response.data.data) {
@ -671,16 +684,16 @@
if (routeLine) map.removeLayer(routeLine);
const latLngs = routeData.pathPoints.map(point => [point.lat, point.lng]);
const lineStyles = {
car: { color: '#165DFF', weight: 5, opacity: 0.8, dashArray: '' },
bike: { color: '#00B42A', weight: 4, opacity: 0.8, dashArray: '5,5' },
foot: { color: '#4b0c35', weight: 3, opacity: 0.8, dashArray: '2,2' }
car: {color: '#165DFF', weight: 5, opacity: 0.8, dashArray: ''},
bike: {color: '#00B42A', weight: 4, opacity: 0.8, dashArray: '5,5'},
foot: {color: '#4b0c35', weight: 3, opacity: 0.8, dashArray: '2,2'}
};
routeLine = L.polyline(latLngs, lineStyles[document.getElementById('profile').value])
.addTo(map)
.bindPopup(`<div class="text-sm"><p>距离:${routeData.distanceKm.toFixed(2)} 公里</p><p>时间:${routeData.timeMinutes} 分钟</p></div>`);
map.fitBounds(routeLine.getBounds(), { padding: [50, 50], maxZoom: 14 });
map.fitBounds(routeLine.getBounds(), {padding: [50, 50], maxZoom: 14});
}
// 事件绑定:移除起点按钮弹窗