680 lines
26 KiB
HTML
680 lines
26 KiB
HTML
{% extends "layout.html" %}
|
||
|
||
{% block title %}
|
||
腾讯地图导航
|
||
{% endblock %}
|
||
|
||
{% block main %}
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
||
<title>地点搜索与导航</title>
|
||
<!-- UIkit CSS -->
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/css/uikit.min.css" />
|
||
<!-- UIkit JS -->
|
||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit-icons.min.js"></script>
|
||
|
||
<style type="text/css">
|
||
body {
|
||
margin: 0;
|
||
height: 100%;
|
||
width: 100%;
|
||
position: absolute;
|
||
}
|
||
|
||
#mapContainer {
|
||
width: 100%;
|
||
height: 80vh;
|
||
position: relative;
|
||
}
|
||
|
||
.search-box {
|
||
position: absolute;
|
||
top: 20px;
|
||
left: 20px;
|
||
right: 20px;
|
||
z-index: 999;
|
||
background: white;
|
||
padding: 10px;
|
||
border-radius: 3px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||
}
|
||
|
||
.search-results {
|
||
position: absolute;
|
||
top: 80px;
|
||
left: 20px;
|
||
right: 20px;
|
||
z-index: 998;
|
||
background: white;
|
||
border-radius: 3px;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
display: none;
|
||
}
|
||
|
||
.search-result-item {
|
||
padding: 10px;
|
||
border-bottom: 1px solid #eee;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.search-result-item:hover {
|
||
background-color: #f5f5f5;
|
||
}
|
||
</style>
|
||
|
||
<div class="search-box">
|
||
<div class="uk-grid-small" uk-grid>
|
||
<div class="uk-width-1-4">
|
||
<select id="save_type" class="uk-select">
|
||
<option value="recent">最近</option>
|
||
<option value="home">住家</option>
|
||
<option value="work">工作</option>
|
||
</select>
|
||
</div>
|
||
<div class="uk-width-expand">
|
||
<div class="uk-inline uk-width-1-1">
|
||
<span class="uk-form-icon" uk-icon="icon: search"></span>
|
||
<input class="uk-input" type="text" id="keyword"
|
||
placeholder="请输入关键字搜索地点"
|
||
autocomplete="off">
|
||
</div>
|
||
</div>
|
||
<div class="uk-width-auto">
|
||
<button class="uk-button uk-button-primary" onclick="searchPlace(document.getElementById('keyword').value)">
|
||
<span uk-icon="icon: search"></span> 搜索
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="uk-margin-small-top">
|
||
<button class="uk-button uk-button-default uk-button-small" onclick="locateUser()">
|
||
<span uk-icon="icon: location"></span> 定位
|
||
</button>
|
||
<button class="uk-button uk-button-default uk-button-small" onclick="showSearchHistory()">
|
||
<span uk-icon="icon: history"></span> 历史记录
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="search-results" class="search-results"></div>
|
||
<div id="mapContainer"></div>
|
||
|
||
<!-- 更改腾讯地图API引入方式,增加passive参数,可能会减少一些警告 -->
|
||
<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&key=BDMBZ-LZQ63-GUG37-OCHES-2ESXV-Q5BVC&libraries=geometry"></script>
|
||
|
||
<script type="text/javascript">
|
||
var map, marker, infoWindow;
|
||
var searchTimeout;
|
||
var searchMarkers = [];
|
||
var initialPosition;
|
||
|
||
// 修复标记图片URL问题 - 使用腾讯地图官方正确的图标URL
|
||
var markerIconDefault = 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/marker.png';
|
||
var markerIconSelected = 'https://mapapi.qq.com/web/lbs/javascriptV2/javascript/img/marker_red.png';
|
||
var markerIconSearch = 'https://mapapi.qq.com/web/lbs/javascriptV2/javascript/img/marker_blue.png';
|
||
|
||
// 初始化地图
|
||
function initMap() {
|
||
// 修复Jinja变量语法,避免JavaScript解析错误
|
||
initialPosition = new TMap.LatLng(Number("{{lat}}"), Number("{{lon}}"));
|
||
|
||
map = new TMap.Map('mapContainer', {
|
||
center: initialPosition,
|
||
zoom: 15,
|
||
showControl: true,
|
||
viewMode: '2D' // 明确指定2D模式,可能会减少一些警告
|
||
});
|
||
|
||
// 添加当前位置标记 - 使用正确的图标URL
|
||
marker = new TMap.MultiMarker({
|
||
map: map,
|
||
styles: {
|
||
"marker": new TMap.MarkerStyle({
|
||
width: 25,
|
||
height: 35,
|
||
anchor: { x: 12.5, y: 35 },
|
||
src: markerIconDefault
|
||
}),
|
||
"selected": new TMap.MarkerStyle({
|
||
width: 30,
|
||
height: 42,
|
||
anchor: { x: 15, y: 42 },
|
||
src: markerIconSelected
|
||
}),
|
||
"searchResult": new TMap.MarkerStyle({
|
||
width: 25,
|
||
height: 35,
|
||
anchor: { x: 12.5, y: 35 },
|
||
src: markerIconSearch
|
||
})
|
||
},
|
||
geometries: [{
|
||
id: 'current',
|
||
position: initialPosition,
|
||
styleId: 'marker'
|
||
}]
|
||
});
|
||
|
||
// 添加点击事件
|
||
map.on('click', handleMapClick);
|
||
|
||
// 初始化搜索服务
|
||
initSearchService();
|
||
}
|
||
|
||
// 初始化搜索服务和联想功能
|
||
function initSearchService() {
|
||
// 监听输入框,实现联想搜索
|
||
var inputElement = document.getElementById('keyword');
|
||
|
||
inputElement.addEventListener('input', function() {
|
||
var keyword = this.value.trim();
|
||
if (keyword) {
|
||
clearTimeout(searchTimeout);
|
||
searchTimeout = setTimeout(function() {
|
||
searchSuggestion(keyword);
|
||
}, 300);
|
||
} else {
|
||
document.getElementById('search-results').style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 防止表单提交刷新页面
|
||
inputElement.form && inputElement.form.addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
searchPlace(inputElement.value);
|
||
});
|
||
}
|
||
|
||
// 处理地图点击
|
||
function handleMapClick(evt) {
|
||
var position = evt.latLng;
|
||
|
||
// 清除搜索结果
|
||
document.getElementById('search-results').style.display = 'none';
|
||
|
||
// 更新标记位置
|
||
marker.updateGeometries([{
|
||
id: 'current',
|
||
position: position,
|
||
styleId: 'marker'
|
||
}]);
|
||
|
||
// 对于地图点击,设置一个默认地名 "选定位置"
|
||
var coordsText = "(" + position.lat.toFixed(6) + ", " + position.lng.toFixed(6) + ")";
|
||
showInfoWindow(position, "选定位置", coordsText);
|
||
}
|
||
|
||
// 显示信息窗口 - 使用搜索框文本作为地名
|
||
function showInfoWindow(position, name, addr) {
|
||
if (infoWindow) {
|
||
infoWindow.close();
|
||
}
|
||
|
||
// 获取搜索框的值作为地名
|
||
var searchText = document.getElementById('keyword').value.trim();
|
||
var placeName = searchText || name || "选定位置";
|
||
|
||
// 调试输出
|
||
console.log("显示信息窗口,搜索框文本:", searchText, "名称:", name, "地址:", addr, "使用地名:", placeName);
|
||
|
||
infoWindow = new TMap.InfoWindow({
|
||
map: map,
|
||
position: position,
|
||
content: `
|
||
<div class="uk-card uk-card-default uk-card-body" style="padding:10px; min-width:200px;">
|
||
<a class="uk-card-badge uk-label" onClick="javascript:infoWindow.close()" uk-close></a>
|
||
<h3 style="padding-top: 10px;" class="uk-card-title">${name || ""}</h3>
|
||
<p>${addr || ""}</p>
|
||
<div class="uk-card-footer" style="padding-top:10px;">
|
||
<form name="navForm" method="post">
|
||
<input type="hidden" name="lat" value="${position.lat}">
|
||
<input type="hidden" name="lon" value="${position.lng}">
|
||
<input type="hidden" name="save_type" value="${document.getElementById('save_type').value}">
|
||
<input type="hidden" name="place_name" value="${placeName}">
|
||
<input class="uk-button uk-button-primary" type="submit" value="导航" onclick="console.log('导航表单提交,地点名:', '${placeName}')">
|
||
</form>
|
||
</div>
|
||
</div>
|
||
`,
|
||
offset: { x: 0, y: -35 }
|
||
});
|
||
}
|
||
|
||
// 修改搜索联想函数,添加错误处理
|
||
function searchSuggestion(keyword) {
|
||
console.log("搜索联想:", keyword);
|
||
|
||
// 避免使用可能导致跨域问题的XMLHttpRequest
|
||
// 直接使用JSONP方法,这是最可靠的
|
||
var script = document.createElement('script');
|
||
var callbackName = 'jsonp_suggestion_' + Math.round(100000 * Math.random());
|
||
|
||
window[callbackName] = function(res) {
|
||
console.log("联想搜索结果:", res);
|
||
if (script.parentNode) document.body.removeChild(script);
|
||
delete window[callbackName];
|
||
|
||
if (res.status === 0 && res.data && res.data.length > 0) {
|
||
showSuggestionResults(res.data);
|
||
} else {
|
||
document.getElementById('search-results').style.display = 'none';
|
||
}
|
||
};
|
||
|
||
var suggestUrl = "https://apis.map.qq.com/ws/place/v1/suggestion?keyword=" +
|
||
encodeURIComponent(keyword) +
|
||
"&key=BDMBZ-LZQ63-GUG37-OCHES-2ESXV-Q5BVC&output=jsonp&callback=" + callbackName;
|
||
|
||
script.src = suggestUrl;
|
||
document.body.appendChild(script);
|
||
|
||
// 添加超时处理
|
||
setTimeout(function() {
|
||
if (window[callbackName]) {
|
||
console.error("联想搜索请求超时");
|
||
document.getElementById('search-results').style.display = 'none';
|
||
if (script.parentNode) document.body.removeChild(script);
|
||
delete window[callbackName];
|
||
}
|
||
}, 5000);
|
||
}
|
||
|
||
// 显示搜索联想结果
|
||
function showSuggestionResults(results) {
|
||
var container = document.getElementById('search-results');
|
||
container.innerHTML = '';
|
||
|
||
if (results && results.length > 0) {
|
||
results.forEach(function(result) {
|
||
var div = document.createElement('div');
|
||
div.className = 'search-result-item';
|
||
div.innerHTML = `
|
||
<div class="uk-text-bold">${result.title}</div>
|
||
<div class="uk-text-small uk-text-muted">${result.address || ''}</div>
|
||
`;
|
||
|
||
div.onclick = function() {
|
||
// 获取详细信息
|
||
getPlaceDetail(result.id);
|
||
};
|
||
|
||
container.appendChild(div);
|
||
});
|
||
|
||
container.style.display = 'block';
|
||
} else {
|
||
container.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// 修改搜索函数,添加错误处理
|
||
function searchPlace(keyword) {
|
||
if (!keyword || keyword.trim() === '') {
|
||
UIkit.notification({
|
||
message: '请输入搜索关键词',
|
||
status: 'warning',
|
||
pos: 'top-center',
|
||
timeout: 3000
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 显示加载提示和调试信息
|
||
console.log("开始搜索:", keyword);
|
||
UIkit.notification({
|
||
message: '正在搜索...',
|
||
status: 'primary',
|
||
pos: 'top-center',
|
||
timeout: 2000
|
||
});
|
||
|
||
// 使用唯一的回调函数名称避免冲突
|
||
var callbackName = 'jsonp_search_' + Math.round(100000 * Math.random());
|
||
var script = document.createElement('script');
|
||
|
||
window[callbackName] = function(res) {
|
||
console.log("搜索结果:", res);
|
||
if (script.parentNode) document.body.removeChild(script);
|
||
delete window[callbackName];
|
||
|
||
if (res.status === 0 && res.data && res.data.length > 0) {
|
||
// 转换返回的数据格式
|
||
var places = res.data.map(function(item) {
|
||
return {
|
||
id: item.id,
|
||
title: item.title,
|
||
address: item.address,
|
||
location: {
|
||
lat: item.location.lat,
|
||
lng: item.location.lng
|
||
}
|
||
};
|
||
});
|
||
|
||
// 显示搜索结果列表
|
||
showSearchResults(places);
|
||
|
||
// 在地图上显示多个标记点
|
||
showMultipleMarkers(places);
|
||
|
||
// 调整地图视野以包含所有结果
|
||
fitMapToBounds(places);
|
||
} else {
|
||
UIkit.notification({
|
||
message: '未找到相关结果',
|
||
status: 'warning',
|
||
pos: 'top-center',
|
||
timeout: 3000
|
||
});
|
||
}
|
||
};
|
||
|
||
var searchUrl = "https://apis.map.qq.com/ws/place/v1/search?keyword=" +
|
||
encodeURIComponent(keyword) +
|
||
"&boundary=region(全国,0)" +
|
||
"&page_size=10" +
|
||
"&page_index=1" +
|
||
"&key=BDMBZ-LZQ63-GUG37-OCHES-2ESXV-Q5BVC&output=jsonp&callback=" + callbackName;
|
||
|
||
script.src = searchUrl;
|
||
document.body.appendChild(script);
|
||
|
||
// 添加超时处理
|
||
setTimeout(function() {
|
||
if (window[callbackName]) {
|
||
console.error("搜索请求超时");
|
||
UIkit.notification({
|
||
message: '搜索超时,请重试',
|
||
status: 'danger',
|
||
pos: 'top-center',
|
||
timeout: 3000
|
||
});
|
||
if (script.parentNode) document.body.removeChild(script);
|
||
delete window[callbackName];
|
||
}
|
||
}, 5000);
|
||
}
|
||
|
||
// 修改获取地点详情函数
|
||
function getPlaceDetail(id) {
|
||
var detailUrl = "https://apis.map.qq.com/ws/place/v1/detail?id=" +
|
||
encodeURIComponent(id) +
|
||
"&key=BDMBZ-LZQ63-GUG37-OCHES-2ESXV-Q5BVC&output=jsonp&callback=detailCallback";
|
||
|
||
var script = document.createElement('script');
|
||
script.src = detailUrl;
|
||
document.body.appendChild(script);
|
||
|
||
// 定义全局回调函数
|
||
window.detailCallback = function(res) {
|
||
console.log("地点详情:", res);
|
||
document.body.removeChild(script);
|
||
|
||
if (res.status === 0 && res.data) {
|
||
// 转换数据格式
|
||
var place = {
|
||
id: res.data.id,
|
||
title: res.data.title,
|
||
address: res.data.address,
|
||
location: {
|
||
lat: res.data.location.lat,
|
||
lng: res.data.location.lng
|
||
}
|
||
};
|
||
|
||
selectPlace(place);
|
||
} else {
|
||
UIkit.notification({
|
||
message: '获取地点详情失败',
|
||
status: 'danger',
|
||
pos: 'top-center',
|
||
timeout: 3000
|
||
});
|
||
}
|
||
};
|
||
}
|
||
|
||
// 选择地点
|
||
function selectPlace(place) {
|
||
var position = new TMap.LatLng(place.location.lat, place.location.lng);
|
||
|
||
// 更新地图视图
|
||
map.setCenter(position);
|
||
map.setZoom(16);
|
||
|
||
// 更新标记
|
||
marker.updateGeometries([{
|
||
id: 'current',
|
||
position: position,
|
||
styleId: 'selected'
|
||
}]);
|
||
|
||
// 显示信息窗口
|
||
showInfoWindow(position, place.title, place.address);
|
||
|
||
// 保存到历史记录
|
||
saveSearchHistory(place);
|
||
|
||
// 隐藏搜索结果
|
||
document.getElementById('search-results').style.display = 'none';
|
||
}
|
||
|
||
// 在地图上显示多个标记点 - 修复图标URL
|
||
function showMultipleMarkers(places) {
|
||
// 清除之前的搜索结果标记
|
||
clearSearchMarkers();
|
||
console.log("显示多个标记点:", places.length);
|
||
|
||
// 准备新的标记点数据
|
||
var geometries = [];
|
||
|
||
places.forEach(function(place, index) {
|
||
// 确保所有必要属性存在
|
||
if (!place.location || !place.location.lat || !place.location.lng) {
|
||
console.error("地点数据不完整:", place);
|
||
return;
|
||
}
|
||
|
||
var position = new TMap.LatLng(place.location.lat, place.location.lng);
|
||
|
||
geometries.push({
|
||
id: 'search_' + index,
|
||
position: position,
|
||
styleId: 'searchResult',
|
||
properties: {
|
||
title: place.title || "",
|
||
address: place.address || ""
|
||
}
|
||
});
|
||
});
|
||
|
||
console.log("创建几何标记:", geometries.length);
|
||
|
||
// 创建新的标记点图层 - 使用正确的图标URL
|
||
if (geometries.length > 0) {
|
||
try {
|
||
var searchResultMarker = new TMap.MultiMarker({
|
||
map: map,
|
||
styles: {
|
||
"searchResult": new TMap.MarkerStyle({
|
||
width: 25,
|
||
height: 35,
|
||
anchor: { x: 12.5, y: 35 },
|
||
src: markerIconSearch
|
||
})
|
||
},
|
||
geometries: geometries
|
||
});
|
||
|
||
// 添加点击事件
|
||
searchResultMarker.on('click', function(evt) {
|
||
var properties = evt.geometry.properties;
|
||
var position = evt.geometry.position;
|
||
|
||
// 显示信息窗口
|
||
showInfoWindow(position, properties.title, properties.address);
|
||
|
||
// 更新当前选中标记
|
||
marker.updateGeometries([{
|
||
id: 'current',
|
||
position: position,
|
||
styleId: 'selected'
|
||
}]);
|
||
});
|
||
|
||
// 保存到搜索标记数组
|
||
searchMarkers.push(searchResultMarker);
|
||
console.log("标记创建成功");
|
||
} catch (error) {
|
||
console.error("创建标记时出错:", error);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 清除搜索结果标记
|
||
function clearSearchMarkers() {
|
||
searchMarkers.forEach(function(markerLayer) {
|
||
markerLayer.setMap(null);
|
||
});
|
||
searchMarkers = [];
|
||
}
|
||
|
||
// 调整地图视野以包含所有结果
|
||
function fitMapToBounds(places) {
|
||
if (!places || places.length === 0) return;
|
||
|
||
// 如果只有一个结果,直接定位到该位置
|
||
if (places.length === 1) {
|
||
var position = new TMap.LatLng(places[0].location.lat, places[0].location.lng);
|
||
map.setCenter(position);
|
||
map.setZoom(16);
|
||
return;
|
||
}
|
||
|
||
// 计算包含所有结果的视野范围
|
||
var bounds = new TMap.LatLngBounds();
|
||
places.forEach(function(place) {
|
||
bounds.extend(new TMap.LatLng(place.location.lat, place.location.lng));
|
||
});
|
||
|
||
// 设置地图视野
|
||
map.fitBounds(bounds, {
|
||
padding: 100 // 设置边距,避免标记点太靠近边缘
|
||
});
|
||
}
|
||
|
||
// 显示搜索结果 - 修复版本
|
||
function showSearchResults(results) {
|
||
console.log("显示搜索结果列表:", results.length);
|
||
|
||
var container = document.getElementById('search-results');
|
||
container.innerHTML = '';
|
||
|
||
if (results && results.length > 0) {
|
||
results.forEach(function(result) {
|
||
var div = document.createElement('div');
|
||
div.className = 'search-result-item';
|
||
div.innerHTML = `
|
||
<div class="uk-text-bold">${result.title || ""}</div>
|
||
<div class="uk-text-small uk-text-muted">${result.address || ""}</div>
|
||
`;
|
||
|
||
div.onclick = function() {
|
||
selectSearchResult(result);
|
||
};
|
||
|
||
container.appendChild(div);
|
||
});
|
||
|
||
container.style.display = 'block';
|
||
} else {
|
||
container.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// 添加用户定位功能
|
||
function locateUser() {
|
||
if (navigator.geolocation) {
|
||
navigator.geolocation.getCurrentPosition(
|
||
(position) => {
|
||
const userPosition = new TMap.LatLng(position.coords.latitude, position.coords.longitude);
|
||
map.setCenter(userPosition);
|
||
marker.updateGeometries([{
|
||
id: 'current',
|
||
position: userPosition,
|
||
styleId: 'selected'
|
||
}]);
|
||
},
|
||
(error) => {
|
||
UIkit.notification({
|
||
message: '无法获取您的位置,请检查位置权限',
|
||
status: 'warning',
|
||
pos: 'top-center',
|
||
timeout: 3000
|
||
});
|
||
}
|
||
);
|
||
} else {
|
||
UIkit.notification({
|
||
message: '您的浏览器不支持地理定位',
|
||
status: 'danger',
|
||
pos: 'top-center',
|
||
timeout: 3000
|
||
});
|
||
}
|
||
}
|
||
|
||
// 添加历史记录功能
|
||
function saveSearchHistory(item) {
|
||
let history = JSON.parse(localStorage.getItem('searchHistory') || '[]');
|
||
// 避免重复添加
|
||
history = history.filter(h => h.title !== item.title);
|
||
history.unshift(item);
|
||
// 最多保存10条记录
|
||
if (history.length > 10) history.pop();
|
||
localStorage.setItem('searchHistory', JSON.stringify(history));
|
||
}
|
||
|
||
function showSearchHistory() {
|
||
const history = JSON.parse(localStorage.getItem('searchHistory') || '[]');
|
||
if (history.length > 0) {
|
||
showSearchResults(history);
|
||
}
|
||
}
|
||
|
||
// 选择搜索结果 - 确保title正确传递
|
||
function selectSearchResult(result) {
|
||
// 调试输出
|
||
console.log("选择搜索结果:", result);
|
||
|
||
const position = new TMap.LatLng(result.location.lat, result.location.lng);
|
||
|
||
// 更新地图视图
|
||
map.setCenter(position);
|
||
map.setZoom(16);
|
||
|
||
// 更新标记
|
||
marker.updateGeometries([{
|
||
id: 'current',
|
||
position: position,
|
||
styleId: 'selected'
|
||
}]);
|
||
|
||
// 确保传递完整的title和address
|
||
showInfoWindow(position, result.title || "", result.address || "");
|
||
|
||
// 保存到历史记录
|
||
saveSearchHistory(result);
|
||
|
||
// 隐藏搜索结果
|
||
document.getElementById('search-results').style.display = 'none';
|
||
}
|
||
|
||
// 初始化地图
|
||
initMap();
|
||
</script>
|
||
{% endblock %} |