// 全局变量
let allAlbums = []; // 存储所有相册数据
let displayedAlbums = []; // 当前显示的相册数据
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 || !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 = `
`;
}
// 显示加载更多指示器
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/image/${album.cover_image_id}`;
coverImageUrl = '/assets/placeholder.svg'; // 初始使用占位图
}
// 格式化日期
const createdDate = new Date(album.created_at).toLocaleDateString('zh-CN');
return `
${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() {
const startIndex = (currentDisplayPage - 1) * albumsPerPage;
const endIndex = startIndex + albumsPerPage;
// 获取当前页要显示的相册
displayedAlbums = allAlbums.slice(startIndex, endIndex);
// 如果是第一页,直接显示;否则追加显示
if (currentDisplayPage === 1) {
displayAlbums(displayedAlbums);
} else {
appendAlbums(displayedAlbums);
}
// 检查是否还有更多数据可以显示
hasMoreAlbums = (currentDisplayPage * albumsPerPage) < allAlbums.length;
}
// 搜索相册
// 防抖搜索函数
async function debouncedSearch() {
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
if (searchTerm === '') {
// 显示所有已加载的相册
displayAlbums(allAlbums);
updateStats();
return;
}
// 直接使用后端搜索API进行全站搜索
await performServerSearch(searchTerm);
}
// 在当前已加载的数据集中搜索
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) {
try {
// 显示搜索中的骨架屏
showSkeleton(4);
const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(searchTerm)}&page=1&limit=${albumsPerPage}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const serverResults = await response.json();
if (serverResults.length === 0) {
showError(`没有找到包含 "${searchTerm}" 的相册`);
return;
}
// 显示服务器搜索结果
displayAlbums(serverResults);
const oldStats = document.getElementById('stats');
if (oldStats) {
oldStats.innerHTML = `在全站找到 ${serverResults.length} 个包含 "${searchTerm}" 的相册`;
}
// 初始化图片懒加载
initLazyLoading();
} catch (error) {
console.error('全站搜索失败:', error);
showError('全站搜索失败,请重试');
}
}
// 显示相册列表
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/image/${album.cover_image_id}`;
coverImageUrl = '/assets/placeholder.svg'; // 初始使用占位图
}
// 格式化日期
const createdDate = new Date(album.created_at).toLocaleDateString('zh-CN');
return `
${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) => `
${formatDate(fav.favorite.created_at)}
${fav.image.width}×${fav.image.height}
`).join('')}
`}
`;
// 更新统计信息 - 显示收藏数量(兼容新旧版布局)
const statsEl = document.getElementById('stats');
if (statsEl) {
statsEl.innerHTML = ``;
}
// 初始化图片懒加载,确保收藏的图片被加载
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 = `
`;
// 添加到页面
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 ``;
}).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();
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 persons = await response.json();
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) {
displayAlbums(detail.albums);
updateStats(detail.albums);
initLazyLoading();
return;
}
}
} catch (e) { /* 回退 */ }
}
await performServerSearch(personName);
} catch (error) {
console.error('人物筛选失败:', error);
await performServerSearch(personName);
}
}
// 显示筛选状态
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;
// 新版筛选条
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;