// 全局变量 let allAlbums = []; // 存储所有相册数据 let displayedAlbums = []; // 当前显示的相册数据 let filteredAlbums = null; // 过滤后的相册数据(有筛选时使用,null=无筛选) let searchTimeout = null; let currentDisplayPage = 1; // 前端显示的分页 let albumsPerPage = 50; // 每次显示50个相册 let isLoadingMore = false; let hasMoreAlbums = true; let lazyLoadObserver = null; let isAllAlbumsLoaded = false; // 标记是否已加载所有相册数据 let isInFavoritesPage = false; // 标记当前是否在收藏页 // 标签和人物筛选 let currentFilter = null; // { type: 'tag'|'person', value: string } let collapsedSections = { people: false, tags: false }; // 侧边栏状态(持久化) let sidebarCollapsed = localStorage.getItem('sidebarCollapsed') === 'true'; // API基础URL const API_BASE = '/api'; // 图片加载器类 class ImageLoader { constructor() { this.cache = new Map(); } // 加载图片 async loadImage(imageId, element) { // 检查缓存 if (this.cache.has(imageId)) { const cachedData = this.cache.get(imageId); this.setImageSrc(element, cachedData.blobUrl, cachedData.mimeType); return; } try { const response = await fetch(`${API_BASE}/image/${imageId}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const contentType = response.headers.get('content-type'); const imageData = await response.blob(); // 创建Blob URL const mimeType = contentType || 'image/jpeg'; const blob = new Blob([imageData], { type: mimeType }); const blobUrl = URL.createObjectURL(blob); // 缓存结果 this.cache.set(imageId, { blobUrl, mimeType }); // 设置图片源 this.setImageSrc(element, blobUrl, mimeType); } catch (error) { console.error('图片加载失败:', error); // 加载失败时使用占位图 element.src = '/assets/placeholder.svg'; } } // 设置图片源 setImageSrc(element, blobUrl, mimeType) { // 创建新的Image对象进行预加载 const img = new Image(); img.onload = () => { element.src = blobUrl; element.classList.add('loaded'); }; img.onerror = () => { console.error('图片解码失败'); element.src = '/assets/placeholder.svg'; }; img.src = blobUrl; } // 清理缓存 clearCache() { // 清理所有Blob URL for (const [imageId, cached] of this.cache.entries()) { URL.revokeObjectURL(cached.blobUrl); } this.cache.clear(); } } // 创建全局图片加载器实例 const imageLoader = new ImageLoader(); // 设备检测 const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', function () { // 添加设备类到body if (isMobile) { document.body.classList.add('mobile-device'); } if (isTouchDevice) { document.body.classList.add('touch-device'); } initSearch(); initInfiniteScroll(); initLazyLoading(); // 初始化图片懒加载 initSidebarSections(); // 初始化侧边栏区块折叠状态 // 检查URL中是否有search参数 const urlParams = new URLSearchParams(window.location.search); const searchQuery = urlParams.get('search'); if (searchQuery) { const searchInput = document.getElementById('search-input'); searchInput.value = searchQuery; // 直接搜索,不先加载全部相册 searchAlbums(); } else { loadAlbums(); } }); // 初始化搜索功能 function initSearch() { const searchInput = document.getElementById('search-input'); // 防抖搜索 searchInput.addEventListener('input', function () { clearTimeout(searchTimeout); // 优化:根据输入长度动态调整延迟 const inputLength = this.value.length; const debounceDelay = inputLength > 3 ? 200 : 500; // 长文本快速搜索,短文本慢速搜索 searchTimeout = setTimeout(debouncedSearch, debounceDelay); }); // 移动端优化:搜索框聚焦时自动滚动到可视区域 if (isMobile) { searchInput.addEventListener('focus', function () { setTimeout(() => { searchInput.scrollIntoView({behavior: 'smooth', block: 'center'}); }, 100); }); } } // 初始化无限滚动 function initInfiniteScroll() { let scrollTimeout; window.addEventListener('scroll', function () { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(handleScroll, 100); }); } // 初始化图片懒加载 function initLazyLoading() { // 使用Intersection Observer实现懒加载 if ('IntersectionObserver' in window) { if (lazyLoadObserver) { lazyLoadObserver.disconnect(); } lazyLoadObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; const imageId = img.getAttribute('data-image-id'); if (imageId && !img.classList.contains('loaded')) { imageLoader.loadImage(imageId, img); } lazyLoadObserver.unobserve(img); } }); }, { rootMargin: '100px 0px', // 提前100px开始加载 threshold: 0.1 }); // 观察所有带有data-image-id的图片 document.querySelectorAll('img[data-image-id]').forEach(img => { if (!img.classList.contains('loaded')) { lazyLoadObserver.observe(img); } }); } else { // 不支持Intersection Observer的回退方案 console.warn('浏览器不支持Intersection Observer,使用回退懒加载方案'); initFallbackLazyLoading(); } } // 回退懒加载方案 function initFallbackLazyLoading() { let scrollTimeout; function checkImagesInView() { const images = document.querySelectorAll('img[data-image-id]:not(.loaded)'); const viewportHeight = window.innerHeight; const scrollTop = window.scrollY; images.forEach(img => { const rect = img.getBoundingClientRect(); const isInView = rect.top < viewportHeight + 200 && rect.bottom > -200; if (isInView) { const imageId = img.getAttribute('data-image-id'); if (imageId) { imageLoader.loadImage(imageId, img); } } }); } window.addEventListener('scroll', () => { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(checkImagesInView, 100); }); // 初始检查 checkImagesInView(); } // 处理滚动事件 function handleScroll() { // 如果在收藏页,禁用无限滚动 if (isInFavoritesPage) return; if (isLoadingMore || !hasMoreAlbums) return; // 检查是否滚动到底部 const scrollTop = window.scrollY || document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; const clientHeight = window.innerHeight; // 当距离底部还有200px时开始加载 if (scrollTop + clientHeight >= scrollHeight - 200) { loadMoreAlbums(); } } // 加载更多相册(从前端已加载的数据中获取) async function loadMoreAlbums() { // 如果在收藏页,不加载更多数据 if (isInFavoritesPage) return; if (isLoadingMore || !hasMoreAlbums) return; // 全量模式下(无筛选)需等 isAllAlbumsLoaded;过滤模式下直接使用 filteredAlbums if (!currentFilter && !isAllAlbumsLoaded) return; isLoadingMore = true; try { // 显示加载更多指示器 showLoadingMore(); // 增加当前显示页数 currentDisplayPage++; // 显示下一页数据 displayCurrentPage(); // 初始化图片懒加载 initLazyLoading(); // 预加载下一批图片 preloadNextBatchImages(); } catch (error) { console.error('加载更多相册失败:', error); showLoadMoreError(); } finally { isLoadingMore = false; hideLoadingMore(); } } // 显示加载更多错误 function showLoadMoreError() { const container = document.getElementById('albums-container'); const errorElement = document.createElement('div'); errorElement.className = 'load-more-error'; errorElement.innerHTML = `
加载失败,点击重试
`; const existingError = container.querySelector('.load-more-error'); if (existingError) { existingError.remove(); } container.appendChild(errorElement); } // 显示骨架屏 function showSkeleton(count = 6) { const container = document.getElementById('albums-container'); container.innerHTML = Array(count).fill(0).map(() => `
`).join(''); } // 显示详细加载状态 function showLoadingState(message = '加载中...', progress = 0) { const container = document.getElementById('albums-container'); const progressWidth = Math.min(Math.max(progress, 0), 100); container.innerHTML = `

