Files
rust-imager/assets/js/app.js
lychang f3ae54a26b init
2026-03-22 04:02:47 +08:00

667 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 全局变量
let allAlbums = [];
let searchTimeout = null;
// API基础URL
const API_BASE = '/api';
// 设备检测
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');
}
loadAlbums();
initSearch();
});
// 初始化搜索功能
function initSearch() {
const searchInput = document.getElementById('search-input');
// 防抖搜索
searchInput.addEventListener('input', function () {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(debouncedSearch, 300);
});
// 移动端优化:搜索框聚焦时自动滚动到可视区域
if (isMobile) {
searchInput.addEventListener('focus', function () {
setTimeout(() => {
searchInput.scrollIntoView({behavior: 'smooth', block: 'center'});
}, 100);
});
}
}
// 防抖搜索
function debouncedSearch() {
searchAlbums();
}
// 显示骨架屏
function showSkeleton(count = 6) {
const container = document.getElementById('albums-container');
container.innerHTML = Array(count).fill(0).map(() => `
<div class="skeleton-card">
<div class="skeleton-cover skeleton"></div>
<div class="skeleton-content">
<div class="skeleton-title skeleton"></div>
<div class="skeleton-meta">
<div class="skeleton-meta-item skeleton"></div>
<div class="skeleton-meta-item skeleton"></div>
</div>
<div class="skeleton-button skeleton"></div>
</div>
</div>
`).join('');
}
// 加载所有相册
async function loadAlbums() {
try {
// 显示骨架屏
showSkeleton();
const response = await fetch(`${API_BASE}/albums`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
allAlbums = await response.json();
displayAlbums(allAlbums);
updateStats(allAlbums);
// 初始化图片懒加载
initLazyLoading();
} catch (error) {
console.error('加载相册失败:', error);
showError('加载相册数据失败,请刷新页面重试');
}
}
// 搜索相册
async function searchAlbums() {
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
if (searchTerm === '') {
// 显示所有相册
displayAlbums(allAlbums);
updateStats(allAlbums);
return;
}
try {
// 显示搜索中的骨架屏
showSkeleton(4);
const response = await fetch(`${API_BASE}/search?q=${encodeURIComponent(searchTerm)}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const filteredAlbums = await response.json();
displayAlbums(filteredAlbums);
if (filteredAlbums.length === 0) {
document.getElementById('stats').innerHTML = `没有找到包含 "${searchTerm}" 的相册`;
} else {
document.getElementById('stats').innerHTML = `找到 ${filteredAlbums.length} 个包含 "${searchTerm}" 的相册`;
}
// 初始化图片懒加载
initLazyLoading();
} catch (error) {
console.error('搜索失败:', error);
showError('搜索失败,请重试');
}
}
// 显示相册列表
function displayAlbums(albums) {
const container = document.getElementById('albums-container');
if (albums.length === 0) {
container.innerHTML = '<div class="no-results"><i class="fas fa-search"></i><br>没有找到匹配的相册</div>';
return;
}
container.innerHTML = albums.map(album => {
// 处理封面图片URL - 使用懒加载
let coverImageUrl = '/assets/placeholder.svg';
let dataSrc = '';
if (album.cover_image_id) {
dataSrc = `/api/thumbnail/${album.cover_image_id}`;
coverImageUrl = '/assets/placeholder.svg'; // 初始使用占位图
}
// 格式化日期
const createdDate = new Date(album.created_at).toLocaleDateString('zh-CN');
return `
<div class="album-card" data-album-id="${album.id}">
<div class="album-cover">
<img
src="${coverImageUrl}"
${dataSrc ? `data-src="${dataSrc}"` : ''}
alt="${album.title}"
class="lazy"
onerror="this.src='/assets/placeholder.svg'"
>
</div>
<div class="album-info">
<h3>${album.title}</h3>
<div class="album-meta">
<span><i class="fas fa-images"></i> ${album.image_count} 张图片</span>
<span><i class="fas fa-calendar"></i> ${createdDate}</span>
</div>
<a href="/album?id=${album.id}" class="view-button">
<i class="fas fa-eye"></i> 查看相册
</a>
</div>
</div>
`;
}).join('');
}
// 初始化图片懒加载
function initLazyLoading() {
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.classList.remove('lazy');
delete img.dataset.src;
}
imageObserver.unobserve(img);
}
});
}, {
rootMargin: '50px 0px',
threshold: 0.1
});
document.querySelectorAll('img.lazy').forEach(img => {
imageObserver.observe(img);
});
} else {
// 不支持IntersectionObserver的浏览器回退方案
document.querySelectorAll('img.lazy').forEach(img => {
if (img.dataset.src) {
img.src = img.dataset.src;
img.classList.remove('lazy');
delete img.dataset.src;
}
});
}
}
// 更新统计信息
function updateStats(albums) {
const totalImages = albums.reduce((sum, album) => sum + album.image_count, 0);
document.getElementById('stats').innerHTML = `共发现 ${albums.length} 个相册,包含 ${totalImages} 张图片`;
}
// 显示错误信息
function showError(message) {
const container = document.getElementById('albums-container');
container.innerHTML = `<div class="no-results"><i class="fas fa-exclamation-triangle"></i><br>${message}</div>`;
document.getElementById('stats').innerHTML = '';
}
// 键盘事件处理
function handleKeyPress(event) {
if (event.key === 'Enter') {
searchAlbums();
}
}
// 性能监控
function logPerformance() {
if ('performance' in window) {
window.addEventListener('load', () => {
setTimeout(() => {
const navigationTiming = performance.getEntriesByType('navigation')[0];
console.log('页面加载时间:', navigationTiming.loadEventEnd - navigationTiming.navigationStart, 'ms');
}, 0);
});
}
}
// 移动端优化:防止双击缩放
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);
}
}
// 初始化性能监控和移动端优化
logPerformance();
preventDoubleTapZoom();
// 随机打开相册
function openRandomAlbum() {
if (allAlbums.length === 0) {
alert('暂无相册数据,请稍后再试');
return;
}
const randomIndex = Math.floor(Math.random() * allAlbums.length);
const randomAlbum = allAlbums[randomIndex];
// 添加随机动画效果
const randomButton = document.querySelector('.random-button');
randomButton.style.transform = 'scale(0.95)';
setTimeout(() => {
randomButton.style.transform = '';
}, 200);
// 跳转到随机相册
window.location.href = `/album?id=${randomAlbum.id}`;
}
// 显示收藏夹
function showFavorites() {
// 从后端API获取收藏列表
fetch(`${API_BASE}/favorites`)
.then(response => {
if (!response.ok) {
throw new Error('获取收藏列表失败');
}
return response.json();
})
.then(favoriteImages => {
if (favoriteImages.length === 0) {
alert('收藏夹为空,快去收藏一些喜欢的图片吧!');
return;
}
// 创建收藏夹页面或跳转到收藏夹视图
displayFavorites(favoriteImages);
})
.catch(error => {
console.error('获取收藏列表失败:', error);
alert('获取收藏列表失败,请稍后重试');
});
}
// 显示收藏夹内容
function displayFavorites(favoriteImages) {
const container = document.getElementById('albums-container');
container.innerHTML = `
${favoriteImages.length === 0 ? `
<div class="empty-favorites">
<div class="empty-icon">
<i class="fas fa-heart-broken"></i>
</div>
<h3>收藏夹空空如也</h3>
<p>快去收藏一些喜欢的图片吧!</p>
<button class="explore-button" onclick="loadAlbums()">
<i class="fas fa-compass"></i> 探索相册
</button>
</div>
` : `${favoriteImages.map((fav, index) => `
<div class="favorite-card" onclick="openFavoriteImage('${fav.favorite.album_id}')">
<div class="card-image">
<img src="${API_BASE}/thumbnail/${fav.image.id}"
alt="${fav.image.title || '收藏的图片'}"
loading="lazy">
<div class="card-overlay">
<div class="overlay-content">
<button class="quick-view" onclick="event.stopPropagation(); previewFavorite(${index})">
<i class="fas fa-expand"></i>
</button>
<button class="remove-favorite" onclick="removeFavorite(event, '${fav.favorite.id}')">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="favorite-badge">
<i class="fas fa-heart"></i>
</div>
</div>
<div class="card-content">
<div class="image-meta">
<span class="meta-item">
<i class="fas fa-calendar"></i>
${formatDate(fav.favorite.created_at)}
</span>
<span class="meta-item">
<i class="fas fa-ruler-combined"></i>
${fav.image.width}×${fav.image.height}
</span>
</div>
</div>
</div>
`).join('')}
`}
`;
// 更新统计信息 - 显示收藏数量
document.getElementById('stats').innerHTML = `<div class="favorites-header">
<div class="header-content">
<div class="header-left">
<div class="favorites-icon">
<i class="fas fa-heart"></i>
</div>
<div class="header-info">
<h1>我的收藏夹</h1>
<p class="favorites-count">共收藏了 <span class="count-number">${favoriteImages.length}</span> 张图片</p>
</div>
</div>
<div class="header-right">
<div class="action-buttons">
<button class="back-button" onclick="loadAlbums()">
<i class="fas fa-arrow-left"></i> 返回首页
</button>
</div>
</div>
</div>
</div>`;
}
// 格式化日期
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
}
// 全局变量存储收藏列表和当前预览索引
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 = `
<div class="preview-modal" id="previewModal">
<div class="preview-overlay" onclick="closePreviewModal()"></div>
<div class="preview-container">
<div class="preview-header">
<div class="preview-title">
<span class="preview-info">来自: ${favorite.album_title}</span>
</div>
<div class="preview-actions">
<button class="preview-nav preview-prev" onclick="navigatePreview(-1)" ${index === 0 ? 'disabled' : ''}>
<i class="fas fa-chevron-left"></i>
</button>
<span class="preview-counter">${index + 1} / ${currentFavorites.length}</span>
<button class="preview-nav preview-next" onclick="navigatePreview(1)" ${index === currentFavorites.length - 1 ? 'disabled' : ''}>
<i class="fas fa-chevron-right"></i>
</button>
<button class="preview-close" onclick="closePreviewModal()">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="preview-content">
<div class="preview-image-container">
<img src="${API_BASE}/original/${favorite.image.id}"
alt="${favorite.image.title || '预览图片'}"
class="preview-image"
id="preview-image"
loading="eager">
</div>
</div>
<div class="preview-footer">
<div class="preview-meta">
<span class="meta-item">
<i class="fas fa-calendar"></i>
${formatDate(favorite.favorite.created_at)}
</span>
<span class="meta-item">
<i class="fas fa-ruler-combined"></i>
${favorite.image.width} × ${favorite.image.height}
</span>
<span class="meta-item">
<i class="fas fa-heart"></i>
已收藏
</span>
</div>
<div class="preview-controls">
<button class="preview-action" onclick="openFavoriteImage('${favorite.favorite.album_id}')">
<i class="fas fa-folder-open"></i> 打开相册
</button>
<button class="preview-action preview-remove" onclick="removeFavoriteFromPreview('${favorite.favorite.id}')">
<i class="fas fa-trash"></i> 取消收藏
</button>
</div>
</div>
</div>
</div>
`;
// 添加到页面
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 = `<i class="fas fa-calendar"></i> ${formatDate(favorite.favorite.created_at)}`;
metaElements[1].innerHTML = `<i class="fas fa-ruler-combined"></i> ${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}/original/${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 = '<i class="fas fa-exclamation-triangle"></i><span>图片加载失败</span>';
};
}
}
// 处理键盘事件
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 sortFavorites(mode) {
// 这里可以实现排序功能
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}`;
}
// 导出函数供全局使用
window.handleKeyPress = handleKeyPress;
window.searchAlbums = searchAlbums;
window.openRandomAlbum = openRandomAlbum;
window.showFavorites = showFavorites;