参考(地图)
<!-- 地图逻辑 -->
(function() {
'use strict';
let travelData = [];
try {
const labelData = '<%= note.getLabelValue("travelData") || "" %>';
if (labelData) {
travelData = JSON.parse(labelData);
} else {
let contentData = `<%- content %>`;
if (contentData.includes('<pre>')) {
contentData = contentData.replace(/<\/?pre>/g, '').trim();
}
contentData = contentData.replace(/[\r\n\t]/g, '').trim();
if (contentData && contentData.startsWith('[')) {
travelData = JSON.parse(contentData);
}
}
console.log('✅ 加载了', travelData.length, '个地点');
} catch(e) {
console.error('❌ 数据解析失败:', e);
}
function calculateStats(data) {
const countries = new Set();
const provinces = new Set();
const cities = new Set();
data.forEach(place => {
if (place.location) {
const parts = place.location.split('/').map(s => s.trim());
if (parts.length === 3) {
countries.add(parts[0]);
provinces.add(parts[1]);
cities.add(parts[2]);
} else if (parts.length === 2) {
provinces.add(parts[0]);
cities.add(parts[1]);
countries.add('中国');
}
}
if (place.tags) {
place.tags.forEach(tag => {
if (tag.startsWith('国家:')) countries.add(tag.split(':')[1]);
if (tag.startsWith('省份:')) provinces.add(tag.split(':')[1]);
if (tag.startsWith('城市:')) cities.add(tag.split(':')[1]);
});
}
});
return {
countries: countries.size,
provinces: provinces.size,
cities: cities.size
};
}
const stats = calculateStats(travelData);
if (travelData.length === 0) {
document.getElementById('places-list').innerHTML =
'<p style="text-align:center;color:#999;padding:30px 20px;">暂无足迹数据<br><small>请在笔记中添加 JSON 格式的地点信息</small></p>';
}
const mapEl = document.getElementById('travel-map');
const sidebar = document.getElementById('travel-sidebar');
const toggleBtn = document.getElementById('toggle-sidebar-btn');
const closeBtn = document.getElementById('close-sidebar-btn');
const placesList = document.getElementById('places-list');
const searchInput = document.getElementById('place-search');
const placesCountBadge = document.getElementById('places-count');
const modal = document.getElementById('place-modal');
const modalBody = document.getElementById('modal-body');
const modalClose = document.querySelector('.modal-close');
const statCountries = document.getElementById('stat-countries');
const statProvinces = document.getElementById('stat-provinces');
const statCities = document.getElementById('stat-cities');
function animateCount(element, target) {
let current = 0;
const increment = target / 30;
const timer = setInterval(() => {
current += increment;
if (current >= target) {
element.textContent = target;
clearInterval(timer);
} else {
element.textContent = Math.floor(current);
}
}, 30);
}
animateCount(statCountries, stats.countries);
animateCount(statProvinces, stats.provinces);
animateCount(statCities, stats.cities);
const map = L.map('travel-map').setView([35.0, 110.0], 4);
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: ['1', '2', '3', '4'],
attribution: '© 高德地图'
}).addTo(map);
let markers = [];
toggleBtn.addEventListener('click', () => {
sidebar.classList.toggle('collapsed');
});
closeBtn.addEventListener('click', () => {
sidebar.classList.add('collapsed');
});
function renderPlacesList(places = travelData) {
placesCountBadge.textContent = places.length;
placesList.innerHTML = '';
if (places.length === 0) {
placesList.innerHTML = '<p style="text-align:center;color:#999;padding:20px;">无匹配结果</p>';
return;
}
places.forEach(place => {
const item = document.createElement('div');
item.className = 'place-item';
item.dataset.id = place.id;
item.innerHTML = `
<div class="place-title">${place.title}</div>
<div class="place-date">📅 ${place.date}</div>
${place.tags ? `<div class="place-tags">${place.tags.map(t => `<span class="place-tag">${t}</span>`).join('')}</div>` : ''}
`;
item.addEventListener('click', () => {
showPlaceDetail(place);
document.querySelectorAll('.place-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
map.flyTo([place.lat, place.lng], 12, { duration: 1.5 });
});
placesList.appendChild(item);
});
}
function addMarkers() {
markers.forEach(m => map.removeLayer(m));
markers = [];
travelData.forEach(place => {
const marker = L.marker([place.lat, place.lng]);
marker.bindPopup(`
<div style="min-width:180px;">
<h3 style="margin:0 0 8px 0;font-size:1.1em;">${place.title}</h3>
<p style="margin:5px 0;font-size:0.9em;color:#666;">📅 ${place.date}</p>
</div>
`);
marker.on('click', () => {
showPlaceDetail(place);
document.querySelectorAll('.place-item').forEach(item => {
if (item.dataset.id === place.id) {
item.classList.add('active');
item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} else {
item.classList.remove('active');
}
});
sidebar.classList.remove('collapsed');
});
marker.addTo(map);
markers.push(marker);
});
if (markers.length > 0) {
const group = L.featureGroup(markers);
map.fitBounds(group.getBounds().pad(0.1));
}
}
function showPlaceDetail(place) {
modalBody.innerHTML = `
<h2 class="modal-title">${place.title}</h2>
<div class="modal-meta">
<span>📅 ${place.date}</span>
<span>📍 ${place.lat.toFixed(4)}, ${place.lng.toFixed(4)}</span>
${place.location ? `<span>🌏 ${place.location}</span>` : ''}
</div>
${place.description ? `<div class="modal-description">${place.description}</div>` : ''}
${place.tags ? `<div style="margin-bottom:25px;">${place.tags.map(t => `<span class="place-tag" style="background:#5986D1;color:white;padding:6px 14px;margin-right:8px;font-size:0.9em;">${t}</span>`).join('')}</div>` : ''}
${place.photos ? `<div class="modal-photos">${place.photos.map(p => `<img src="${p}" class="modal-photo" data-fancybox="gallery-${place.id}" alt="${place.title}">`).join('')}</div>` : ''}
`;
modal.classList.add('show');
if (typeof Fancybox !== 'undefined' && place.photos) {
Fancybox.bind(`[data-fancybox="gallery-${place.id}"]`, { Hash: false });
}
}
searchInput.addEventListener('input', (e) => {
const term = e.target.value.toLowerCase();
const filtered = term ? travelData.filter(p =>
p.title.toLowerCase().includes(term) ||
(p.description && p.description.toLowerCase().includes(term)) ||
(p.location && p.location.toLowerCase().includes(term)) ||
(p.tags && p.tags.some(t => t.toLowerCase().includes(term)))
) : travelData;
renderPlacesList(filtered);
});
modalClose.addEventListener('click', () => modal.classList.remove('show'));
modal.addEventListener('click', (e) => {
if (e.target === modal) modal.classList.remove('show');
});
renderPlacesList();
addMarkers();
console.log('🗺️ 地图初始化完成 | 统计:', stats);
})();
项目分区导航:⬅️ 02-参考(图谱) | 03-参考(地图) | ➡️ 04-古建筑
💬 评论