${message}

`; } // 显示加载更多指示器 function showLoadingMore() { const container = document.getElementById('albums-container'); const loadingIndicator = document.createElement('div'); loadingIndicator.id = 'loading-more'; loadingIndicator.className = 'loading-more'; loadingIndicator.innerHTML = `
加载更多相册...
`; container.appendChild(loadingIndicator); } // 隐藏加载更多指示器 function hideLoadingMore() { const loadingIndicator = document.getElementById('loading-more'); if (loadingIndicator) { loadingIndicator.remove(); } } // 追加相册到列表 function appendAlbums(albums) { const container = document.getElementById('albums-container'); const newAlbumsHTML = albums.map(album => { // 处理封面图片URL - 使用懒加载 let coverImageUrl = '/assets/placeholder.svg'; let dataSrc = ''; if (album.cover_image_id) { dataSrc = `${API_BASE}/image/${album.cover_image_id}`; coverImageUrl = '/assets/placeholder.svg'; // 初始使用占位图 } // 格式化日期 const createdDate = new Date(album.created_at).toLocaleDateString('zh-CN'); return `
${album.title}

${album.title}

${album.image_count} 张图片 ${createdDate}
`; }).join(''); // 移除加载指示器并添加新相册 hideLoadingMore(); container.insertAdjacentHTML('beforeend', newAlbumsHTML); // 重新初始化懒加载,观察新添加的图片 initLazyLoading(); } // 加载所有相册数据(一次性加载全部) async function loadAlbums(reset = false) { try { if (reset) { currentDisplayPage = 1; allAlbums = []; displayedAlbums = []; hasMoreAlbums = true; isAllAlbumsLoaded = false; const container = document.getElementById('albums-container'); container.innerHTML = ''; } // 如果已经加载过所有数据,直接显示当前页 if (isAllAlbumsLoaded) { displayCurrentPage(); return; } // 显示骨架屏 showSkeleton(); // 一次性加载所有相册数据 const response = await fetch(`${API_BASE}/albums`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } allAlbums = await response.json(); isAllAlbumsLoaded = true; // 显示第一页数据 displayCurrentPage(); updateStats(allAlbums); // 初始化图片懒加载 initLazyLoading(); // 预加载下一批图片 preloadNextBatchImages(); // 加载热门标签和人物 loadDiscoverData(); } catch (error) { console.error('加载相册失败:', error); showError('加载相册数据失败,请刷新页面重试'); } } // 显示当前页的相册数据 function displayCurrentPage() { // 有筛选时用 filteredAlbums,否则用 allAlbums const source = filteredAlbums || allAlbums; const startIndex = (currentDisplayPage - 1) * albumsPerPage; const endIndex = startIndex + albumsPerPage; // 获取当前页要显示的相册 displayedAlbums = source.slice(startIndex, endIndex); // 如果是第一页,直接显示;否则追加显示 if (currentDisplayPage === 1) { displayAlbums(displayedAlbums); } else { appendAlbums(displayedAlbums); } // 检查是否还有更多数据可以显示 hasMoreAlbums = (currentDisplayPage * albumsPerPage) < source.length; } // 搜索相册 // 防抖搜索函数 async function debouncedSearch() { const searchTerm = document.getElementById('search-input').value.toLowerCase().trim(); if (searchTerm === '') { // 搜索框为空 → 清除过滤状态,显示所有相册 currentFilter = null; filteredAlbums = null; displayAlbums(allAlbums); updateStats(); return; } // 直接使用后端搜索API进行全站搜索 const results = await performServerSearchWithResult(searchTerm); if (results && results.length > 0) { filteredAlbums = results; currentDisplayPage = 1; hasMoreAlbums = results.length > albumsPerPage; displayAlbums(results); updateStats(results); initLazyLoading(); } } // 在当前已加载的数据集中搜索 function searchInLoadedAlbums(searchTerm) { if (!allAlbums.length) return []; return allAlbums.filter(album => { // 搜索标题和描述 const titleMatch = album.title.toLowerCase().includes(searchTerm); const descriptionMatch = album.description && album.description.toLowerCase().includes(searchTerm); return titleMatch || descriptionMatch; }); } // 显示搜索提示 function showSearchHint(searchTerm) { const container = document.getElementById('albums-container'); container.innerHTML = `

