From cb09f2caacf76446c72fe7cd3c330fd8ae28a771 Mon Sep 17 00:00:00 2001 From: lychang Date: Sun, 19 Jul 2026 17:44:08 +0800 Subject: [PATCH] update --- assets/css/style.css | 213 +++++++++++++++++++++++++++++++++++++++++++ assets/index.html | 33 +++++++ assets/js/app.js | 199 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 444 insertions(+), 1 deletion(-) diff --git a/assets/css/style.css b/assets/css/style.css index e3bc870..4cf1503 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -1084,6 +1084,219 @@ body { border: 1px solid var(--medium-gray); } +/* ============================================================ + 发现区域 - 热门人物 & 热门标签 + ============================================================ */ + +.discover-section { + background: var(--white); + border-radius: var(--border-radius-lg); + box-shadow: var(--shadow-sm); + border: 1px solid var(--medium-gray); + margin-bottom: 30px; + overflow: hidden; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 18px 24px; + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); + border-bottom: 1px solid var(--medium-gray); +} + +.section-header h3 { + margin: 0; + font-size: 1.1rem; + font-weight: 700; + color: var(--text-dark); + display: flex; + align-items: center; + gap: 10px; +} + +.section-header h3 i { + color: var(--secondary-blue); +} + +.section-toggle { + background: none; + border: none; + color: var(--text-light); + cursor: pointer; + padding: 6px 10px; + border-radius: 6px; + transition: all 0.2s ease; + font-size: 0.9rem; +} + +.section-toggle:hover { + background: rgba(59, 130, 246, 0.1); + color: var(--secondary-blue); +} + +/* 标签云 */ +.tag-cloud { + padding: 20px 24px; + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; +} + +.tag-chip { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 6px 14px; + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); + border: 1px solid #bae6fd; + border-radius: 20px; + cursor: pointer; + font-weight: 600; + transition: all 0.25s ease; + user-select: none; +} + +.tag-chip small { + color: var(--text-light); + font-weight: 500; + opacity: 0.8; +} + +.tag-chip:hover { + background: linear-gradient(135deg, var(--secondary-blue) 0%, var(--primary-blue) 100%); + color: var(--white) !important; + border-color: var(--secondary-blue); + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.tag-chip:hover small { + color: rgba(255, 255, 255, 0.85); +} + +/* 热门人物 */ +.people-cloud { + padding: 20px 24px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 12px; +} + +.person-chip { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 14px; + background: linear-gradient(135deg, #fdf4ff 0%, #fae8ff 100%); + border: 1px solid #f5d0fe; + border-radius: 12px; + cursor: pointer; + transition: all 0.25s ease; +} + +.person-chip:hover { + background: linear-gradient(135deg, #a855f7 0%, #9333ea 100%); + border-color: #9333ea; + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.person-chip:hover .person-name, +.person-chip:hover .person-meta { + color: var(--white); +} + +.person-avatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #a855f7 0%, #7c3aed 100%); + color: var(--white); + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 1.1rem; + flex-shrink: 0; +} + +.person-chip:hover .person-avatar { + background: rgba(255, 255, 255, 0.25); +} + +.person-info { + flex: 1; + min-width: 0; +} + +.person-name { + font-weight: 700; + color: var(--text-dark); + font-size: 0.95rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.person-meta { + font-size: 0.8rem; + color: var(--text-light); + margin-top: 2px; +} + +/* 筛选状态条 */ +.filter-status { + display: none; + align-items: center; + gap: 12px; + padding: 14px 20px; + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); + border: 1px solid #fcd34d; + border-radius: var(--border-radius); + margin-bottom: 24px; + box-shadow: var(--shadow-sm); +} + +.filter-label { + font-weight: 600; + color: #92400e; + font-size: 0.95rem; +} + +.filter-tag { + background: var(--white); + padding: 4px 12px; + border-radius: 16px; + font-weight: 700; + color: #92400e; + font-size: 0.9rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.filter-clear { + margin-left: auto; + padding: 6px 14px; + background: #92400e; + color: var(--white); + border: none; + border-radius: 16px; + font-weight: 600; + font-size: 0.85rem; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + gap: 6px; +} + +.filter-clear:hover { + background: #78350f; + transform: translateY(-1px); +} + /* 响应式网格布局 - 区分PC和手机 */ .albums-grid { display: grid; diff --git a/assets/index.html b/assets/index.html index 9dd48e5..e884a2f 100644 --- a/assets/index.html +++ b/assets/index.html @@ -33,6 +33,39 @@
正在加载相册数据...
+ + + + + + + + +
diff --git a/assets/js/app.js b/assets/js/app.js index 3709235..975fa60 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -10,6 +10,10 @@ let lazyLoadObserver = null; let isAllAlbumsLoaded = false; // 标记是否已加载所有相册数据 let isInFavoritesPage = false; // 标记当前是否在收藏页 +// 标签和人物筛选 +let currentFilter = null; // { type: 'tag'|'person', value: string } +let collapsedSections = { people: false, tags: false }; + // API基础URL const API_BASE = '/api'; @@ -437,6 +441,9 @@ async function loadAlbums(reset = false) { // 预加载下一批图片 preloadNextBatchImages(); + // 加载热门标签和人物 + loadDiscoverData(); + } catch (error) { console.error('加载相册失败:', error); showError('加载相册数据失败,请刷新页面重试'); @@ -1199,6 +1206,192 @@ function openFavoriteImage(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 tags = await tagsResp.json(); + 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 section = document.getElementById('tags-section'); + const cloud = document.getElementById('tag-cloud'); + if (!section || !cloud || !tags.length) return; + + 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 || ''; + // 计算字体大小 (12px - 22px) + 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 section = document.getElementById('people-section'); + const cloud = document.getElementById('people-cloud'); + if (!section || !cloud || !people.length) return; + + 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(''); +} + +// 按标签筛选 +async function filterByTag(tag) { + currentFilter = { type: 'tag', value: tag }; + showFilterStatus(`标签: ${tag}`); + + 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 filterByPerson(personName) { + currentFilter = { type: 'person', value: personName }; + showFilterStatus(`人物: ${personName}`); + + 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) { + // 先尝试通过人物详情获取相册 + const personId = persons[0].id; + 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 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 status = document.getElementById('filter-status'); + if (status) status.style.display = 'none'; + + // 清空搜索框 + const searchInput = document.getElementById('search-input'); + if (searchInput) searchInput.value = ''; + + // 重新加载所有相册 + 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'; + } + } +} + // 导出函数供全局使用 window.searchAlbums = searchAlbums; window.openRandomAlbum = openRandomAlbum; @@ -1209,4 +1402,8 @@ window.loadAlbums = loadAlbums; window.removeFavorite = removeFavorite; window.previewFavorite = previewFavorite; window.openFavoriteImage = openFavoriteImage; -window.removeFavoriteFromPreview = removeFavoriteFromPreview; \ No newline at end of file +window.removeFavoriteFromPreview = removeFavoriteFromPreview; +window.filterByTag = filterByTag; +window.filterByPerson = filterByPerson; +window.clearFilter = clearFilter; +window.toggleSection = toggleSection; \ No newline at end of file