未在当前页面找到 "${searchTerm}"

尝试:

`; document.getElementById('stats').innerHTML = `在当前页面中未找到包含 "${searchTerm}" 的相册`; } // 执行服务器端搜索(全站搜索) async function performServerSearch(searchTerm) { const results = await performServerSearchWithResult(searchTerm); if (results && results.length > 0) { displayAlbums(results); const oldStats = document.getElementById('stats'); if (oldStats) { oldStats.innerHTML = `在全站找到 ${results.length} 个包含 "${searchTerm}" 的相册`; } updateStats(results); initLazyLoading(); } } /// 执行服务器端搜索并返回结果数组(不直接渲染) async function performServerSearchWithResult(searchTerm) { try { const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(searchTerm)}&page=1&limit=200`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const serverResults = await response.json(); if (serverResults.length === 0) { return []; } return serverResults; } catch (error) { console.error('全站搜索失败:', error); return []; } } // 显示相册列表 function displayAlbums(albums) { const container = document.getElementById('albums-container'); if (albums.length === 0) { container.innerHTML = '

没有找到匹配的相册
'; return; } container.innerHTML = albums.map(album => { // 处理封面图片URL - 使用懒加载 let coverImageUrl = '/assets/placeholder.svg'; let dataSrc = ''; if (album.cover_image_id) { dataSrc = `${API_BASE}/image/${album.cover_image_id}`; coverImageUrl = '/assets/placeholder.svg'; // 初始使用占位图 } // 格式化日期 const createdDate = new Date(album.created_at).toLocaleDateString('zh-CN'); return `
${album.title}

${album.title}

${album.image_count} 张图片 ${createdDate}
`; }).join(''); } // 预加载下一批图片 function preloadNextBatchImages() { const imagesToPreload = []; const visibleImages = document.querySelectorAll('img[data-image-id]:not(.loaded)'); // 预加载前5张可见图片 visibleImages.forEach((img, index) => { if (index < 5) { const imageId = img.getAttribute('data-image-id'); if (imageId) { imagesToPreload.push(imageId); } } }); // 使用图片加载器预加载 imagesToPreload.forEach(imageId => { const tempImg = new Image(); imageLoader.loadImage(imageId, tempImg); }); } // 更新统计信息 function updateStats(albums) { if (!albums) { albums = allAlbums; } const totalImages = albums.reduce((sum, album) => sum + album.image_count, 0); // 更新统计条 const statAlbums = document.getElementById('stat-albums'); const statImages = document.getElementById('stat-images'); if (statAlbums) statAlbums.textContent = albums.length.toLocaleString(); if (statImages) statImages.textContent = totalImages.toLocaleString(); // 兼容旧版 stats const oldStats = document.getElementById('stats'); if (oldStats) oldStats.innerHTML = `共发现 ${albums.length} 个相册,包含 ${totalImages} 张图片`; // 获取人物数量(用 metadata stats API) fetch(`${API_BASE}/metadata/stats`).then(r => r.ok && r.json()).then(data => { if (data && data.total_persons !== undefined) { const statPeople = document.getElementById('stat-people'); if (statPeople) statPeople.textContent = data.total_persons.toLocaleString(); } }).catch(() => {}); } // 显示错误信息 function showError(message) { const container = document.getElementById('albums-container'); container.innerHTML = `

${message}
`; const oldStats = document.getElementById('stats'); if (oldStats) oldStats.innerHTML = ''; } // 搜索相册函数 async function searchAlbums() { await debouncedSearch(); } // 性能监控 function logPerformance() { if ('performance' in window) { // 使用更可靠的方式获取页面加载时间 const calculateLoadTime = () => { const navigationTiming = performance.getEntriesByType('navigation')[0]; if (navigationTiming && navigationTiming.loadEventEnd > 0 && navigationTiming.navigationStart > 0) { const loadTime = navigationTiming.loadEventEnd - navigationTiming.navigationStart; console.log('页面加载时间:', loadTime, 'ms'); } else { // 备用方案:使用DOMContentLoaded时间 const domContentLoadedTime = performance.timing?.domContentLoadedEventEnd - performance.timing?.navigationStart; if (domContentLoadedTime && domContentLoadedTime > 0) { console.log('DOM加载时间:', domContentLoadedTime, 'ms'); } else { console.log('页面加载时间: 无法计算'); } } }; // 如果页面已经加载完成,直接计算 if (document.readyState === 'complete') { calculateLoadTime(); } else { // 否则等待页面加载完成 window.addEventListener('load', calculateLoadTime); } } } // 移动端优化:防止双击缩放 function preventDoubleTapZoom() { if (isTouchDevice) { let lastTouchEnd = 0; document.addEventListener('touchend', function (event) { const now = (new Date()).getTime(); if (now - lastTouchEnd <= 300) { event.preventDefault(); } lastTouchEnd = now; }, false); } } // 预加载关键资源 function preloadCriticalResources() { // 预加载字体文件(CSS文件不应该预加载,因为已经在HTML中引用了) const fontResources = [ '/assets/webfonts/fa-solid-900.woff2', '/assets/webfonts/fa-brands-400.woff2', '/assets/webfonts/fa-regular-400.woff2' ]; fontResources.forEach(resource => { const link = document.createElement('link'); link.rel = 'preload'; link.href = resource; link.as = 'font'; link.crossOrigin = 'anonymous'; document.head.appendChild(link); }); // 预加载第一屏可能需要的图片 setTimeout(() => { const firstScreenImages = [ '/assets/placeholder.svg' ]; firstScreenImages.forEach(img => { const preload = document.createElement('link'); preload.rel = 'preload'; preload.href = img; preload.as = 'image'; document.head.appendChild(preload); }); }, 100); } // 优化滚动性能 function optimizeScrollPerformance() { // 使用requestAnimationFrame优化滚动 let ticking = false; function updateOnScroll() { // 在滚动时暂停非关键任务 if (!ticking) { requestAnimationFrame(() => { // 延迟加载非关键图片 delayNonCriticalImages(); ticking = false; }); ticking = true; } } window.addEventListener('scroll', updateOnScroll, {passive: true}); } // 延迟加载非关键图片 function delayNonCriticalImages() { const nonCriticalImages = document.querySelectorAll('img[data-delayed]'); nonCriticalImages.forEach(img => { if (isElementInViewport(img)) { const src = img.getAttribute('data-delayed'); if (src) { img.src = src; img.removeAttribute('data-delayed'); } } }); } // 检查元素是否在视口中 function isElementInViewport(el) { const rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } // 初始化性能监控和移动端优化 logPerformance(); preventDoubleTapZoom(); // 预加载关键资源 preloadCriticalResources(); // 优化滚动性能 optimizeScrollPerformance(); // 打开相册详情 function openAlbum(albumId) { window.location.href = `/album?id=${albumId}`; } // 随机打开相册 function openRandomAlbum() { // 添加随机动画效果 - 兼容侧边栏导航项 const navItems = document.querySelectorAll('.nav-item'); let randomButton = null; navItems.forEach(item => { if (item.textContent.includes('随机')) randomButton = item; }); // 也试试顶部操作按钮 if (!randomButton) { randomButton = document.querySelector('.action-btn[onclick*="openRandomAlbum"]'); } if (randomButton) { randomButton.style.transform = 'scale(0.95)'; } // 调用新的随机相册API fetch(`${API_BASE}/album/random`) .then(response => { if (!response.ok) { if (response.status === 404) { throw new Error('暂无相册数据,请稍后再试'); } throw new Error('获取随机相册失败'); } return response.json(); }) .then(randomAlbum => { // 恢复按钮状态 if (randomButton) { setTimeout(() => { randomButton.style.transform = ''; }, 200); } // 跳转到随机相册 window.location.href = `/album?id=${randomAlbum.id}`; }) .catch(error => { // 恢复按钮状态 if (randomButton) { setTimeout(() => { randomButton.style.transform = ''; }, 200); } alert(error.message); }); } // 显示收藏夹 function showFavorites() { // 设置当前在收藏页 isInFavoritesPage = true; // 切换侧边栏选中状态 document.querySelectorAll('.nav-item').forEach(item => item.classList.remove('active')); const favNav = document.querySelector('.nav-item[data-view="favorites"]'); if (favNav) favNav.classList.add('active'); // 从后端API获取收藏列表 fetch(`${API_BASE}/favorites`) .then(response => { if (!response.ok) { throw new Error('获取收藏列表失败'); } return response.json(); }) .then(favoriteImages => { // 始终显示收藏夹视图(包括空状态) displayFavorites(favoriteImages); }) .catch(error => { console.error('获取收藏列表失败:', error); alert('获取收藏列表失败,请稍后重试'); }); } // 显示收藏夹内容 function displayFavorites(favoriteImages) { const container = document.getElementById('albums-container'); container.innerHTML = ` ${favoriteImages.length === 0 ? `

收藏夹空空如也

快去收藏一些喜欢的图片吧!

` : `${favoriteImages.map((fav, index) => `
${fav.image.title || '收藏的图片'}
${formatDate(fav.favorite.created_at)} ${fav.image.width}×${fav.image.height}
`).join('')} `} `; // 更新统计信息 - 显示收藏数量(兼容新旧版布局) const statsEl = document.getElementById('stats'); if (statsEl) { statsEl.innerHTML = `

我的收藏夹

共收藏了 ${favoriteImages.length} 张图片

`; } // 初始化图片懒加载,确保收藏的图片被加载 initLazyLoading(); } // 格式化日期 function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString('zh-CN', { year: 'numeric', month: 'short', day: 'numeric' }); } // 返回首页 function backToHome() { // 重置收藏页标记 isInFavoritesPage = false; // 切换导航高亮到首页 document.querySelectorAll('.nav-item').forEach(item => { item.classList.toggle('active', item.dataset.view === 'home'); }); // 清除筛选 clearFilter(); } // 全局变量存储收藏列表和当前预览索引 let currentFavorites = []; let currentPreviewIndex = -1; // 预览收藏的图片 function previewFavorite(index) { // 获取收藏列表 fetch(`${API_BASE}/favorites`) .then(response => { if (!response.ok) { throw new Error('获取收藏列表失败'); } return response.json(); }) .then(favorites => { if (favorites && favorites.length > index) { currentFavorites = favorites; currentPreviewIndex = index; showPreviewModal(index); } else { alert('图片不存在或已被删除'); } }) .catch(error => { console.error('预览图片失败:', error); alert('预览图片失败,请稍后重试'); }); } // 显示预览模态框 function showPreviewModal(index) { const favorite = currentFavorites[index]; // 构建模态框HTML const modalHtml = `
来自: ${favorite.album_title}
${index + 1} / ${currentFavorites.length}
${favorite.image.title || '预览图片'}
`; // 添加到页面 document.body.insertAdjacentHTML('beforeend', modalHtml); // 显示模态框 setTimeout(() => { const modal = document.getElementById('previewModal'); modal.classList.add('active'); }, 10); // 监听键盘事件 document.addEventListener('keydown', handlePreviewKeydown); } // 关闭预览模态框 function closePreviewModal() { const modal = document.getElementById('previewModal'); if (modal) { modal.classList.remove('active'); setTimeout(() => { modal.remove(); }, 300); } // 移除键盘事件监听 document.removeEventListener('keydown', handlePreviewKeydown); } // 导航预览图片 function navigatePreview(direction) { const newIndex = currentPreviewIndex + direction; if (newIndex >= 0 && newIndex < currentFavorites.length) { updatePreviewContent(newIndex); } } // 更新预览内容(不关闭面板) function updatePreviewContent(newIndex) { const favorite = currentFavorites[newIndex]; currentPreviewIndex = newIndex; // 更新标题和相册信息 const titleElement = document.querySelector('.preview-title h3'); const infoElement = document.querySelector('.preview-info'); if (titleElement) titleElement.textContent = favorite.image.title || '未命名图片'; if (infoElement) infoElement.textContent = `来自: ${favorite.album_title}`; // 更新计数器 const counterElement = document.querySelector('.preview-counter'); if (counterElement) counterElement.textContent = `${newIndex + 1} / ${currentFavorites.length}`; // 更新导航按钮状态 const prevButton = document.querySelector('.preview-prev'); const nextButton = document.querySelector('.preview-next'); if (prevButton) prevButton.disabled = newIndex === 0; if (nextButton) nextButton.disabled = newIndex === currentFavorites.length - 1; // 更新元数据 const metaElements = document.querySelectorAll('.preview-meta .meta-item'); if (metaElements.length >= 3) { metaElements[0].innerHTML = ` ${formatDate(favorite.favorite.created_at)}`; metaElements[1].innerHTML = ` ${favorite.image.width} × ${favorite.image.height}`; } // 更新操作按钮 const openAlbumButton = document.querySelector('.preview-action:first-child'); const removeButton = document.querySelector('.preview-remove'); if (openAlbumButton) { openAlbumButton.onclick = () => openFavoriteImage(favorite.favorite.album_id); } if (removeButton) { removeButton.onclick = () => removeFavoriteFromPreview(favorite.favorite.id); } // 更新图片 const imageElement = document.getElementById('preview-image'); if (imageElement) { // 显示加载状态 imageElement.style.opacity = '0'; // 加载新图片 const newImage = new Image(); newImage.src = `${API_BASE}/image/${favorite.image.id}`; newImage.alt = favorite.image.title || '预览图片'; newImage.className = 'preview-image'; newImage.onload = function () { // 替换图片 if (imageElement.parentNode) { imageElement.parentNode.replaceChild(newImage, imageElement); // 重新设置ID newImage.id = 'preview-image'; } newImage.style.opacity = '1'; }; newImage.onerror = function () { // 图片加载失败 imageElement.innerHTML = '图片加载失败'; }; } } // 处理键盘事件 function handlePreviewKeydown(event) { if (event.key === 'Escape') { closePreviewModal(); } else if (event.key === 'ArrowLeft') { navigatePreview(-1); } else if (event.key === 'ArrowRight') { navigatePreview(1); } } // 从预览中移除收藏 function removeFavoriteFromPreview(favoriteId) { if (confirm('确定要从收藏夹移除这张图片吗?')) { fetch(`${API_BASE}/favorites/${favoriteId}`, { method: 'DELETE' }) .then(response => { if (!response.ok) { throw new Error('取消收藏失败'); } return response.json(); }) .then(() => { closePreviewModal(); // 重新显示收藏夹 showFavorites(); }) .catch(error => { console.error('取消收藏失败:', error); alert('取消收藏失败,请稍后重试'); }); } } // 从收藏夹移除图片 function removeFavorite(event, favoriteId) { event.stopPropagation(); // 阻止事件冒泡 if (confirm('确定要从收藏夹移除这张图片吗?')) { fetch(`${API_BASE}/favorites/${favoriteId}`, { method: 'DELETE' }) .then(response => { if (!response.ok) { throw new Error('取消收藏失败'); } return response.json(); }) .then(() => { // 重新显示收藏夹 showFavorites(); }) .catch(error => { console.error('取消收藏失败:', error); alert('取消收藏失败,请稍后重试'); }); } } // 打开收藏的图片 function openFavoriteImage(favoriteId) { // 这里可以跳转到对应的相册页面 // 在实际实现中,我们需要根据favoriteId获取相册信息 window.location.href = `/album?id=${favoriteId}`; } // ============================================================ // 标签和人物发现功能 // ============================================================ // 加载热门标签和人物数据 async function loadDiscoverData() { try { // 并行加载 const [tagsResp, peopleResp] = await Promise.all([ fetch(`${API_BASE}/tags/popular?limit=40`), fetch(`${API_BASE}/recommend/popular-persons?limit=30`) ]); if (tagsResp.ok) { const tagsData = await tagsResp.json(); // 兼容两种格式:直接数组 或 { total, tags } const tags = Array.isArray(tagsData) ? tagsData : (tagsData.tags || tagsData.items || []); if (tags && tags.length > 0) { renderTagCloud(tags); } } if (peopleResp.ok) { const people = await peopleResp.json(); if (people && people.length > 0) { renderPeopleCloud(people); } } } catch (error) { console.warn('加载发现数据失败:', error); } } // 渲染标签(侧边栏列表) function renderTagCloud(tags) { // 新版:侧边栏标签列表 const sidebarTags = document.getElementById('sidebar-tags'); if (sidebarTags && tags && tags.length) { sidebarTags.innerHTML = tags.slice(0, 25).map(tag => { const count = tag.album_cnt || tag.count || 1; const name = tag.tag || tag.name || ''; const isActive = currentFilter && currentFilter.type === 'tag' && currentFilter.value === name; return ``; }).join(''); } // 兼容旧版:标签云区域 const section = document.getElementById('tags-section'); const cloud = document.getElementById('tag-cloud'); if (section && cloud && tags && tags.length) { section.style.display = 'block'; const maxCount = Math.max(...tags.map(t => t.album_cnt || t.count || 1)); const minCount = Math.min(...tags.map(t => t.album_cnt || t.count || 1)); cloud.innerHTML = tags.map(tag => { const count = tag.album_cnt || tag.count || 1; const name = tag.tag || tag.name || ''; const ratio = maxCount === minCount ? 0.5 : (count - minCount) / (maxCount - minCount); const fontSize = 12 + ratio * 10; const hue = 210 - ratio * 50; const opacity = 0.6 + ratio * 0.4; return ` ${name} (${count}) `; }).join(''); } } // 渲染热门人物(横向滚动条) function renderPeopleCloud(people) { // 侧边栏人物列表 const sidebarPeople = document.getElementById('sidebar-people'); if (sidebarPeople && people && people.length) { sidebarPeople.innerHTML = people.slice(0, 25).map(p => { const name = p.name || p.display_name || ''; const albumCount = p.total_albums || 0; const isActive = currentFilter && currentFilter.type === 'person' && currentFilter.value === name; return ``; }).join(''); } // 横向滚动条 const strip = document.getElementById('people-strip'); const scroll = document.getElementById('people-scroll'); if (strip && scroll && people && people.length) { strip.style.display = 'flex'; scroll.innerHTML = people.slice(0, 20).map(p => { const name = p.name || p.display_name || ''; const albumCount = p.total_albums || 0; const isActive = currentFilter && currentFilter.type === 'person' && currentFilter.value === name; return `
${name.charAt(0)}
${name}
${albumCount} 相册
`; }).join(''); } // 兼容旧版:人物卡片区域 const section = document.getElementById('people-section'); const cloud = document.getElementById('people-cloud'); if (section && cloud && people && people.length) { section.style.display = 'block'; cloud.innerHTML = people.map(p => { const name = p.name || p.display_name || ''; const albumCount = p.total_albums || 0; const imageCount = p.total_images || 0; return `
${name.charAt(0)}
${name}
${albumCount} 相册 · ${imageCount} 图
`; }).join(''); } } // 侧边栏区块折叠/展开 function toggleSidebarSection(section) { const group = document.querySelector(`.sidebar-section-group [data-section="${section}"]`)?.closest('.sidebar-section-group'); if (!group) return; group.classList.toggle('collapsed'); // 保存状态到 localStorage try { const state = JSON.parse(localStorage.getItem('sidebarSections') || '{}'); state[section] = group.classList.contains('collapsed'); localStorage.setItem('sidebarSections', JSON.stringify(state)); } catch(e) {} } // 初始化侧边栏区块折叠状态 function initSidebarSections() { try { const state = JSON.parse(localStorage.getItem('sidebarSections') || '{}'); Object.keys(state).forEach(section => { if (state[section]) { const group = document.querySelector(`.sidebar-section-group [data-section="${section}"]`)?.closest('.sidebar-section-group'); if (group) group.classList.add('collapsed'); } }); } catch(e) {} } // 按标签筛选 async function filterByTag(tag) { currentFilter = { type: 'tag', value: tag }; showFilterStatus(`标签: ${tag}`); renderTagCloud(await fetchTags().catch(() => [])); try { showSkeleton(6); const response = await fetch(`${API_BASE}/tags/search?tag=${encodeURIComponent(tag)}&limit=200`); if (!response.ok) throw new Error('搜索失败'); const results = await response.json(); // 保存过滤结果到 filteredAlbums,重置分页 filteredAlbums = results; currentDisplayPage = 1; hasMoreAlbums = filteredAlbums.length > albumsPerPage; displayAlbums(results); updateStats(results); initLazyLoading(); } catch (error) { console.error('标签筛选失败:', error); showError('标签筛选失败'); } } // 获取标签(辅助函数) async function fetchTags() { const resp = await fetch(`${API_BASE}/tags/popular?limit=25`); if (!resp.ok) return []; return resp.json(); } // 按人物筛选 async function filterByPerson(personName) { currentFilter = { type: 'person', value: personName }; showFilterStatus(`人物: ${personName}`); // 刷新侧边栏人物高亮 try { const peopleResp = await fetch(`${API_BASE}/recommend/popular-persons?limit=30`); if (peopleResp.ok) { const peopleData = await peopleResp.json(); renderPeopleCloud(peopleData); } } catch(e) {} try { showSkeleton(6); const response = await fetch(`${API_BASE}/people/search?q=${encodeURIComponent(personName)}`); if (!response.ok) throw new Error('搜索失败'); const personData = await response.json(); const persons = personData.persons || personData; if (persons && persons.length > 0) { try { const detailResp = await fetch(`${API_BASE}/people/${encodeURIComponent(personName)}`); if (detailResp.ok) { const detail = await detailResp.json(); if (detail && detail.albums) { // 保存过滤结果到 filteredAlbums,重置分页 filteredAlbums = detail.albums; currentDisplayPage = 1; hasMoreAlbums = filteredAlbums.length > albumsPerPage; displayAlbums(detail.albums); updateStats(detail.albums); initLazyLoading(); return; } } } catch (e) { /* 回退 */ } } // 回退到全站搜索 filteredAlbums = await performServerSearchWithResult(personName); } catch (error) { console.error('人物筛选失败:', error); filteredAlbums = await performServerSearchWithResult(personName); } // 无论回退搜索是否找到结果,都要渲染页面(移除骨架屏) if (filteredAlbums !== null) { currentDisplayPage = 1; hasMoreAlbums = filteredAlbums.length > albumsPerPage; displayAlbums(filteredAlbums); updateStats(filteredAlbums); initLazyLoading(); } } // 显示筛选状态 function showFilterStatus(text) { // 新版筛选条 const bar = document.getElementById('filter-bar'); const pill = document.getElementById('current-filter-pill'); if (bar && pill) { pill.textContent = text; bar.style.display = 'flex'; } // 兼容旧版 const status = document.getElementById('filter-status'); const label = document.getElementById('current-filter'); if (status && label) { label.textContent = text; status.style.display = 'flex'; } } // 清除筛选 function clearFilter() { currentFilter = null; filteredAlbums = null; // 清除过滤结果缓存 currentDisplayPage = 1; // 新版筛选条 const bar = document.getElementById('filter-bar'); if (bar) bar.style.display = 'none'; // 兼容旧版 const status = document.getElementById('filter-status'); if (status) status.style.display = 'none'; // 清空搜索框 const searchInput = document.getElementById('search-input'); if (searchInput) { searchInput.value = ''; updateSearchClear(); } // 重新渲染标签和人物(清除高亮) loadDiscoverData(); // 重新加载所有相册 loadAlbums(true); } // ============ 侧边栏交互 ============ // 初始化侧边栏状态 function initSidebar() { const sidebar = document.getElementById('sidebar'); if (sidebar && sidebarCollapsed) { sidebar.classList.add('collapsed'); } } // 切换侧边栏折叠 function toggleSidebar() { const sidebar = document.getElementById('sidebar'); if (!sidebar) return; sidebarCollapsed = !sidebarCollapsed; sidebar.classList.toggle('collapsed', sidebarCollapsed); localStorage.setItem('sidebarCollapsed', sidebarCollapsed); } // 移动端切换侧边栏 function toggleMobileSidebar() { const sidebar = document.getElementById('sidebar'); const overlay = document.getElementById('mobile-overlay'); if (!sidebar || !overlay) return; sidebar.classList.toggle('mobile-open'); overlay.classList.toggle('active'); } // 切换视图(首页/收藏夹) function switchView(view) { // 更新导航高亮 document.querySelectorAll('.nav-item').forEach(item => { item.classList.toggle('active', item.dataset.view === view); }); // 移动端关闭侧边栏 const sidebar = document.getElementById('sidebar'); const overlay = document.getElementById('mobile-overlay'); if (sidebar && sidebar.classList.contains('mobile-open')) { sidebar.classList.remove('mobile-open'); overlay.classList.remove('active'); } if (view === 'home') { backToHome(); } else if (view === 'favorites') { showFavorites(); } } // 搜索框清除按钮 function updateSearchClear() { const input = document.getElementById('search-input'); const clear = document.getElementById('search-clear'); if (input && clear) { clear.style.display = input.value.length > 0 ? 'flex' : 'none'; } } // 清除搜索 function clearSearch() { const input = document.getElementById('search-input'); if (input) { input.value = ''; updateSearchClear(); if (!currentFilter) { loadAlbums(true); } } } // 折叠/展开区域(兼容旧版) function toggleSection(name) { collapsedSections[name] = !collapsedSections[name]; const cloudId = name === 'people' ? 'people-cloud' : 'tag-cloud'; const cloud = document.getElementById(cloudId); const btn = document.querySelector(`#${name}-section .section-toggle i`); if (cloud) { if (collapsedSections[name]) { cloud.style.display = 'none'; btn.className = 'fas fa-chevron-down'; } else { cloud.style.display = ''; btn.className = 'fas fa-chevron-up'; } } } // ============ 初始化 ============ document.addEventListener('DOMContentLoaded', function() { initSidebar(); // 搜索框输入事件 const searchInput = document.getElementById('search-input'); if (searchInput) { searchInput.addEventListener('input', updateSearchClear); } // 键盘快捷键:ESC 关闭筛选/搜索 document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { if (currentFilter) clearFilter(); } }); }); // 导出函数供全局使用 window.searchAlbums = searchAlbums; window.openRandomAlbum = openRandomAlbum; window.showFavorites = showFavorites; window.openAlbum = openAlbum; window.backToHome = backToHome; window.loadAlbums = loadAlbums; window.removeFavorite = removeFavorite; window.previewFavorite = previewFavorite; window.openFavoriteImage = openFavoriteImage; window.removeFavoriteFromPreview = removeFavoriteFromPreview; window.filterByTag = filterByTag; window.filterByPerson = filterByPerson; window.clearFilter = clearFilter; window.toggleSection = toggleSection; window.toggleSidebar = toggleSidebar; window.toggleMobileSidebar = toggleMobileSidebar; window.switchView = switchView; window.clearSearch = clearSearch;