Compare commits
9 Commits
5e33d53ea3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e271cf342b | ||
|
|
4f52069560 | ||
|
|
afc019b1fd | ||
|
|
5c22d3f59d | ||
|
|
47396b88f0 | ||
|
|
40278c5d84 | ||
|
|
3f5ddde664 | ||
|
|
be004854e2 | ||
|
|
f0605dca4c |
198
index.html
@@ -42,6 +42,43 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<!-- 搜索功能 -->
|
||||||
|
<div id="search-overlay" class="search-overlay">
|
||||||
|
<div class="search-modal">
|
||||||
|
<button class="search-lock-btn" id="search-lock-btn" title="搜索常驻">
|
||||||
|
<svg id="lock-icon-open" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||||
|
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||||
|
</svg>
|
||||||
|
<svg id="lock-icon-closed" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: none;">
|
||||||
|
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||||
|
<path d="M7 11V7a5 5 0 0 1 9.9-1"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div class="search-box">
|
||||||
|
<div class="search-engine-selector" id="search-engine-selector">
|
||||||
|
<img src="./static/img/search_icon/google.png" alt="Google" id="search-engine-icon" />
|
||||||
|
<div class="search-engine-dropdown" id="search-engine-dropdown">
|
||||||
|
<div class="engine-option active" data-engine="google" data-icon="./static/img/search_icon/google.png">
|
||||||
|
<img src="./static/img/search_icon/google.png" alt="Google" /> Google
|
||||||
|
</div>
|
||||||
|
<div class="engine-option" data-engine="baidu" data-icon="./static/img/search_icon/baidu.png">
|
||||||
|
<img src="./static/img/search_icon/baidu.png" alt="Baidu" /> Baidu
|
||||||
|
</div>
|
||||||
|
<div class="engine-option" data-engine="duckduckgo" data-icon="./static/img/search_icon/DuckDuckGo.png">
|
||||||
|
<img src="./static/img/search_icon/DuckDuckGo.png" alt="DuckDuckGo" /> DuckDuckGo
|
||||||
|
</div>
|
||||||
|
<div class="engine-option" data-engine="yandex" data-icon="./static/img/search_icon/Yandex.png">
|
||||||
|
<img src="./static/img/search_icon/Yandex.png" alt="Yandex" /> Yandex
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="global-search-input" placeholder="Search..." autocomplete="off" />
|
||||||
|
<button id="global-search-btn">搜索</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="letter-container">
|
<div class="letter-container">
|
||||||
<!-- 语言切换按钮 -->
|
<!-- 语言切换按钮 -->
|
||||||
<div class="lang-switch-container">
|
<div class="lang-switch-container">
|
||||||
@@ -96,7 +133,7 @@
|
|||||||
|
|
||||||
<!-- 完整博客文章弹窗(点击后显示) -->
|
<!-- 完整博客文章弹窗(点击后显示) -->
|
||||||
<div id="blog-posts-full-modal" class="guestbook-full-modal">
|
<div id="blog-posts-full-modal" class="guestbook-full-modal">
|
||||||
<div class="full-modal-content">
|
<div class="full-modal-content solid-modal">
|
||||||
<div class="guestbook-header">
|
<div class="guestbook-header">
|
||||||
<h2>博客文章</h2>
|
<h2>博客文章</h2>
|
||||||
<button id="blog-posts-close" class="guestbook-close" title="关闭">×</button>
|
<button id="blog-posts-close" class="guestbook-close" title="关闭">×</button>
|
||||||
@@ -107,9 +144,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 旅途剪影按钮 -->
|
||||||
|
<div class="photo-album-btn-container">
|
||||||
|
<button id="photo-album-btn" class="photo-album-btn" title="旅途剪影">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" stroke="#666" stroke-width="2"/>
|
||||||
|
<circle cx="8.5" cy="8.5" r="1.5" fill="#666"/>
|
||||||
|
<path d="M21 15L16 10L5 21" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
<span class="btn-label" data-i18n="btn_photo_album">旅途剪影</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Hover 浮动预览面板 -->
|
||||||
|
<div id="photo-album-preview" class="photo-album-preview">
|
||||||
|
<div class="preview-header">
|
||||||
|
<h3 data-i18n="photo_latest_photos">最新照片</h3>
|
||||||
|
</div>
|
||||||
|
<div class="preview-body photo-album-list">
|
||||||
|
<!-- 最新照片预览将在这里动态加载 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 完整旅途剪影弹窗(点击后显示) -->
|
||||||
|
<div id="photo-album-full-modal" class="guestbook-full-modal">
|
||||||
|
<div class="full-modal-content">
|
||||||
|
<div class="guestbook-header">
|
||||||
|
<h2 data-i18n="photo_album_title">旅途剪影</h2>
|
||||||
|
<button id="photo-album-close" class="guestbook-close" title="关闭">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="guestbook-body photo-album-body">
|
||||||
|
<!-- 照片网格将在这里动态加载 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 完整留言板弹窗(点击后显示) -->
|
<!-- 完整留言板弹窗(点击后显示) -->
|
||||||
<div id="guestbook-full-modal" class="guestbook-full-modal">
|
<div id="guestbook-full-modal" class="guestbook-full-modal">
|
||||||
<div class="full-modal-content">
|
<div class="full-modal-content solid-modal">
|
||||||
<div class="guestbook-header">
|
<div class="guestbook-header">
|
||||||
<h2 data-i18n="guestbook_title">留言板</h2>
|
<h2 data-i18n="guestbook_title">留言板</h2>
|
||||||
<button id="guestbook-close" class="guestbook-close" title="关闭">×</button>
|
<button id="guestbook-close" class="guestbook-close" title="关闭">×</button>
|
||||||
@@ -161,19 +233,22 @@
|
|||||||
<h3 data-i18n="social_media_h3">社交媒体</h3>
|
<h3 data-i18n="social_media_h3">社交媒体</h3>
|
||||||
<div class="website-links">
|
<div class="website-links">
|
||||||
<a href="https://github.com/dellevin" target="_blank" data-i18n="social_link1"
|
<a href="https://github.com/dellevin" target="_blank" data-i18n="social_link1"
|
||||||
data-i18n-attr="href">GitHub</a>
|
data-i18n-attr="href" onclick="return confirmLink(this.href)">GitHub</a>
|
||||||
<a href="https://weibo.com/u/6094785109999999999" target="_blank" data-i18n="social_link2"
|
<a href="https://weibo.com/u/6094785109999999999" target="_blank" data-i18n="social_link2"
|
||||||
data-i18n-attr="href">微博</a>
|
data-i18n-attr="href" onclick="return confirmLink(this.href)">微博</a>
|
||||||
<a href="https://www.zhihu.com/people/bing-xue-chen-xi-59" target="_blank" data-i18n="social_link3"
|
<a href="https://www.zhihu.com/people/bing-xue-chen-xi-59" target="_blank" data-i18n="social_link3"
|
||||||
data-i18n-attr="href">知乎</a>
|
data-i18n-attr="href" onclick="return confirmLink(this.href)">知乎</a>
|
||||||
<a href="https://space.bilibili.com/403551212" target="_blank" data-i18n="social_link4"
|
<a href="https://space.bilibili.com/403551212" target="_blank" data-i18n="social_link4"
|
||||||
data-i18n-attr="href">B站</a>
|
data-i18n-attr="href" onclick="return confirmLink(this.href)">B站</a>
|
||||||
|
<a href="https://www.xiaohongshu.com/user/profile/65aabb99000000000e0271f1" target="_blank"
|
||||||
|
data-i18n="social_link6"
|
||||||
|
data-i18n-attr="href" onclick="return confirmLink(this.href)">小红书</a>
|
||||||
<a href="https://www.52pojie.cn/home.php?mod=space&uid=2059006" target="_blank"
|
<a href="https://www.52pojie.cn/home.php?mod=space&uid=2059006" target="_blank"
|
||||||
data-i18n="social_link5" data-i18n-attr="href">52pojie</a>
|
data-i18n="social_link5" data-i18n-attr="href" onclick="return confirmLink(this.href)">52pojie</a>
|
||||||
</div>
|
</div>
|
||||||
<h3 data-i18n="leave_message_h3">留言</h3>
|
<h3 data-i18n="leave_message_h3">留言</h3>
|
||||||
<p data-i18n="leave_message_p"><a href="https://blog.iletter.top/401.html" target="_blank"
|
<p data-i18n="leave_message_p"><a href="https://blog.iletter.top/401.html" target="_blank"
|
||||||
data-i18n="leave_message_link" data-i18n-attr="href">点击此链接</a>去我的博客下面给我留言!</p>
|
data-i18n="leave_message_link" data-i18n-attr="href" onclick="return confirmLink(this.href)">点击此链接</a>去我的博客下面给我留言!</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -199,6 +274,65 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 自定义确认弹窗 -->
|
||||||
|
<div id="custom-confirm-modal" class="custom-confirm-modal">
|
||||||
|
<div class="custom-confirm-content">
|
||||||
|
<div class="custom-confirm-message"></div>
|
||||||
|
<div class="custom-confirm-buttons">
|
||||||
|
<button class="confirm-btn-cancel">取消</button>
|
||||||
|
<button class="confirm-btn-ok">确定</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MookNote 作品详情弹窗 -->
|
||||||
|
<div id="mooknote-modal" class="guestbook-full-modal">
|
||||||
|
<div class="full-modal-content solid-modal mooknote-modal-content">
|
||||||
|
<div class="guestbook-header">
|
||||||
|
<h2>MookNote:为记录而生</h2>
|
||||||
|
<button id="mooknote-close" class="guestbook-close" title="关闭">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="guestbook-body mooknote-body">
|
||||||
|
<div class="mooknote-banner-carousel">
|
||||||
|
<div class="carousel-container">
|
||||||
|
<img src="./static/img/my_work/mooknote/banner.jpg" alt="MookNote Banner" class="carousel-image active" onclick="openMooknoteLightbox(0)">
|
||||||
|
<img src="./static/img/my_work/mooknote/1.jpg" alt="MookNote Screenshot 1" class="carousel-image" onclick="openMooknoteLightbox(1)">
|
||||||
|
<img src="./static/img/my_work/mooknote/2.jpg" alt="MookNote Screenshot 2" class="carousel-image" onclick="openMooknoteLightbox(2)">
|
||||||
|
<img src="./static/img/my_work/mooknote/3.jpg" alt="MookNote Screenshot 3" class="carousel-image" onclick="openMooknoteLightbox(3)">
|
||||||
|
<img src="./static/img/my_work/mooknote/4.jpg" alt="MookNote Screenshot 4" class="carousel-image" onclick="openMooknoteLightbox(4)">
|
||||||
|
<img src="./static/img/my_work/mooknote/5.jpg" alt="MookNote Screenshot 5" class="carousel-image" onclick="openMooknoteLightbox(5)">
|
||||||
|
<img src="./static/img/my_work/mooknote/6.jpg" alt="MookNote Screenshot 6" class="carousel-image" onclick="openMooknoteLightbox(6)">
|
||||||
|
</div>
|
||||||
|
<button class="carousel-btn carousel-prev" onclick="prevMooknoteImage()">❮</button>
|
||||||
|
<button class="carousel-btn carousel-next" onclick="nextMooknoteImage()">❯</button>
|
||||||
|
<div class="carousel-indicators"></div>
|
||||||
|
</div>
|
||||||
|
<div class="mooknote-content">
|
||||||
|
<p class="mooknote-intro">一款专注于个人记录的极简工具,让每一份感动与思考都有迹可循。</p>
|
||||||
|
<p>在这个信息爆炸的时代,我们每天都在消费大量的内容——看一部电影、读一本书、产生一个灵感。然而,这些内容往往如流水般逝去,留下的只有模糊的记忆。</p>
|
||||||
|
<p><strong>MookNote 诞生于对简洁工具的追求。</strong></p>
|
||||||
|
<p>我相信,好的工具应该像纸笔一样自然,不打扰思考,只记录生活。它不应该有繁琐的注册流程,不应该将你的数据存储在陌生的服务器上,更不应该用花哨的功能分散你的注意力。</p>
|
||||||
|
<p>我厌烦了一款软件让用户不断地注册来注册去,使用手机号验证来验证去,数据不于自己,甚至自己写的笔记也不属于自己,我不喜欢这样,我想一款优秀的软件应该抛去繁杂的设计,就像太极一般的黑白两色。就像专业的人做专业的事情一样,MookNote主打记录生活一切有迹可循的影视娱乐,想法笔记。</p>
|
||||||
|
<p>MookNote名字的由来也是让我冥思苦想,Mo取自Movie的前两个字母ok取自book的后两个字母,Note则名为笔记,名字就这么来了。影视,书籍,笔记的三项生活记录,因为热爱,所以记录,因为想法,所以书写。</p>
|
||||||
|
<p>目前笔记还处于基础功能的版本,希望各位能多给我提提建议,我也会在其中做一些取舍,能与不能我都会一一记录下来的。在此,希望大家能多多帮助我这一个不知名的小开发者。</p>
|
||||||
|
<p class="mooknote-thanks">最后的最后,感谢阿里千问的通义灵码,让我一个小小的java开发者接触到Android开发,帮了我的大忙,帮了我最纯真的梦想。</p>
|
||||||
|
<div class="mooknote-download">
|
||||||
|
<a href="https://mooknote.iletter.top/" target="_blank" class="download-btn" onclick="return confirmLink(this.href)">前往下载 / 了解更多</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MookNote 全屏图片预览 -->
|
||||||
|
<div id="mooknote-lightbox" class="photo-lightbox mooknote-lightbox">
|
||||||
|
<button class="lightbox-close" onclick="closeMooknoteLightbox()">×</button>
|
||||||
|
<button class="lightbox-nav lightbox-prev" onclick="prevLightboxImage()">❮</button>
|
||||||
|
<button class="lightbox-nav lightbox-next" onclick="nextLightboxImage()">❯</button>
|
||||||
|
<div class="lightbox-counter"></div>
|
||||||
|
<img src="" alt="MookNote Fullscreen" class="lightbox-img">
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 工具内容 -->
|
<!-- 工具内容 -->
|
||||||
<div class="tab-content" id="tools-content">
|
<div class="tab-content" id="tools-content">
|
||||||
<div class="content-section">
|
<div class="content-section">
|
||||||
@@ -254,7 +388,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="copyright" style="margin-top: 15px;"><a href="http://beian.miit.gov.cn"
|
<div class="copyright" style="margin-top: 15px;"><a href="http://beian.miit.gov.cn"
|
||||||
target="_blank">冀ICP备2025122609号</a> ©<span>2024 - 2026</span> <span>Power BY</span> <span>Del
|
target="_blank" onclick="return confirmLink(this.href)">冀ICP备2025122609号</a> ©<span>2024 - 2026</span> <span>Power BY</span> <span>Del
|
||||||
Levin </span>
|
Levin </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -320,6 +454,51 @@
|
|||||||
switchButton.addEventListener('click', switchLanguage);
|
switchButton.addEventListener('click', switchLanguage);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- 自定义确认弹窗逻辑 -->
|
||||||
|
<script>
|
||||||
|
// 链接跳转确认函数
|
||||||
|
function confirmLink(url) {
|
||||||
|
showCustomConfirm(`确定要打开网站吗?\n${url}`, () => {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
});
|
||||||
|
return false; // 阻止默认跳转行为
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义确认弹窗
|
||||||
|
let confirmCallback = null;
|
||||||
|
const customConfirmModal = document.getElementById('custom-confirm-modal');
|
||||||
|
const confirmMessage = customConfirmModal.querySelector('.custom-confirm-message');
|
||||||
|
const confirmBtnOk = customConfirmModal.querySelector('.confirm-btn-ok');
|
||||||
|
const confirmBtnCancel = customConfirmModal.querySelector('.confirm-btn-cancel');
|
||||||
|
|
||||||
|
function showCustomConfirm(message, onConfirm) {
|
||||||
|
confirmMessage.textContent = message;
|
||||||
|
confirmCallback = onConfirm;
|
||||||
|
customConfirmModal.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideCustomConfirm() {
|
||||||
|
customConfirmModal.classList.remove('active');
|
||||||
|
confirmCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmBtnOk.addEventListener('click', () => {
|
||||||
|
if (confirmCallback) {
|
||||||
|
confirmCallback();
|
||||||
|
}
|
||||||
|
hideCustomConfirm();
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmBtnCancel.addEventListener('click', hideCustomConfirm);
|
||||||
|
|
||||||
|
// 点击背景关闭
|
||||||
|
customConfirmModal.addEventListener('click', (e) => {
|
||||||
|
if (e.target === customConfirmModal) {
|
||||||
|
hideCustomConfirm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<script src="./static/js/anypage.js"></script>
|
<script src="./static/js/anypage.js"></script>
|
||||||
<script src="./static/js/game_my.js"></script>
|
<script src="./static/js/game_my.js"></script>
|
||||||
<script src="./static/js/tools_my.js"></script>
|
<script src="./static/js/tools_my.js"></script>
|
||||||
@@ -328,6 +507,7 @@
|
|||||||
<script src="./static/js/map-footprint.js"></script>
|
<script src="./static/js/map-footprint.js"></script>
|
||||||
<script src="./static/js/guestbook.js"></script>
|
<script src="./static/js/guestbook.js"></script>
|
||||||
<script src="./static/js/blog_posts.js"></script>
|
<script src="./static/js/blog_posts.js"></script>
|
||||||
|
<script src="./static/js/photo_album.js"></script>
|
||||||
<script src="./static/js/tabchange.js"></script>
|
<script src="./static/js/tabchange.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
14
scripts/build-photos.bat
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
echo ========================================
|
||||||
|
echo 人生相册构建脚本
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
cd /d "%~dp0.."
|
||||||
|
|
||||||
|
python scripts\generate-photos-json.py
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
pause
|
||||||
93
scripts/generate-photos-json.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# ==================== 配置区域 ====================
|
||||||
|
# 请在此处指定你的照片目录和输出文件路径
|
||||||
|
# DEFAULT_PHOTOS_DIR = Path("/www/wwwroot/www.iletter.top/static/img/photos")
|
||||||
|
# DEFAULT_OUTPUT_FILE = Path("/www/wwwroot/www.iletter.top/static/img/photos/photos.json")
|
||||||
|
DEFAULT_PHOTOS_DIR = Path("D:\\UserData\\Desktop\\my_proj\\www.iletter.top\\static\\img\\photos")
|
||||||
|
DEFAULT_OUTPUT_FILE = Path("D:\\UserData\\Desktop\\my_proj\\www.iletter.top\\static\\img\\photos\\photos.json")
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
# 支持的图片格式
|
||||||
|
IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def natural_sort_key(filename):
|
||||||
|
"""自然排序键函数,支持数字正确排序"""
|
||||||
|
return [int(text) if text.isdigit() else text.lower()
|
||||||
|
for text in re.split(r'(\d+)', filename)]
|
||||||
|
|
||||||
|
|
||||||
|
def scan_photos(photos_dir):
|
||||||
|
"""扫描照片目录,返回按修改时间排序的文件名列表(最新的在最前面)"""
|
||||||
|
if not photos_dir.exists():
|
||||||
|
print(f"❌ 目录不存在: {photos_dir}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
photos = []
|
||||||
|
for file in photos_dir.iterdir():
|
||||||
|
if file.is_file() and file.suffix.lower() in IMAGE_EXTENSIONS:
|
||||||
|
# 排除 JSON 文件本身
|
||||||
|
if file.name == "photos.json":
|
||||||
|
continue
|
||||||
|
# 存储文件名和修改时间戳
|
||||||
|
photos.append((file.name, file.stat().st_mtime))
|
||||||
|
|
||||||
|
# 按修改时间降序排序(最新的在最前面)
|
||||||
|
photos.sort(key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
|
# 只返回文件名列表
|
||||||
|
return [p[0] for p in photos]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_json(photos, output_file):
|
||||||
|
"""生成 photos.json 文件"""
|
||||||
|
data = {
|
||||||
|
"photos": photos,
|
||||||
|
"total": len(photos),
|
||||||
|
"lastUpdated": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
}
|
||||||
|
|
||||||
|
# 写入文件
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
return len(photos)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# 确定照片目录和输出文件路径
|
||||||
|
photos_dir = DEFAULT_PHOTOS_DIR.resolve()
|
||||||
|
output_file = DEFAULT_OUTPUT_FILE.resolve()
|
||||||
|
|
||||||
|
print("🚀 开始扫描照片目录...")
|
||||||
|
print(f"📁 照片目录: {photos_dir}")
|
||||||
|
print(f"📝 输出文件: {output_file}")
|
||||||
|
|
||||||
|
photos = scan_photos(photos_dir)
|
||||||
|
|
||||||
|
if not photos:
|
||||||
|
print("⚠️ 未找到任何照片文件")
|
||||||
|
# 仍然生成空的 JSON
|
||||||
|
generate_json([], output_file)
|
||||||
|
print(f"✅ 已生成空的 {output_file}")
|
||||||
|
return
|
||||||
|
|
||||||
|
count = generate_json(photos, output_file)
|
||||||
|
|
||||||
|
print(f"✅ 成功生成 {output_file}")
|
||||||
|
print(f"📊 共扫描到 {count} 张照片:")
|
||||||
|
for i, photo in enumerate(photos, 1):
|
||||||
|
print(f" {i}. {photo}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -14,6 +14,31 @@
|
|||||||
z-index: 999;
|
z-index: 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 旅途剪影按钮容器 */
|
||||||
|
.photo-album-btn-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 210px;
|
||||||
|
right: 80px;
|
||||||
|
z-index: 999;
|
||||||
|
display: block !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 透明桥梁:向左延伸覆盖预览面板区域,确保鼠标滑入时 hover 不中断 */
|
||||||
|
.guestbook-btn-container::after,
|
||||||
|
.blog-posts-btn-container::after,
|
||||||
|
.photo-album-btn-container::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 80px; /* 从容器的左边缘开始 */
|
||||||
|
top: 0;
|
||||||
|
width: 50px; /* 15px 间距 + 380px 预览面板宽度 */
|
||||||
|
height: 80px; /* 与按钮高度一致 */
|
||||||
|
background: transparent;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.guestbook-btn {
|
.guestbook-btn {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -47,6 +72,24 @@
|
|||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.photo-album-btn {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
visibility: visible !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-label {
|
.btn-label {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #666;
|
color: #666;
|
||||||
@@ -76,9 +119,106 @@
|
|||||||
stroke: #333;
|
stroke: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 自定义确认弹窗样式 */
|
||||||
|
.custom-confirm-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
z-index: 99999;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
animation: fadeIn 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-confirm-modal.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-confirm-content {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(255, 255, 255, 0.95) 0%,
|
||||||
|
rgba(255, 255, 255, 0.9) 100%
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
-webkit-backdrop-filter: blur(20px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
max-width: 400px;
|
||||||
|
width: 90%;
|
||||||
|
box-shadow:
|
||||||
|
0 20px 60px rgba(0, 0, 0, 0.3),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.4) inset;
|
||||||
|
animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-confirm-message {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-confirm-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn-cancel,
|
||||||
|
.confirm-btn-ok {
|
||||||
|
padding: 8px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn-cancel {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn-cancel:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn-ok {
|
||||||
|
background: #1a73e8;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn-ok:hover {
|
||||||
|
background: #1557b0;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(26, 115, 232, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px) scale(0.95);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Hover 浮动预览面板 */
|
/* Hover 浮动预览面板 */
|
||||||
.guestbook-preview,
|
.guestbook-preview,
|
||||||
.blog-posts-preview {
|
.blog-posts-preview,
|
||||||
|
.photo-album-preview {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -103,7 +243,8 @@
|
|||||||
|
|
||||||
/* 当鼠标悬停在容器上时显示预览面板 */
|
/* 当鼠标悬停在容器上时显示预览面板 */
|
||||||
.guestbook-btn-container:hover .guestbook-preview,
|
.guestbook-btn-container:hover .guestbook-preview,
|
||||||
.blog-posts-btn-container:hover .blog-posts-preview {
|
.blog-posts-btn-container:hover .blog-posts-preview,
|
||||||
|
.photo-album-btn-container:hover .photo-album-preview {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(0) scale(1);
|
transform: translateX(0) scale(1);
|
||||||
@@ -130,6 +271,21 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: calc(400px - 50px);
|
max-height: calc(400px - 50px);
|
||||||
|
scrollbar-width: thin; /* Firefox */
|
||||||
|
scrollbar-color: rgba(0, 0, 0, 0.2) transparent; /* Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-body::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-body::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-body::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-item {
|
.preview-item {
|
||||||
@@ -186,7 +342,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -206,15 +362,200 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 旅途剪影弹窗样式 - iOS 26 风格极致玻璃拟态 */
|
||||||
.full-modal-content {
|
.full-modal-content {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(255, 255, 255, 0.12) 0%,
|
||||||
|
rgba(255, 255, 255, 0.06) 50%,
|
||||||
|
rgba(255, 255, 255, 0.1) 100%
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(80px) saturate(250%);
|
||||||
|
-webkit-backdrop-filter: blur(80px) saturate(250%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||||
|
border-radius: 28px;
|
||||||
|
width: 95%;
|
||||||
|
max-width: 1400px;
|
||||||
|
max-height: 95vh;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow:
|
||||||
|
0 32px 100px rgba(0, 0, 0, 0.25),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.3) inset,
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.6),
|
||||||
|
inset 0 -1px 1px rgba(255, 255, 255, 0.15);
|
||||||
|
animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部高光反射 - 增强版 */
|
||||||
|
.full-modal-content::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 50%;
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
rgba(255, 255, 255, 0.5) 0%,
|
||||||
|
rgba(255, 255, 255, 0.2) 30%,
|
||||||
|
rgba(255, 255, 255, 0.05) 70%,
|
||||||
|
transparent 100%);
|
||||||
|
border-radius: 28px 28px 0 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部环境光反射 */
|
||||||
|
.full-modal-content::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 10%;
|
||||||
|
right: 10%;
|
||||||
|
height: 80px;
|
||||||
|
background: radial-gradient(ellipse at center bottom,
|
||||||
|
rgba(255, 255, 255, 0.2) 0%,
|
||||||
|
transparent 60%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 侧边边缘反光 */
|
||||||
|
.full-modal-content .guestbook-header::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 20%;
|
||||||
|
left: 0;
|
||||||
|
bottom: 20%;
|
||||||
|
width: 2px;
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(255, 255, 255, 0.3) 30%,
|
||||||
|
rgba(255, 255, 255, 0.5) 50%,
|
||||||
|
rgba(255, 255, 255, 0.3) 70%,
|
||||||
|
transparent 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 旅途剪影弹窗样式 - iOS 26 风格极致玻璃拟态 */
|
||||||
|
.full-modal-content {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(255, 255, 255, 0.12) 0%,
|
||||||
|
rgba(255, 255, 255, 0.06) 50%,
|
||||||
|
rgba(255, 255, 255, 0.1) 100%
|
||||||
|
);
|
||||||
|
backdrop-filter: blur(80px) saturate(250%);
|
||||||
|
-webkit-backdrop-filter: blur(80px) saturate(250%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||||
|
border-radius: 28px;
|
||||||
|
width: 95%;
|
||||||
|
height: 95vh;
|
||||||
|
max-width: 1400px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow:
|
||||||
|
0 32px 100px rgba(0, 0, 0, 0.25),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.3) inset,
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.6),
|
||||||
|
inset 0 -1px 1px rgba(255, 255, 255, 0.15);
|
||||||
|
animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 针对旅途剪影弹窗的绝对全屏处理 */
|
||||||
|
#photo-album-full-modal .full-modal-content {
|
||||||
|
width: 100vw !important;
|
||||||
|
height: 100vh !important;
|
||||||
|
max-width: none !important;
|
||||||
|
max-height: none !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photo-album-full-modal .full-modal-content::before {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photo-album-full-modal .full-modal-content::after {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部高光反射 - 增强版 */
|
||||||
|
.full-modal-content::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 50%;
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
rgba(255, 255, 255, 0.5) 0%,
|
||||||
|
rgba(255, 255, 255, 0.2) 30%,
|
||||||
|
rgba(255, 255, 255, 0.05) 70%,
|
||||||
|
transparent 100%);
|
||||||
|
border-radius: 28px 28px 0 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部环境光反射 */
|
||||||
|
.full-modal-content::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 10%;
|
||||||
|
right: 10%;
|
||||||
|
height: 80px;
|
||||||
|
background: radial-gradient(ellipse at center bottom,
|
||||||
|
rgba(255, 255, 255, 0.2) 0%,
|
||||||
|
transparent 60%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 侧边边缘反光 */
|
||||||
|
.full-modal-content .guestbook-header::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 20%;
|
||||||
|
left: 0;
|
||||||
|
bottom: 20%;
|
||||||
|
width: 2px;
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(255, 255, 255, 0.3) 30%,
|
||||||
|
rgba(255, 255, 255, 0.5) 50%,
|
||||||
|
rgba(255, 255, 255, 0.3) 70%,
|
||||||
|
transparent 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 实心背景弹窗样式(留言板、博客文章专用) */
|
||||||
|
.full-modal-content.solid-modal {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
backdrop-filter: none;
|
||||||
|
-webkit-backdrop-filter: none;
|
||||||
|
border: none;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
width: 92%;
|
width: 92%;
|
||||||
max-width: 850px;
|
max-width: 850px;
|
||||||
max-height: 92vh;
|
max-height: 92vh;
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.18);
|
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.18);
|
||||||
animation: slideUp 0.3s ease;
|
}
|
||||||
|
|
||||||
|
/* 隐藏弹窗内部滚动条 */
|
||||||
|
.guestbook-body {
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(95vh - 70px);
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
|
}
|
||||||
|
|
||||||
|
.guestbook-body::-webkit-scrollbar {
|
||||||
|
display: none; /* Chrome, Safari, Opera */
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideUp {
|
@keyframes slideUp {
|
||||||
@@ -316,41 +657,68 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.guestbook-header {
|
.guestbook-header {
|
||||||
padding: 16px 20px;
|
padding: 22px 30px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: rgba(255, 255, 255, 0.8);
|
background: transparent;
|
||||||
backdrop-filter: blur(10px);
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 旅途剪影弹窗标题样式优化 */
|
||||||
|
#photo-album-full-modal .guestbook-header h2 {
|
||||||
|
color: #1a1a1a;
|
||||||
|
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 旅途剪影弹窗关闭按钮样式优化 */
|
||||||
|
#photo-album-full-modal .guestbook-close {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guestbook-header h2 {
|
.guestbook-header h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 16px;
|
font-size: 24px;
|
||||||
color: #333;
|
color: #1a1a1a;
|
||||||
font-weight: 500;
|
font-weight: 700;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 1.5px;
|
||||||
|
text-shadow: 0 1px 3px rgba(255, 255, 255, 0.9), 0 0 20px rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.guestbook-close {
|
.guestbook-close {
|
||||||
background: none;
|
background: rgba(255, 255, 255, 0.25);
|
||||||
border: none;
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
font-size: 24px;
|
font-size: 28px;
|
||||||
color: #999;
|
color: #444;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 24px;
|
width: 42px;
|
||||||
height: 24px;
|
height: 42px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: color 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
border-radius: 50%;
|
||||||
|
backdrop-filter: blur(20px) saturate(150%);
|
||||||
|
-webkit-backdrop-filter: blur(20px) saturate(150%);
|
||||||
|
box-shadow:
|
||||||
|
0 2px 8px rgba(0, 0, 0, 0.1),
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.5);
|
||||||
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guestbook-close:hover {
|
.guestbook-close:hover {
|
||||||
color: #333;
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
color: #222;
|
||||||
|
transform: rotate(90deg) scale(1.05);
|
||||||
|
box-shadow:
|
||||||
|
0 4px 16px rgba(0, 0, 0, 0.15),
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.guestbook-body {
|
.guestbook-body {
|
||||||
@@ -691,10 +1059,95 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加载更多按钮 */
|
/* 分页导航样式 - 极致玻璃拟态 */
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 30px 0;
|
||||||
|
background: transparent;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btn {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
backdrop-filter: blur(20px) saturate(150%);
|
||||||
|
-webkit-backdrop-filter: blur(20px) saturate(150%);
|
||||||
|
border-radius: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow:
|
||||||
|
0 2px 8px rgba(0, 0, 0, 0.06),
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.35);
|
||||||
|
transform: translateY(-3px) scale(1.05);
|
||||||
|
box-shadow:
|
||||||
|
0 6px 20px rgba(0, 0, 0, 0.1),
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.5);
|
||||||
|
border-color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-num {
|
||||||
|
min-width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||||
|
padding: 0 12px;
|
||||||
|
backdrop-filter: blur(10px) saturate(150%);
|
||||||
|
-webkit-backdrop-filter: blur(10px) saturate(150%);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-num:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.35);
|
||||||
|
border-color: rgba(255, 255, 255, 0.55);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-num.active {
|
||||||
|
background: rgba(255, 255, 255, 0.45);
|
||||||
|
color: #1a1a1a;
|
||||||
|
cursor: default;
|
||||||
|
backdrop-filter: blur(15px) saturate(180%);
|
||||||
|
-webkit-backdrop-filter: blur(15px) saturate(180%);
|
||||||
|
box-shadow:
|
||||||
|
0 4px 16px rgba(0, 0, 0, 0.1),
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.5);
|
||||||
|
border-color: rgba(255, 255, 255, 0.7);
|
||||||
|
font-weight: 700;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-dots {
|
||||||
|
color: #777;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载更多按钮样式(用于留言板等实心弹窗) */
|
||||||
.load-more-btn {
|
.load-more-btn {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 20px auto 20px auto;
|
margin: 20px auto 50px auto;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid #e8e8e8;
|
||||||
color: #666;
|
color: #666;
|
||||||
@@ -712,16 +1165,10 @@
|
|||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.load-more-btn:disabled {
|
|
||||||
color: #ccc;
|
|
||||||
border-color: #eee;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 加载完成提示 */
|
/* 加载完成提示 */
|
||||||
.load-complete {
|
.load-complete {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px 25px;
|
padding: 20px 25px 50px 25px;
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
@@ -753,6 +1200,17 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blog-post-digest {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.6;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.blog-post-meta {
|
.blog-post-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -768,6 +1226,218 @@
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 旅途剪影预览面板样式 - 列表模式 */
|
||||||
|
.photo-album-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-preview-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-preview-item:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-preview-item img {
|
||||||
|
width: 100%;
|
||||||
|
height: 120px;
|
||||||
|
object-fit: cover;
|
||||||
|
display: block;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 旅途剪影瀑布流布局 - 紧密贴住 */
|
||||||
|
.photo-album-grid {
|
||||||
|
column-count: 3;
|
||||||
|
column-gap: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.photo-album-grid {
|
||||||
|
column-count: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.photo-album-grid {
|
||||||
|
column-count: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-album-item {
|
||||||
|
break-inside: avoid;
|
||||||
|
margin-bottom: 0;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
box-shadow: none;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 照片加载动画 */
|
||||||
|
.photo-loading-spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-top-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: photo-spin 0.8s linear infinite;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes photo-spin {
|
||||||
|
to {
|
||||||
|
transform: translate(-50%, -50%) rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-album-item:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-album-item img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
opacity: 0; /* 初始隐藏,加载完再显示 */
|
||||||
|
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-album-item:hover img {
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 照片放大弹窗 */
|
||||||
|
.photo-lightbox {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.95);
|
||||||
|
z-index: 10000;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox img {
|
||||||
|
max-width: 85%;
|
||||||
|
max-height: 85%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||||
|
animation: zoomIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
right: 40px;
|
||||||
|
font-size: 40px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
z-index: 10001;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox-close:hover {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左右切换按钮 */
|
||||||
|
.photo-lightbox-nav {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 50px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
z-index: 10001;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 50%;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox-nav:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
transform: translateY(-50%) scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox-prev {
|
||||||
|
left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox-next {
|
||||||
|
right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图片计数器 */
|
||||||
|
.photo-lightbox-counter {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes zoomIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.guestbook-btn-container {
|
.guestbook-btn-container {
|
||||||
@@ -777,12 +1447,18 @@
|
|||||||
|
|
||||||
.blog-posts-btn-container {
|
.blog-posts-btn-container {
|
||||||
right: 15px;
|
right: 15px;
|
||||||
top: 90px;
|
top: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-album-btn-container {
|
||||||
|
right: 15px;
|
||||||
|
top: 205px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 移动端预览面板调整位置,避免超出屏幕 */
|
/* 移动端预览面板调整位置,避免超出屏幕 */
|
||||||
.guestbook-preview,
|
.guestbook-preview,
|
||||||
.blog-posts-preview {
|
.blog-posts-preview,
|
||||||
|
.photo-album-preview {
|
||||||
right: auto;
|
right: auto;
|
||||||
left: calc(100% + 10px);
|
left: calc(100% + 10px);
|
||||||
transform-origin: center left;
|
transform-origin: center left;
|
||||||
@@ -790,7 +1466,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.guestbook-btn-container:hover .guestbook-preview,
|
.guestbook-btn-container:hover .guestbook-preview,
|
||||||
.blog-posts-btn-container:hover .blog-posts-preview {
|
.blog-posts-btn-container:hover .blog-posts-preview,
|
||||||
|
.photo-album-btn-container:hover .photo-album-preview {
|
||||||
transform: translateX(0) scale(1);
|
transform: translateX(0) scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,177 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 搜索功能样式 */
|
||||||
|
.search-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
z-index: 9999;
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-overlay.active {
|
||||||
|
display: flex;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 常驻搜索模式:背景保持模糊,阻止页面交互 */
|
||||||
|
body.persistent-search-mode .search-overlay {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
pointer-events: auto; /* 确保遮罩层接收点击事件以阻止页面交互 */
|
||||||
|
}
|
||||||
|
|
||||||
|
body.persistent-search-mode .search-modal {
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-modal {
|
||||||
|
position: relative;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
transform: translateY(-20px);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-lock-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: -45px;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-lock-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-lock-btn.locked {
|
||||||
|
background: #1a73e8;
|
||||||
|
border-color: #1a73e8;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-overlay.active .search-modal {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||||
|
overflow: visible;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-engine-selector {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10px 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-engine-selector:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-engine-selector img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
#global-search-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 15px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: 16px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#global-search-btn {
|
||||||
|
padding: 12px 24px;
|
||||||
|
border: none;
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#global-search-btn:hover {
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-engine-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
margin-top: 8px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
min-width: 180px;
|
||||||
|
display: none;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-engine-dropdown.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-option {
|
||||||
|
padding: 12px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-option:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-option.active {
|
||||||
|
background: #e8f0fe;
|
||||||
|
color: #1a73e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-option img {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Georgia', 'Times New Roman', 'STSong', serif;
|
font-family: 'Georgia', 'Times New Roman', 'STSong', serif;
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
@@ -183,6 +354,326 @@ body {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 视图切换文字样式 */
|
||||||
|
.website-view-toggle {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-view-toggle:hover {
|
||||||
|
color: #333;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片视图样式 */
|
||||||
|
.website-card-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card:hover {
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card-icon {
|
||||||
|
width: 100%;
|
||||||
|
height: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f9f9f9;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card-placeholder {
|
||||||
|
background: linear-gradient(135deg, #e0e0e0 0%, #f5f5f5 100%);
|
||||||
|
color: #999;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card-icon img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card-info {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
line-height: 1.4;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
line-height: 1.5;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MookNote 弹窗样式 */
|
||||||
|
.mooknote-modal-content {
|
||||||
|
max-width: 800px;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mooknote-banner-carousel {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-image {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-image.active {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-image:hover {
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 60px;
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 28px;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-btn:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-prev {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-next {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-indicators {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 15px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-indicator {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-indicator.active {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MookNote 全屏预览样式 */
|
||||||
|
.photo-lightbox {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.95);
|
||||||
|
z-index: 10000;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-lightbox.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-img {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 95%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 30px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
font-size: 36px;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-close:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-nav {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
font-size: 32px;
|
||||||
|
width: 60px;
|
||||||
|
height: 80px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-nav:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-prev {
|
||||||
|
left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-next {
|
||||||
|
right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightbox-counter {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mooknote-body {
|
||||||
|
padding: 40px 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mooknote-content {
|
||||||
|
padding: 40px 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mooknote-content p {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #444;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mooknote-intro {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mooknote-thanks {
|
||||||
|
font-style: italic;
|
||||||
|
color: #666;
|
||||||
|
background: #f9f9f9;
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 4px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mooknote-download {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px dashed #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 12px 30px;
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 25px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn:hover {
|
||||||
|
background: #000;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
/* 工具和游戏网格 */
|
/* 工具和游戏网格 */
|
||||||
.tools-grid,
|
.tools-grid,
|
||||||
.games-grid {
|
.games-grid {
|
||||||
@@ -205,8 +696,7 @@ body {
|
|||||||
|
|
||||||
.tool-item:hover,
|
.tool-item:hover,
|
||||||
.game-item:hover {
|
.game-item:hover {
|
||||||
transform: translateY(-3px);
|
border-color: #000;
|
||||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-name,
|
.tool-name,
|
||||||
|
|||||||
BIN
static/img/my_work/bitwarden/banner.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
static/img/my_work/blog_iletter/banner.png
Normal file
|
After Width: | Height: | Size: 342 KiB |
BIN
static/img/my_work/easyimage2/banner.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
static/img/my_work/frp_panel/banner.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
static/img/my_work/ittoolman/banner.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
static/img/my_work/mooknote/1.jpg
Normal file
|
After Width: | Height: | Size: 368 KiB |
BIN
static/img/my_work/mooknote/2.jpg
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
static/img/my_work/mooknote/3.jpg
Normal file
|
After Width: | Height: | Size: 183 KiB |
BIN
static/img/my_work/mooknote/4.jpg
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
static/img/my_work/mooknote/5.jpg
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
static/img/my_work/mooknote/6.jpg
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
static/img/my_work/mooknote/banner.jpg
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
static/img/my_work/openlist/banner.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
static/img/photos/0c49fd33f4e83dcedde820d858ec44e9.jpg
Normal file
|
After Width: | Height: | Size: 467 KiB |
BIN
static/img/photos/1 (1).jpg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
static/img/photos/1 (10).jpg
Normal file
|
After Width: | Height: | Size: 490 KiB |
BIN
static/img/photos/1 (11).jpg
Normal file
|
After Width: | Height: | Size: 248 KiB |
BIN
static/img/photos/1 (12).jpg
Normal file
|
After Width: | Height: | Size: 722 KiB |
BIN
static/img/photos/1 (13).jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
static/img/photos/1 (2).jpg
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
static/img/photos/1 (3).jpg
Normal file
|
After Width: | Height: | Size: 442 KiB |
BIN
static/img/photos/1 (4).jpg
Normal file
|
After Width: | Height: | Size: 473 KiB |
BIN
static/img/photos/1 (5).jpg
Normal file
|
After Width: | Height: | Size: 345 KiB |
BIN
static/img/photos/1 (6).jpg
Normal file
|
After Width: | Height: | Size: 352 KiB |
BIN
static/img/photos/1 (7).jpg
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
static/img/photos/1 (8).jpg
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
static/img/photos/1 (9).jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
static/img/photos/1b72bd604e55942f6a7d7c28bc5ed5be.jpg
Normal file
|
After Width: | Height: | Size: 453 KiB |
BIN
static/img/photos/20955b34b4cbc661f3ec7ff2eda0b815.jpg
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
static/img/photos/2799e89f4632184d151941741b59257f.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
static/img/photos/3e0ad66c001c9894c26eac4b98601c73.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
static/img/photos/4c5a2a39e6ae9e01bf2ea9860ae8e41f.jpg
Normal file
|
After Width: | Height: | Size: 423 KiB |
BIN
static/img/photos/522915fe4632f9591faca7c46a068ea7.jpg
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
static/img/photos/64b539e20378d3e6ac3324e9dcc616ce.jpg
Normal file
|
After Width: | Height: | Size: 815 KiB |
BIN
static/img/photos/6b736db3eeaa315422e98e5e550f23ce.jpg
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
static/img/photos/74641a449ac2d8f9d113d27ca7a7254c.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
static/img/photos/7480c6e1755218592bd4881338750d8c.jpg
Normal file
|
After Width: | Height: | Size: 236 KiB |
BIN
static/img/photos/7b9532126cb8cd0752f8bdcd2d683cb6.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
static/img/photos/7fe76931d5653349a7876166db59d25e.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
static/img/photos/8498ee3191ba50dc27b85b7fbfd1e0fc.jpg
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
static/img/photos/85306709e27843201604a7b0efc88565.jpg
Normal file
|
After Width: | Height: | Size: 456 KiB |
BIN
static/img/photos/9f647be6532ea2acf9778eb302a19742.jpg
Normal file
|
After Width: | Height: | Size: 547 KiB |
BIN
static/img/photos/a09ebc8f61856503211b42c975fe5cd5.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
static/img/photos/a62e6036f35722b1f0dd131a929d3a17.jpg
Normal file
|
After Width: | Height: | Size: 490 KiB |
BIN
static/img/photos/c0ca11cc8eaaa16564c75263357e19ad.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
static/img/photos/c53486080dd63ac735f1c5d27203b1c4.jpg
Normal file
|
After Width: | Height: | Size: 401 KiB |
BIN
static/img/photos/c8be53d1cb4d3689420aa56dea0b16f5.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
static/img/photos/dae46343c972876a77f6b765ce1e57ea.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
static/img/photos/e1349403bdf5692c209d809ddb1e026d.jpg
Normal file
|
After Width: | Height: | Size: 275 KiB |
BIN
static/img/photos/f3aece19d8e1fdcd0bb140f5ef3ff882.jpg
Normal file
|
After Width: | Height: | Size: 242 KiB |
43
static/img/photos/photos.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"photos": [
|
||||||
|
"74641a449ac2d8f9d113d27ca7a7254c.jpg",
|
||||||
|
"9f647be6532ea2acf9778eb302a19742.jpg",
|
||||||
|
"a09ebc8f61856503211b42c975fe5cd5.jpg",
|
||||||
|
"a62e6036f35722b1f0dd131a929d3a17.jpg",
|
||||||
|
"1b72bd604e55942f6a7d7c28bc5ed5be.jpg",
|
||||||
|
"0c49fd33f4e83dcedde820d858ec44e9.jpg",
|
||||||
|
"64b539e20378d3e6ac3324e9dcc616ce.jpg",
|
||||||
|
"c0ca11cc8eaaa16564c75263357e19ad.jpg",
|
||||||
|
"7480c6e1755218592bd4881338750d8c.jpg",
|
||||||
|
"8498ee3191ba50dc27b85b7fbfd1e0fc.jpg",
|
||||||
|
"e1349403bdf5692c209d809ddb1e026d.jpg",
|
||||||
|
"85306709e27843201604a7b0efc88565.jpg",
|
||||||
|
"c53486080dd63ac735f1c5d27203b1c4.jpg",
|
||||||
|
"f3aece19d8e1fdcd0bb140f5ef3ff882.jpg",
|
||||||
|
"dae46343c972876a77f6b765ce1e57ea.jpg",
|
||||||
|
"6b736db3eeaa315422e98e5e550f23ce.jpg",
|
||||||
|
"522915fe4632f9591faca7c46a068ea7.jpg",
|
||||||
|
"7fe76931d5653349a7876166db59d25e.jpg",
|
||||||
|
"7b9532126cb8cd0752f8bdcd2d683cb6.jpg",
|
||||||
|
"c8be53d1cb4d3689420aa56dea0b16f5.jpg",
|
||||||
|
"2799e89f4632184d151941741b59257f.jpg",
|
||||||
|
"4c5a2a39e6ae9e01bf2ea9860ae8e41f.jpg",
|
||||||
|
"3e0ad66c001c9894c26eac4b98601c73.jpg",
|
||||||
|
"20955b34b4cbc661f3ec7ff2eda0b815.jpg",
|
||||||
|
"1 (7).jpg",
|
||||||
|
"1 (11).jpg",
|
||||||
|
"1 (9).jpg",
|
||||||
|
"1 (6).jpg",
|
||||||
|
"1 (5).jpg",
|
||||||
|
"1 (4).jpg",
|
||||||
|
"1 (3).jpg",
|
||||||
|
"1 (2).jpg",
|
||||||
|
"1 (1).jpg",
|
||||||
|
"1 (8).jpg",
|
||||||
|
"1 (13).jpg",
|
||||||
|
"1 (10).jpg",
|
||||||
|
"1 (12).jpg"
|
||||||
|
],
|
||||||
|
"total": 37,
|
||||||
|
"lastUpdated": "2026-05-18 02:36:56"
|
||||||
|
}
|
||||||
BIN
static/img/search_icon/DuckDuckGo.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/img/search_icon/Yandex.png
Normal file
|
After Width: | Height: | Size: 1000 B |
BIN
static/img/search_icon/baidu.png
Normal file
|
After Width: | Height: | Size: 1012 B |
BIN
static/img/search_icon/google.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,3 +1,195 @@
|
|||||||
|
// ==================== 全局搜索功能 ====================
|
||||||
|
(function() {
|
||||||
|
const searchEngines = {
|
||||||
|
google: 'https://www.google.com/search?q=',
|
||||||
|
baidu: 'https://www.baidu.com/s?wd=',
|
||||||
|
duckduckgo: 'https://duckduckgo.com/?q=',
|
||||||
|
yandex: 'https://yandex.com/search/?text='
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentEngine = localStorage.getItem('search_engine') || 'google';
|
||||||
|
let enterCount = 0;
|
||||||
|
let enterTimer = null;
|
||||||
|
|
||||||
|
const searchOverlay = document.getElementById('search-overlay');
|
||||||
|
const searchLockBtn = document.getElementById('search-lock-btn');
|
||||||
|
const lockIconOpen = document.getElementById('lock-icon-open');
|
||||||
|
const lockIconClosed = document.getElementById('lock-icon-closed');
|
||||||
|
const searchInput = document.getElementById('global-search-input');
|
||||||
|
const searchBtn = document.getElementById('global-search-btn');
|
||||||
|
const searchIcon = document.getElementById('search-engine-icon');
|
||||||
|
const dropdown = document.getElementById('search-engine-dropdown');
|
||||||
|
const engineSelector = document.getElementById('search-engine-selector');
|
||||||
|
const engineOptions = document.querySelectorAll('.engine-option');
|
||||||
|
|
||||||
|
// 检查是否开启常驻模式
|
||||||
|
const isPersistentSearch = localStorage.getItem('persistent_search') === 'true';
|
||||||
|
if (isPersistentSearch) {
|
||||||
|
document.body.classList.add('persistent-search-mode');
|
||||||
|
// 确保搜索框也显示出来
|
||||||
|
if (searchOverlay) {
|
||||||
|
searchOverlay.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化搜索引擎图标
|
||||||
|
function updateSearchIcon() {
|
||||||
|
const activeOption = document.querySelector(`.engine-option[data-engine="${currentEngine}"]`);
|
||||||
|
if (activeOption) {
|
||||||
|
const iconSrc = activeOption.getAttribute('data-icon');
|
||||||
|
searchIcon.src = iconSrc;
|
||||||
|
searchIcon.alt = currentEngine.charAt(0).toUpperCase() + currentEngine.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新锁图标状态
|
||||||
|
function updateLockIcon() {
|
||||||
|
const isLocked = document.body.classList.contains('persistent-search-mode');
|
||||||
|
if (isLocked) {
|
||||||
|
lockIconOpen.style.display = 'none';
|
||||||
|
lockIconClosed.style.display = 'block';
|
||||||
|
searchLockBtn.classList.add('locked');
|
||||||
|
searchLockBtn.title = '取消常驻';
|
||||||
|
} else {
|
||||||
|
lockIconOpen.style.display = 'block';
|
||||||
|
lockIconClosed.style.display = 'none';
|
||||||
|
searchLockBtn.classList.remove('locked');
|
||||||
|
searchLockBtn.title = '搜索常驻';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示搜索框
|
||||||
|
function showSearch() {
|
||||||
|
searchOverlay.classList.add('active');
|
||||||
|
setTimeout(() => {
|
||||||
|
searchInput.focus();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏搜索框(仅非驻留模式下调用)
|
||||||
|
function hideSearch() {
|
||||||
|
if (document.body.classList.contains('persistent-search-mode')) {
|
||||||
|
return; // 常驻模式下不允许通过此函数关闭
|
||||||
|
}
|
||||||
|
searchOverlay.classList.remove('active');
|
||||||
|
searchInput.value = '';
|
||||||
|
dropdown.classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换常驻模式
|
||||||
|
function togglePersistentMode() {
|
||||||
|
const isLocked = document.body.classList.contains('persistent-search-mode');
|
||||||
|
if (isLocked) {
|
||||||
|
// 退出常驻模式(解锁):只改变锁状态,不关闭搜索框
|
||||||
|
document.body.classList.remove('persistent-search-mode');
|
||||||
|
localStorage.setItem('persistent_search', 'false');
|
||||||
|
// 注意:不移除 active 类,保持搜索框显示
|
||||||
|
} else {
|
||||||
|
// 开启常驻模式(锁定)
|
||||||
|
document.body.classList.add('persistent-search-mode');
|
||||||
|
localStorage.setItem('persistent_search', 'true');
|
||||||
|
searchOverlay.classList.add('active');
|
||||||
|
}
|
||||||
|
updateLockIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行搜索
|
||||||
|
function performSearch() {
|
||||||
|
const query = searchInput.value.trim();
|
||||||
|
if (query) {
|
||||||
|
const url = searchEngines[currentEngine] + encodeURIComponent(query);
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听双击回车键
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (enterTimer && (now - enterTimer < 500)) {
|
||||||
|
// 双击回车:如果当前不是常驻模式,则打开/关闭搜索框;如果是常驻模式,则不处理
|
||||||
|
if (!document.body.classList.contains('persistent-search-mode')) {
|
||||||
|
if (searchOverlay.classList.contains('active')) {
|
||||||
|
hideSearch();
|
||||||
|
} else {
|
||||||
|
showSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enterCount = 0;
|
||||||
|
enterTimer = null;
|
||||||
|
} else {
|
||||||
|
enterCount = 1;
|
||||||
|
enterTimer = now;
|
||||||
|
|
||||||
|
// 如果搜索框已经打开,按单下回车执行搜索
|
||||||
|
if (searchOverlay.classList.contains('active') && document.activeElement === searchInput) {
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ESC 键关闭搜索框(仅在非驻留模式下生效)
|
||||||
|
if (e.key === 'Escape' && searchOverlay.classList.contains('active') && !document.body.classList.contains('persistent-search-mode')) {
|
||||||
|
hideSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击搜索引擎图标切换下拉菜单
|
||||||
|
engineSelector.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
dropdown.classList.toggle('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 选择搜索引擎
|
||||||
|
engineOptions.forEach(option => {
|
||||||
|
option.addEventListener('click', () => {
|
||||||
|
currentEngine = option.getAttribute('data-engine');
|
||||||
|
localStorage.setItem('search_engine', currentEngine);
|
||||||
|
|
||||||
|
// 更新激活状态
|
||||||
|
engineOptions.forEach(opt => opt.classList.remove('active'));
|
||||||
|
option.classList.add('active');
|
||||||
|
|
||||||
|
updateSearchIcon();
|
||||||
|
dropdown.classList.remove('show');
|
||||||
|
searchInput.focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击页面其他地方关闭下拉菜单
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
// 如果不在常驻模式下,且点击的是搜索框外部,则关闭搜索框
|
||||||
|
if (!document.body.classList.contains('persistent-search-mode') && !searchOverlay.contains(e.target)) {
|
||||||
|
hideSearch();
|
||||||
|
}
|
||||||
|
dropdown.classList.remove('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 阻止下拉菜单内部点击事件冒泡
|
||||||
|
dropdown.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击搜索按钮
|
||||||
|
searchBtn.addEventListener('click', performSearch);
|
||||||
|
|
||||||
|
// 点击锁图标切换常驻模式
|
||||||
|
searchLockBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation(); // 阻止事件冒泡,防止触发 document 的点击关闭逻辑
|
||||||
|
togglePersistentMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
updateSearchIcon();
|
||||||
|
updateLockIcon();
|
||||||
|
const initialActiveOption = document.querySelector(`.engine-option[data-engine="${currentEngine}"]`);
|
||||||
|
if (initialActiveOption) {
|
||||||
|
engineOptions.forEach(opt => opt.classList.remove('active'));
|
||||||
|
initialActiveOption.classList.add('active');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// ==================== Umami Stats 请求 ====================
|
// ==================== Umami Stats 请求 ====================
|
||||||
(async () => {
|
(async () => {
|
||||||
const webid = "ae6cd64c-5900-49c9-9c22-95cedc24a508";
|
const webid = "ae6cd64c-5900-49c9-9c22-95cedc24a508";
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
const blogPostsClose = document.getElementById('blog-posts-close');
|
const blogPostsClose = document.getElementById('blog-posts-close');
|
||||||
const blogPostsList = document.querySelector('.blog-posts-list');
|
const blogPostsList = document.querySelector('.blog-posts-list');
|
||||||
const blogPostsFullBody = document.querySelector('#blog-posts-full-modal .guestbook-body');
|
const blogPostsFullBody = document.querySelector('#blog-posts-full-modal .guestbook-body');
|
||||||
|
const previewBody = blogPostsPreview ? blogPostsPreview.querySelector('.preview-body') : null;
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
function init() {
|
function init() {
|
||||||
@@ -29,23 +30,23 @@
|
|||||||
|
|
||||||
// 关闭按钮
|
// 关闭按钮
|
||||||
blogPostsClose.addEventListener('click', closeFullModal);
|
blogPostsClose.addEventListener('click', closeFullModal);
|
||||||
|
|
||||||
// 点击背景关闭
|
|
||||||
blogPostsFullModal.addEventListener('click', function(e) {
|
|
||||||
if (e.target === blogPostsFullModal) {
|
|
||||||
closeFullModal();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESC 键关闭
|
|
||||||
document.addEventListener('keydown', function(e) {
|
|
||||||
if (e.key === 'Escape' && blogPostsFullModal.classList.contains('active')) {
|
|
||||||
closeFullModal();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 页面加载时自动加载数据(用于 Hover 预览)
|
// 页面加载时自动加载数据(用于 Hover 预览)
|
||||||
loadBlogPosts(true);
|
loadBlogPosts(true);
|
||||||
|
|
||||||
|
// 监听预览面板滚动事件实现触底加载
|
||||||
|
if (previewBody) {
|
||||||
|
previewBody.addEventListener('scroll', handleScroll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理滚动事件
|
||||||
|
function handleScroll(e) {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||||
|
// 距离底部 50px 时触发加载
|
||||||
|
if (scrollHeight - scrollTop - clientHeight < 50) {
|
||||||
|
loadMoreBlogPosts();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开完整弹窗
|
// 打开完整弹窗
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${BLOG_API_URL}?page=${currentPage}&pageSize=${PAGE_SIZE}&showContent=false&showDigest=excerpt&limit=30`, {
|
const response = await fetch(`${BLOG_API_URL}?page=${currentPage}&pageSize=${PAGE_SIZE}&showContent=false&showDigest=excerpt&limit=60`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -94,12 +95,13 @@
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
const posts = result.data.dataSet || [];
|
const posts = result.data.dataSet || [];
|
||||||
const totalPages = result.data.pages || 0;
|
const totalPages = result.data.pages || 0;
|
||||||
|
const totalCount = result.data.count || 0; // 获取总数
|
||||||
|
|
||||||
// 判断是否还有更多数据
|
// 判断是否还有更多数据
|
||||||
hasMore = currentPage < totalPages;
|
hasMore = currentPage < totalPages;
|
||||||
|
|
||||||
if (reset) {
|
if (reset) {
|
||||||
renderPosts(posts, true);
|
renderPosts(posts, true, totalCount);
|
||||||
} else {
|
} else {
|
||||||
appendPosts(posts);
|
appendPosts(posts);
|
||||||
}
|
}
|
||||||
@@ -117,34 +119,53 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 渲染文章列表
|
// 渲染文章列表
|
||||||
function renderPosts(posts, isReset = false) {
|
function renderPosts(posts, isReset = false, totalCount = 0) {
|
||||||
let html = '';
|
// 预览面板 HTML(无底部分页按钮,使用无限滚动)
|
||||||
|
let previewHtml = '';
|
||||||
|
// 全屏弹窗 HTML(保留底部分页按钮)
|
||||||
|
let fullHtml = '';
|
||||||
|
|
||||||
if (!posts || posts.length === 0) {
|
if (!posts || posts.length === 0) {
|
||||||
html = '<div class="guestbook-empty">暂无文章</div>';
|
const emptyHtml = '<div class="guestbook-empty">暂无文章</div>';
|
||||||
|
previewHtml = emptyHtml;
|
||||||
|
fullHtml = emptyHtml;
|
||||||
} else {
|
} else {
|
||||||
html = '<div class="blog-posts-container">';
|
const containerHtml = '<div class="blog-posts-container">' +
|
||||||
posts.forEach(post => {
|
posts.map(post => renderPostItem(post)).join('') +
|
||||||
html += renderPostItem(post);
|
'</div>';
|
||||||
});
|
|
||||||
html += '</div>';
|
previewHtml = containerHtml;
|
||||||
|
if (!hasMore) {
|
||||||
|
previewHtml += `<div class="load-complete">已显示全部</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
fullHtml = containerHtml;
|
||||||
if (hasMore) {
|
if (hasMore) {
|
||||||
html += `<button class="load-more-btn" onclick="window.loadMoreBlogPosts()">点击加载更多</button>`;
|
fullHtml += `<button class="load-more-btn" onclick="window.loadMoreBlogPosts()">点击加载更多</button>`;
|
||||||
} else {
|
} else {
|
||||||
html += `<div class="load-complete">已显示全部</div>`;
|
fullHtml += `<div class="load-complete">已显示全部</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blogPostsList) blogPostsList.innerHTML = html;
|
if (blogPostsList) blogPostsList.innerHTML = previewHtml;
|
||||||
if (blogPostsFullBody) blogPostsFullBody.innerHTML = html;
|
if (blogPostsFullBody) blogPostsFullBody.innerHTML = fullHtml;
|
||||||
|
|
||||||
|
// 更新预览面板标题显示总数
|
||||||
|
const previewHeader = document.querySelector('#blog-posts-preview .preview-header h3');
|
||||||
|
if (previewHeader && totalCount > 0) {
|
||||||
|
const lang = localStorage.getItem('preferred_language') || 'zh';
|
||||||
|
const translations = {
|
||||||
|
'zh': typeof translationsZH !== 'undefined' ? translationsZH : {},
|
||||||
|
'en': typeof translationsEN !== 'undefined' ? translationsEN : {}
|
||||||
|
};
|
||||||
|
const titleText = translations[lang]['blog_latest_posts'] || '最新文章';
|
||||||
|
previewHeader.textContent = `${titleText} (${totalCount})`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 追加文章
|
// 追加文章
|
||||||
function appendPosts(posts) {
|
function appendPosts(posts) {
|
||||||
const containers = document.querySelectorAll('.blog-posts-container');
|
const containers = document.querySelectorAll('.blog-posts-container');
|
||||||
const loadMoreBtns = document.querySelectorAll('.load-more-btn');
|
|
||||||
const oldTips = document.querySelectorAll('.load-complete');
|
|
||||||
|
|
||||||
if (containers.length === 0) {
|
if (containers.length === 0) {
|
||||||
console.error('未找到 .blog-posts-container 容器');
|
console.error('未找到 .blog-posts-container 容器');
|
||||||
@@ -158,29 +179,29 @@
|
|||||||
div.innerHTML = renderPostItem(post);
|
div.innerHTML = renderPostItem(post);
|
||||||
container.appendChild(div.firstElementChild);
|
container.appendChild(div.firstElementChild);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// 移除所有旧的加载更多按钮和提示
|
// 移除当前容器后的旧按钮和提示(只操作当前容器的兄弟元素)
|
||||||
loadMoreBtns.forEach(btn => btn.remove());
|
let nextSibling = container.nextElementSibling;
|
||||||
oldTips.forEach(tip => tip.remove());
|
while (nextSibling && (nextSibling.classList.contains('load-more-btn') || nextSibling.classList.contains('load-complete'))) {
|
||||||
|
const toRemove = nextSibling;
|
||||||
|
nextSibling = nextSibling.nextElementSibling;
|
||||||
|
toRemove.remove();
|
||||||
|
}
|
||||||
|
|
||||||
// 向所有容器后添加新的加载更多按钮或提示
|
// 在当前容器后添加新的加载更多按钮或提示
|
||||||
if (hasMore) {
|
if (hasMore) {
|
||||||
containers.forEach(container => {
|
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'load-more-btn';
|
btn.className = 'load-more-btn';
|
||||||
btn.textContent = '点击加载更多';
|
btn.textContent = '点击加载更多';
|
||||||
btn.onclick = window.loadMoreBlogPosts;
|
btn.onclick = window.loadMoreBlogPosts;
|
||||||
container.after(btn);
|
container.after(btn);
|
||||||
});
|
} else {
|
||||||
} else {
|
|
||||||
containers.forEach(container => {
|
|
||||||
const tip = document.createElement('div');
|
const tip = document.createElement('div');
|
||||||
tip.className = 'load-complete';
|
tip.className = 'load-complete';
|
||||||
tip.textContent = '已显示全部';
|
tip.textContent = '已显示全部';
|
||||||
container.after(tip);
|
container.after(tip);
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
console.log(`成功追加 ${posts.length} 篇文章到 ${containers.length} 个容器,当前页码: ${currentPage}`);
|
console.log(`成功追加 ${posts.length} 篇文章到 ${containers.length} 个容器,当前页码: ${currentPage}`);
|
||||||
}
|
}
|
||||||
@@ -190,9 +211,13 @@
|
|||||||
const date = new Date(post.created * 1000);
|
const date = new Date(post.created * 1000);
|
||||||
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||||
const category = post.categories && post.categories.length > 0 ? post.categories[0].name : '未分类';
|
const category = post.categories && post.categories.length > 0 ? post.categories[0].name : '未分类';
|
||||||
|
const digest = post.digest || '';
|
||||||
|
|
||||||
let html = `<div class="blog-post-item" onclick="window.open('${post.permalink}', '_blank')">`;
|
let html = `<div class="blog-post-item" onclick="showCustomConfirm('确定要打开网站吗?\\n${post.permalink}', () => window.open('${post.permalink}', '_blank'))">`;
|
||||||
html += `<div class="blog-post-title">${escapeHtml(post.title)}</div>`;
|
html += `<div class="blog-post-title">${escapeHtml(post.title)}</div>`;
|
||||||
|
if (digest) {
|
||||||
|
html += `<div class="blog-post-digest">${escapeHtml(digest)}</div>`;
|
||||||
|
}
|
||||||
html += `<div class="blog-post-meta">`;
|
html += `<div class="blog-post-meta">`;
|
||||||
html += `<span>${dateStr}</span>`;
|
html += `<span>${dateStr}</span>`;
|
||||||
html += `<span class="blog-post-category">${escapeHtml(category)}</span>`;
|
html += `<span class="blog-post-category">${escapeHtml(category)}</span>`;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Flask API 地址
|
// Flask API 地址
|
||||||
const API_BASE_URL = 'http://localhost:8360';
|
const API_BASE_URL = 'https://comments.iletter.top';
|
||||||
const PAGE_SIZE = 10;
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
@@ -63,20 +63,6 @@
|
|||||||
// 关闭按钮
|
// 关闭按钮
|
||||||
guestbookClose.addEventListener('click', closeFullModal);
|
guestbookClose.addEventListener('click', closeFullModal);
|
||||||
|
|
||||||
// 点击背景关闭
|
|
||||||
guestbookFullModal.addEventListener('click', function(e) {
|
|
||||||
if (e.target === guestbookFullModal) {
|
|
||||||
closeFullModal();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ESC 键关闭
|
|
||||||
document.addEventListener('keydown', function(e) {
|
|
||||||
if (e.key === 'Escape' && guestbookFullModal.classList.contains('active')) {
|
|
||||||
closeFullModal();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 预览面板触底加载
|
// 预览面板触底加载
|
||||||
if (previewBody) {
|
if (previewBody) {
|
||||||
previewBody.addEventListener('scroll', handlePreviewScroll);
|
previewBody.addEventListener('scroll', handlePreviewScroll);
|
||||||
@@ -138,7 +124,7 @@
|
|||||||
guestbookBody.innerHTML = '<div class="guestbook-loading" data-i18n="guestbook_loading">正在加载留言...</div>';
|
guestbookBody.innerHTML = '<div class="guestbook-loading" data-i18n="guestbook_loading">正在加载留言...</div>';
|
||||||
} else {
|
} else {
|
||||||
// 添加加载更多按钮状态
|
// 添加加载更多按钮状态
|
||||||
const loadMoreBtn = document.querySelector('.load-more-btn');
|
const loadMoreBtn = guestbookBody.querySelector('.load-more-btn');
|
||||||
if (loadMoreBtn) {
|
if (loadMoreBtn) {
|
||||||
loadMoreBtn.disabled = true;
|
loadMoreBtn.disabled = true;
|
||||||
loadMoreBtn.textContent = '加载中...';
|
loadMoreBtn.textContent = '加载中...';
|
||||||
@@ -210,19 +196,35 @@
|
|||||||
// 判断是否还有更多数据
|
// 判断是否还有更多数据
|
||||||
if (hasMore) {
|
if (hasMore) {
|
||||||
html += `<button class="load-more-btn" onclick="window.loadMoreGuestbook()">点击加载更多</button>`;
|
html += `<button class="load-more-btn" onclick="window.loadMoreGuestbook()">点击加载更多</button>`;
|
||||||
} else {
|
|
||||||
html += `<div class="load-complete">已显示全部</div>`;
|
|
||||||
}
|
}
|
||||||
|
// 否则不显示任何内容
|
||||||
|
|
||||||
guestbookBody.innerHTML = html;
|
guestbookBody.innerHTML = html;
|
||||||
|
|
||||||
// 同时更新 Hover 预览面板(使用累积的所有留言)
|
// 同时更新 Hover 预览面板(使用累积的所有留言)
|
||||||
renderPreview(allComments);
|
renderPreview(allComments, totalCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲染 Hover 预览面板
|
// 渲染 Hover 预览面板
|
||||||
function renderPreview(comments) {
|
function renderPreview(comments, totalCount = 0) {
|
||||||
if (!previewBody || !comments || comments.length === 0) return;
|
if (!previewBody) return;
|
||||||
|
|
||||||
|
if (!comments || comments.length === 0) {
|
||||||
|
previewBody.innerHTML = '<div class="guestbook-empty">暂无留言,快来抢沙发吧!</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新标题显示总数
|
||||||
|
const previewHeader = document.querySelector('#guestbook-preview .preview-header h3');
|
||||||
|
if (previewHeader && totalCount > 0) {
|
||||||
|
const lang = localStorage.getItem('preferred_language') || 'zh';
|
||||||
|
const translations = {
|
||||||
|
'zh': typeof translationsZH !== 'undefined' ? translationsZH : {},
|
||||||
|
'en': typeof translationsEN !== 'undefined' ? translationsEN : {}
|
||||||
|
};
|
||||||
|
const titleText = translations[lang]['guestbook_title'] || '留言板';
|
||||||
|
previewHeader.textContent = `${titleText} (${totalCount})`;
|
||||||
|
}
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
comments.forEach(comment => {
|
comments.forEach(comment => {
|
||||||
@@ -268,9 +270,8 @@
|
|||||||
|
|
||||||
// 追加留言
|
// 追加留言
|
||||||
function appendGuestbook(comments) {
|
function appendGuestbook(comments) {
|
||||||
const listContainer = document.querySelector('.guestbook-list');
|
const listContainer = guestbookBody.querySelector('.guestbook-list');
|
||||||
const loadMoreBtn = document.querySelector('.load-more-btn');
|
const loadMoreBtn = guestbookBody.querySelector('.load-more-btn');
|
||||||
const allDataTip = document.querySelector('.guestbook-body > div:last-child');
|
|
||||||
|
|
||||||
if (!listContainer) return;
|
if (!listContainer) return;
|
||||||
|
|
||||||
@@ -280,16 +281,12 @@
|
|||||||
listContainer.appendChild(div.firstElementChild);
|
listContainer.appendChild(div.firstElementChild);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 移除旧的加载更多按钮和提示
|
// 移除旧的加载更多按钮
|
||||||
if (loadMoreBtn) {
|
if (loadMoreBtn) {
|
||||||
loadMoreBtn.remove();
|
loadMoreBtn.remove();
|
||||||
}
|
}
|
||||||
const oldTip = document.querySelector('.load-complete');
|
|
||||||
if (oldTip) {
|
|
||||||
oldTip.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果还有更多,添加新的加载更多按钮
|
// 如果还有更多,添加新的加载更多按钮;否则显示"已显示全部"
|
||||||
if (hasMore) {
|
if (hasMore) {
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = 'load-more-btn';
|
btn.className = 'load-more-btn';
|
||||||
@@ -297,7 +294,6 @@
|
|||||||
btn.onclick = window.loadMoreGuestbook;
|
btn.onclick = window.loadMoreGuestbook;
|
||||||
listContainer.after(btn);
|
listContainer.after(btn);
|
||||||
} else {
|
} else {
|
||||||
// 没有更多数据,显示提示
|
|
||||||
const tip = document.createElement('div');
|
const tip = document.createElement('div');
|
||||||
tip.className = 'load-complete';
|
tip.className = 'load-complete';
|
||||||
tip.textContent = '已显示全部';
|
tip.textContent = '已显示全部';
|
||||||
@@ -305,7 +301,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新预览面板
|
// 更新预览面板
|
||||||
renderPreview(allComments);
|
renderPreview(allComments, allComments.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲染单个留言项
|
// 渲染单个留言项
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ const translationsEN = {
|
|||||||
social_link3: "Zhihu",
|
social_link3: "Zhihu",
|
||||||
social_link4: "Bilibili",
|
social_link4: "Bilibili",
|
||||||
social_link5: "52pojie",
|
social_link5: "52pojie",
|
||||||
|
social_link6: "XiaoHongShu",
|
||||||
|
|
||||||
// 留言
|
// 留言
|
||||||
leave_message_h3: "Leave a Message",
|
leave_message_h3: "Leave a Message",
|
||||||
leave_message_p:
|
leave_message_p:
|
||||||
@@ -73,5 +75,8 @@ const translationsEN = {
|
|||||||
// Top-right buttons
|
// Top-right buttons
|
||||||
btn_guestbook: "Guestbook",
|
btn_guestbook: "Guestbook",
|
||||||
btn_blog_posts: "Blog Posts",
|
btn_blog_posts: "Blog Posts",
|
||||||
|
btn_photo_album: "Travel Shadows",
|
||||||
blog_latest_posts: "Latest Posts",
|
blog_latest_posts: "Latest Posts",
|
||||||
|
photo_latest_photos: "Latest Photos",
|
||||||
|
photo_album_title: "Travel Shadows",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ const translationsZH = {
|
|||||||
social_link3: "知乎",
|
social_link3: "知乎",
|
||||||
social_link4: "B站",
|
social_link4: "B站",
|
||||||
social_link5: "52pojie",
|
social_link5: "52pojie",
|
||||||
|
social_link6: "小红书",
|
||||||
|
|
||||||
// 留言
|
// 留言
|
||||||
leave_message_h3: "留言",
|
leave_message_h3: "留言",
|
||||||
leave_message_p:
|
leave_message_p:
|
||||||
@@ -73,5 +75,8 @@ const translationsZH = {
|
|||||||
// 右上角按钮
|
// 右上角按钮
|
||||||
btn_guestbook: "留言板",
|
btn_guestbook: "留言板",
|
||||||
btn_blog_posts: "博客文章",
|
btn_blog_posts: "博客文章",
|
||||||
|
btn_photo_album: "旅途剪影",
|
||||||
blog_latest_posts: "最新文章",
|
blog_latest_posts: "最新文章",
|
||||||
|
photo_latest_photos: "最新照片",
|
||||||
|
photo_album_title: "旅途剪影",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
let html = "";
|
let html = "";
|
||||||
memos.forEach((memo) => {
|
memos.forEach((memo) => {
|
||||||
const formattedDate = formatDate(memo.displayTime);
|
const formattedDate = formatDate(memo.createTime);
|
||||||
const tagsHtml =
|
const tagsHtml =
|
||||||
memo.tags && memo.tags.length > 0
|
memo.tags && memo.tags.length > 0
|
||||||
? `<div class="memo-tags">${memo.tags
|
? `<div class="memo-tags">${memo.tags
|
||||||
|
|||||||
318
static/js/photo_album.js
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
// ==================== 旅途剪影功能 ====================
|
||||||
|
(function() {
|
||||||
|
// DOM 元素
|
||||||
|
const photoAlbumBtn = document.getElementById('photo-album-btn');
|
||||||
|
const photoAlbumPreview = document.getElementById('photo-album-preview');
|
||||||
|
const photoAlbumFullModal = document.getElementById('photo-album-full-modal');
|
||||||
|
const photoAlbumClose = document.getElementById('photo-album-close');
|
||||||
|
const photoAlbumList = document.querySelector('.photo-album-list');
|
||||||
|
const photoAlbumBody = document.querySelector('#photo-album-full-modal .photo-album-body');
|
||||||
|
|
||||||
|
// 照片数据
|
||||||
|
let allPhotos = [];
|
||||||
|
let currentPhotoIndex = 0; // 当前查看的照片索引
|
||||||
|
const PHOTO_BASE_PATH = './static/img/photos/';
|
||||||
|
|
||||||
|
// 分页加载配置
|
||||||
|
const PAGE_SIZE = 20; // 每页加载数量
|
||||||
|
let currentPage = 1; // 当前页码
|
||||||
|
let hasMore = true; // 是否还有更多数据
|
||||||
|
let isLoading = false; // 是否正在加载中
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
function init() {
|
||||||
|
if (!photoAlbumBtn || !photoAlbumFullModal) return;
|
||||||
|
|
||||||
|
// 点击按钮打开完整弹窗
|
||||||
|
photoAlbumBtn.addEventListener('click', openFullModal);
|
||||||
|
|
||||||
|
// 关闭按钮
|
||||||
|
photoAlbumClose.addEventListener('click', closeFullModal);
|
||||||
|
|
||||||
|
// 页面加载时自动加载照片(用于 Hover 预览)
|
||||||
|
loadPhotos(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载照片
|
||||||
|
async function loadPhotos(reset = false) {
|
||||||
|
if (reset) {
|
||||||
|
if (photoAlbumList) photoAlbumList.innerHTML = '<div class="guestbook-loading">正在加载照片...</div>';
|
||||||
|
if (photoAlbumBody) photoAlbumBody.innerHTML = '<div class="guestbook-loading">正在加载照片...</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从 JSON 文件获取照片列表
|
||||||
|
const response = await fetch(`${PHOTO_BASE_PATH}photos.json`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to load photos.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const photoFiles = data.photos || [];
|
||||||
|
|
||||||
|
allPhotos = photoFiles.map((filename, index) => ({
|
||||||
|
id: index + 1,
|
||||||
|
src: `${PHOTO_BASE_PATH}${encodeURIComponent(filename)}`,
|
||||||
|
filename: filename
|
||||||
|
}));
|
||||||
|
|
||||||
|
renderPhotos(allPhotos, reset);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载照片失败:', error);
|
||||||
|
if (reset) {
|
||||||
|
const errorMsg = '<div class="guestbook-empty">加载照片失败,请稍后重试</div>';
|
||||||
|
if (photoAlbumList) photoAlbumList.innerHTML = errorMsg;
|
||||||
|
if (photoAlbumBody) photoAlbumBody.innerHTML = errorMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染照片列表
|
||||||
|
function renderPhotos(photos, isReset = false) {
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
if (!photos || photos.length === 0) {
|
||||||
|
html = '<div class="guestbook-empty">暂无照片</div>';
|
||||||
|
} else {
|
||||||
|
// 预览面板:显示最新的几张照片
|
||||||
|
if (isReset && photoAlbumList) {
|
||||||
|
const previewPhotos = photos.slice(0, 5); // 只显示前5张
|
||||||
|
let previewHtml = '<div class="photo-album-container">';
|
||||||
|
previewPhotos.forEach((photo, index) => {
|
||||||
|
previewHtml += `
|
||||||
|
<div class="photo-preview-item" onclick="window.openPhotoLightbox(${index})">
|
||||||
|
<img src="${photo.src}" alt="照片 ${photo.id}" loading="lazy">
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
previewHtml += '</div>';
|
||||||
|
photoAlbumList.innerHTML = previewHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完整弹窗:瀑布流布局显示当前页的照片
|
||||||
|
const startIndex = (currentPage - 1) * PAGE_SIZE;
|
||||||
|
const endIndex = Math.min(currentPage * PAGE_SIZE, photos.length);
|
||||||
|
const currentPhotos = photos.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
// 首次渲染:创建完整的 HTML
|
||||||
|
html = '<div class="photo-album-grid">';
|
||||||
|
currentPhotos.forEach((photo, index) => {
|
||||||
|
const globalIndex = startIndex + index;
|
||||||
|
html += `
|
||||||
|
<div class="photo-album-item" onclick="window.openPhotoLightbox(${globalIndex})">
|
||||||
|
<div class="photo-loading-spinner"></div>
|
||||||
|
<img src="${photo.src}" alt="照片 ${photo.id}" loading="lazy" onload="this.style.opacity=1; this.previousElementSibling.style.display='none'">
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
// 分页导航
|
||||||
|
const totalPages = Math.ceil(photos.length / PAGE_SIZE);
|
||||||
|
if (totalPages > 1) {
|
||||||
|
html += '<div class="pagination-container">';
|
||||||
|
|
||||||
|
// 上一页
|
||||||
|
if (currentPage > 1) {
|
||||||
|
html += `<button class="page-btn" onclick="window.goToPhotoPage(${currentPage - 1})"><</button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页码
|
||||||
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
if (i === currentPage) {
|
||||||
|
html += `<span class="page-num active">${i}</span>`;
|
||||||
|
} else {
|
||||||
|
// 只显示当前页附近的页码,避免太多
|
||||||
|
if (i === 1 || i === totalPages || (i >= currentPage - 2 && i <= currentPage + 2)) {
|
||||||
|
html += `<span class="page-num" onclick="window.goToPhotoPage(${i})">${i}</span>`;
|
||||||
|
} else if (i === currentPage - 3 || i === currentPage + 3) {
|
||||||
|
html += `<span class="page-dots">...</span>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下一页
|
||||||
|
if (currentPage < totalPages) {
|
||||||
|
html += `<button class="page-btn" onclick="window.goToPhotoPage(${currentPage + 1})">></button>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (photoAlbumBody) photoAlbumBody.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开完整弹窗
|
||||||
|
function openFullModal() {
|
||||||
|
photoAlbumFullModal.classList.add('active');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
// 重置分页状态
|
||||||
|
currentPage = 1;
|
||||||
|
hasMore = allPhotos.length > PAGE_SIZE;
|
||||||
|
|
||||||
|
// 清空弹窗内容,避免重复渲染
|
||||||
|
if (photoAlbumBody) photoAlbumBody.innerHTML = '';
|
||||||
|
|
||||||
|
renderPhotos(allPhotos, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭完整弹窗
|
||||||
|
function closeFullModal() {
|
||||||
|
photoAlbumFullModal.classList.remove('active');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开照片放大查看器
|
||||||
|
window.openPhotoLightbox = function(photoIndex) {
|
||||||
|
currentPhotoIndex = photoIndex;
|
||||||
|
|
||||||
|
// 检查是否已经存在 lightbox(使用特定的类名避免与MookNote冲突)
|
||||||
|
let lightbox = document.querySelector('.photo-album-lightbox');
|
||||||
|
if (!lightbox) {
|
||||||
|
lightbox = document.createElement('div');
|
||||||
|
lightbox.className = 'photo-lightbox photo-album-lightbox'; // 同时保留两个类名以兼容CSS
|
||||||
|
lightbox.innerHTML = `
|
||||||
|
<span class="photo-lightbox-close">×</span>
|
||||||
|
<span class="photo-lightbox-nav photo-lightbox-prev">❮</span>
|
||||||
|
<img src="" alt="放大照片">
|
||||||
|
<span class="photo-lightbox-nav photo-lightbox-next">❯</span>
|
||||||
|
<div class="photo-lightbox-counter"></div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(lightbox);
|
||||||
|
|
||||||
|
// 点击关闭按钮
|
||||||
|
lightbox.querySelector('.photo-lightbox-close').addEventListener('click', closePhotoLightbox);
|
||||||
|
|
||||||
|
// 上一张按钮
|
||||||
|
lightbox.querySelector('.photo-lightbox-prev').addEventListener('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
showPreviousPhoto();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 下一张按钮
|
||||||
|
lightbox.querySelector('.photo-lightbox-next').addEventListener('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
showNextPhoto();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 键盘事件
|
||||||
|
document.addEventListener('keydown', handleLightboxKeydown);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新图片显示
|
||||||
|
updateLightboxImage();
|
||||||
|
lightbox.classList.add('active');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新 Lightbox 图片
|
||||||
|
function updateLightboxImage() {
|
||||||
|
const lightbox = document.querySelector('.photo-album-lightbox');
|
||||||
|
if (!lightbox || !allPhotos[currentPhotoIndex]) return;
|
||||||
|
|
||||||
|
const img = lightbox.querySelector('img');
|
||||||
|
const counter = lightbox.querySelector('.photo-lightbox-counter');
|
||||||
|
|
||||||
|
// 淡出效果
|
||||||
|
img.style.opacity = '0';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
img.src = allPhotos[currentPhotoIndex].src;
|
||||||
|
img.alt = `照片 ${currentPhotoIndex + 1}`;
|
||||||
|
|
||||||
|
// 更新计数器
|
||||||
|
if (counter) {
|
||||||
|
counter.textContent = `${currentPhotoIndex + 1} / ${allPhotos.length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 淡入效果
|
||||||
|
img.onload = () => {
|
||||||
|
img.style.opacity = '1';
|
||||||
|
};
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示上一张照片
|
||||||
|
function showPreviousPhoto() {
|
||||||
|
if (currentPhotoIndex > 0) {
|
||||||
|
currentPhotoIndex--;
|
||||||
|
} else {
|
||||||
|
currentPhotoIndex = allPhotos.length - 1; // 循环到最后一张
|
||||||
|
}
|
||||||
|
updateLightboxImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示下一张照片
|
||||||
|
function showNextPhoto() {
|
||||||
|
if (currentPhotoIndex < allPhotos.length - 1) {
|
||||||
|
currentPhotoIndex++;
|
||||||
|
} else {
|
||||||
|
currentPhotoIndex = 0; // 循环到第一张
|
||||||
|
}
|
||||||
|
updateLightboxImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理键盘事件
|
||||||
|
function handleLightboxKeydown(e) {
|
||||||
|
// 只在旅途剪影的lightbox激活时处理
|
||||||
|
const albumLightbox = document.querySelector('.photo-album-lightbox');
|
||||||
|
if (!albumLightbox || !albumLightbox.classList.contains('active')) return;
|
||||||
|
|
||||||
|
if (e.key === 'ArrowLeft') {
|
||||||
|
showPreviousPhoto();
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
showNextPhoto();
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
closePhotoLightbox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭照片放大查看器
|
||||||
|
function closePhotoLightbox() {
|
||||||
|
const lightbox = document.querySelector('.photo-album-lightbox');
|
||||||
|
if (lightbox) {
|
||||||
|
lightbox.classList.remove('active');
|
||||||
|
|
||||||
|
// 移除键盘事件监听器
|
||||||
|
document.removeEventListener('keydown', handleLightboxKeydown);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
lightbox.remove();
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多照片
|
||||||
|
window.loadMorePhotos = function() {
|
||||||
|
if (isLoading || !hasMore) return;
|
||||||
|
|
||||||
|
isLoading = true;
|
||||||
|
currentPage++;
|
||||||
|
|
||||||
|
// 判断是否还有更多数据
|
||||||
|
hasMore = currentPage * PAGE_SIZE < allPhotos.length;
|
||||||
|
|
||||||
|
renderPhotos(allPhotos, false);
|
||||||
|
isLoading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 跳转到指定页码
|
||||||
|
window.goToPhotoPage = function(page) {
|
||||||
|
if (page < 1 || page > Math.ceil(allPhotos.length / PAGE_SIZE)) return;
|
||||||
|
currentPage = page;
|
||||||
|
renderPhotos(allPhotos, false);
|
||||||
|
// 滚动到顶部
|
||||||
|
if (photoAlbumBody) photoAlbumBody.scrollTop = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// DOM 加载完成后初始化
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -1,96 +1,53 @@
|
|||||||
// ========== 网站链接数据 (包含中英双语) ==========
|
// ========== 网站链接数据 (包含中英双语) ==========
|
||||||
const websitesData = [
|
const websitesData = [
|
||||||
{
|
{ category_zh: '我的作品', category_en: 'My Works', items: [
|
||||||
// 分类标题
|
{ url: 'https://mooknote.iletter.top/#/',
|
||||||
category_zh: '个人博客',
|
linkText_zh: 'MookNote - 观影阅读笔记管理',
|
||||||
category_en: 'Personal Blogs',
|
linkText_en: 'MookNote - Movie & Reading Note Manager',
|
||||||
items: [
|
desc_zh: '用于记录观影、阅读及笔记的个人知识管理应用。',
|
||||||
{
|
desc_en: 'Personal knowledge management app for movies, reading, and notes.',
|
||||||
url: 'https://www.ittoolman.top/',
|
img: './static/img/my_work/mooknote/banner.jpg'
|
||||||
linkText_zh: 'ittoolman.top - Github托管',
|
}
|
||||||
linkText_en: 'ittoolman.top - Github Page'
|
]},
|
||||||
},
|
{ category_zh: '个人博客', category_en: 'Personal Blogs', items: [
|
||||||
{
|
{ url: 'https://www.ittoolman.top/',
|
||||||
url: 'https://blog.iletter.top/',
|
linkText_zh: 'ittoolman - Github托管',
|
||||||
linkText_zh: 'blog.iletter.top - typecho用户的最后坚守',
|
linkText_en: 'ittoolman - Github Page',
|
||||||
linkText_en: 'blog.iletter.top - Last Stand for Typecho Users'
|
desc_zh: '基于 GitHub Pages 搭建的个人技术站点。',
|
||||||
}
|
desc_en: 'Personal tech site based on GitHub Pages.',
|
||||||
]
|
img: './static/img/my_work/ittoolman/banner.png'
|
||||||
},
|
},
|
||||||
{
|
{ url: 'https://blog.iletter.top/',
|
||||||
category_zh: '私人在线应用',
|
linkText_zh: 'iletter - typecho极简与高效并存',
|
||||||
category_en: 'Personal Online Apps',
|
linkText_en: 'iletter - typecho Minimalism and Efficiency',
|
||||||
items: [
|
desc_zh: '记录日常开发与生活点滴的 Typecho 博客。',
|
||||||
{
|
desc_en: 'A Typecho blog recording daily development and life.',
|
||||||
url: 'https://img.iletter.top/',
|
img: './static/img/my_work/blog_iletter/banner.png'
|
||||||
linkText_zh: 'EsayImage2 - 私人图床工具',
|
}
|
||||||
linkText_en: 'EsayImage2 - Image Uploader Tool'
|
]},
|
||||||
},
|
{ category_zh: '私人在线应用', category_en: 'Personal Online Apps', items: [
|
||||||
{
|
{ img: './static/img/my_work/easyimage2/banner.png',
|
||||||
url: 'https://gitea.iletter.top/',
|
url: 'https://img.iletter.top/', linkText_zh: 'EsayImage2 - 私人图床工具', linkText_en: 'EsayImage2 - Image Uploader Tool', desc_zh: '稳定高效的图片上传与托管服务。', desc_en: 'Stable and efficient image hosting service.' },
|
||||||
linkText_zh: 'Gitea - 私人git托管仓库',
|
{ url: 'https://gitea.iletter.top/', linkText_zh: 'Gitea - 私人git托管仓库', linkText_en: 'Gitea - Private Git Repository', desc_zh: '轻量级自托管 Git 代码管理平台。', desc_en: 'Lightweight self-hosted Git code management platform.' },
|
||||||
linkText_en: 'Gitea - Private Git Repository'
|
{ url: 'http://umami.iletter.top/', linkText_zh: 'Umami - 网站访问分析', linkText_en: 'Umami - Website visit analysis', desc_zh: '简洁、隐私友好的网站流量统计工具。', desc_en: 'Simple, privacy-friendly website analytics tool.' },
|
||||||
},
|
{ url: 'http://beszel.iletter.top/', linkText_zh: 'Beszel - 服务器监控', linkText_en: 'Beszel - Server Monitoring', desc_zh: '实时监控服务器资源占用情况。', desc_en: 'Real-time monitoring of server resource usage.' },
|
||||||
{
|
{ url: 'https://memos.iletter.top/', linkText_zh: 'Memos - 个人随想', linkText_en: 'Memos - Personal Thoughts', desc_zh: '碎片化知识管理与灵感记录空间。', desc_en: 'Fragmented knowledge management and inspiration space.' },
|
||||||
url: 'http://umami.iletter.top/',
|
{ url: 'http://frp.iletter.top/', linkText_zh: 'FRP - 个人反向代理',
|
||||||
linkText_zh: 'Umami - 网站访问分析',
|
linkText_en: 'FRP - Personal Reverse Proxy', desc_zh: '用于内网穿透的高性能反向代理。',
|
||||||
linkText_en: 'Umami - Website visit analysis'
|
desc_en: 'High-performance reverse proxy for intranet penetration.',
|
||||||
},
|
img: './static/img/my_work/frp_panel/banner.png'
|
||||||
{
|
},
|
||||||
url: 'http://beszel.iletter.top/',
|
{ img: './static/img/my_work/bitwarden/banner.png',
|
||||||
linkText_zh: 'Beszel - 服务器监控',
|
url: 'https://bitwarden.iletter.top/', linkText_zh: 'Bitwarden - 我的密码库', linkText_en: 'Bitwarden - My Password Library', desc_zh: '安全可靠的开源密码管理方案。', desc_en: 'Secure and reliable open-source password management solution.' },
|
||||||
linkText_en: 'Beszel - Server Monitoring'
|
{ url: 'http://docmost.iletter.top/', linkText_zh: 'Docmost - 在线笔记', linkText_en: 'Docmost - Online Notes', desc_zh: '支持协作的实时在线文档编辑平台。', desc_en: 'Real-time online document editing platform supporting collaboration.' },
|
||||||
},
|
{ img: './static/img/my_work/openlist/banner.png',
|
||||||
{
|
url: 'http://openlist.iletter.top/', linkText_zh: 'OpenList - 在线云盘合集', linkText_en: 'OpenList - Online Cloud Disk Collection', desc_zh: '聚合多种网盘服务的文件列表管理。', desc_en: 'File list management aggregating various cloud storage services.' }
|
||||||
url: 'https://memos.iletter.top/',
|
]}
|
||||||
linkText_zh: 'Memos - 个人随想',
|
|
||||||
linkText_en: 'Memos - Personal Thoughts'
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
url: 'http://frp.iletter.top/',
|
|
||||||
linkText_zh: 'FRP - 个人反向代理',
|
|
||||||
linkText_en: 'FRP - Personal Reverse Proxy'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: 'https://bitwarden.iletter.top/',
|
|
||||||
linkText_zh: 'Bitwarden - 我的密码库',
|
|
||||||
linkText_en: 'Bitwarden - My Password Library'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: 'http://docmost.iletter.top/',
|
|
||||||
linkText_zh: 'Docmost - 在线笔记',
|
|
||||||
linkText_en: 'Docmost - Online Notes'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: 'http://openlist.iletter.top/',
|
|
||||||
linkText_zh: 'OpenList - 在线云盘合集',
|
|
||||||
linkText_en: 'OpenList - Online Cloud Disk Collection'
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// url: 'http://py.iletter.top/en-de-code',
|
|
||||||
// linkText_zh: 'Base64在线加密解密 - 自写demo',
|
|
||||||
// linkText_en: 'Base64 Online Encryption and Decryption - My Demo'
|
|
||||||
// },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
// 如果你想添加新分类,可以仿照上面的格式:
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
category_zh: '新的分类名',
|
|
||||||
category_en: 'New Category Name',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
url: 'https://example.com/',
|
|
||||||
linkText_zh: '示例网站',
|
|
||||||
linkText_en: 'Example Site'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// ========== 渲染网站链接的函数 ==========
|
// ========== 渲染网站链接的函数 ==========
|
||||||
|
let currentViewMode = localStorage.getItem('website_view_mode') || 'card'; // 'list' or 'card'
|
||||||
|
|
||||||
function renderWebsites(websitesArray) {
|
function renderWebsites(websitesArray) {
|
||||||
const container = document.getElementById('dynamic-website-links');
|
const container = document.getElementById('dynamic-website-links');
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@@ -109,39 +66,233 @@ function renderWebsites(websitesArray) {
|
|||||||
|
|
||||||
const currentLang = localStorage.getItem("preferred_language") || "zh";
|
const currentLang = localStorage.getItem("preferred_language") || "zh";
|
||||||
|
|
||||||
// 清空容器
|
// 清空容器并添加切换按钮
|
||||||
container.innerHTML = '';
|
const toggleText = currentViewMode === 'list' ? '切换为卡片视图' : '切换为列表视图';
|
||||||
|
container.innerHTML = `
|
||||||
|
<div style="text-align:right;margin-bottom:10px;">
|
||||||
|
<span class="website-view-toggle" onclick="switchWebsiteView('${currentViewMode === 'list' ? 'card' : 'list'}')">${toggleText}</span>
|
||||||
|
</div>
|
||||||
|
<div id="website-content-area"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const contentArea = container.querySelector('#website-content-area');
|
||||||
|
|
||||||
arrayToRender.forEach(categoryObj => {
|
if (currentViewMode === 'list') {
|
||||||
// 根据当前语言选择分类标题
|
arrayToRender.forEach(categoryObj => {
|
||||||
const categoryName = currentLang === 'en' ? categoryObj.category_en : categoryObj.category_zh;
|
const categoryName = currentLang === 'en' ? categoryObj.category_en : categoryObj.category_zh;
|
||||||
|
const heading = document.createElement('h3');
|
||||||
|
heading.textContent = categoryName;
|
||||||
|
contentArea.appendChild(heading);
|
||||||
|
|
||||||
// 创建分类标题
|
const linksDiv = document.createElement('div');
|
||||||
const heading = document.createElement('h3');
|
linksDiv.className = 'category-links';
|
||||||
heading.textContent = categoryName;
|
|
||||||
container.appendChild(heading);
|
|
||||||
|
|
||||||
// 创建一个 div 来包裹该分类下的所有链接
|
categoryObj.items.forEach(item => {
|
||||||
const linksDiv = document.createElement('div');
|
const linkElement = document.createElement('a');
|
||||||
linksDiv.className = 'category-links'; // 可选,方便 CSS 样式化
|
linkElement.href = '#';
|
||||||
|
linkElement.textContent = currentLang === 'en' ? item.linkText_en : item.linkText_zh;
|
||||||
categoryObj.items.forEach(item => {
|
|
||||||
const linkElement = document.createElement('a');
|
if (item.url === 'https://mooknote.iletter.top/#/') {
|
||||||
linkElement.href = item.url;
|
linkElement.onclick = (e) => {
|
||||||
linkElement.target = '_blank';
|
e.preventDefault();
|
||||||
|
openMookNoteModal();
|
||||||
// 根据当前语言选择链接文本
|
};
|
||||||
const linkText = currentLang === 'en' ? item.linkText_en : item.linkText_zh;
|
} else {
|
||||||
linkElement.textContent = linkText;
|
linkElement.onclick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
linksDiv.appendChild(linkElement);
|
showCustomConfirm(`确定要打开网站吗?\n${item.url}`, () => {
|
||||||
|
window.open(item.url, '_blank');
|
||||||
// 添加一个间隔(例如空格或换行符,取决于你的 CSS 布局)
|
});
|
||||||
const space = document.createTextNode(' ');
|
};
|
||||||
linksDiv.appendChild(space);
|
}
|
||||||
|
|
||||||
|
linksDiv.appendChild(linkElement);
|
||||||
|
const space = document.createTextNode(' ');
|
||||||
|
linksDiv.appendChild(space);
|
||||||
|
});
|
||||||
|
contentArea.appendChild(linksDiv);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
arrayToRender.forEach(categoryObj => {
|
||||||
|
const categoryName = currentLang === 'en' ? categoryObj.category_en : categoryObj.category_zh;
|
||||||
|
const heading = document.createElement('h3');
|
||||||
|
heading.textContent = categoryName;
|
||||||
|
contentArea.appendChild(heading);
|
||||||
|
|
||||||
|
const gridDiv = document.createElement('div');
|
||||||
|
gridDiv.className = 'website-card-grid';
|
||||||
|
|
||||||
|
categoryObj.items.forEach(item => {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'website-card';
|
||||||
|
if (item.url === 'https://mooknote.iletter.top/#/') {
|
||||||
|
card.onclick = () => openMookNoteModal();
|
||||||
|
} else {
|
||||||
|
card.onclick = () => {
|
||||||
|
showCustomConfirm(`确定要打开网站吗?\n${item.url}`, () => {
|
||||||
|
window.open(item.url, '_blank');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = currentLang === 'en' ? item.linkText_en : item.linkText_zh;
|
||||||
|
const desc = currentLang === 'en' ? (item.desc_en || '') : (item.desc_zh || '');
|
||||||
|
|
||||||
|
const imgSrc = item.img;
|
||||||
|
const placeholderText = item.linkText_zh.split(' - ')[0];
|
||||||
|
const imgHtml = imgSrc
|
||||||
|
? `<div class="website-card-icon"><img src="${imgSrc}" alt="${placeholderText}" onerror="this.style.display='none';this.parentNode.classList.add('website-card-placeholder');this.parentNode.textContent='${placeholderText}'"></div>`
|
||||||
|
: `<div class="website-card-icon website-card-placeholder">${placeholderText}</div>`;
|
||||||
|
|
||||||
|
card.innerHTML = `
|
||||||
|
${imgHtml}
|
||||||
|
<div class="website-card-info">
|
||||||
|
<div class="website-card-title">${title}</div>
|
||||||
|
<div class="website-card-desc">${desc}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
gridDiv.appendChild(card);
|
||||||
|
});
|
||||||
|
|
||||||
|
contentArea.appendChild(gridDiv);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
container.appendChild(linksDiv);
|
window.switchWebsiteView = function(mode) {
|
||||||
|
currentViewMode = mode;
|
||||||
|
localStorage.setItem('website_view_mode', mode);
|
||||||
|
renderWebsites();
|
||||||
|
};
|
||||||
|
|
||||||
|
// MookNote 弹窗逻辑
|
||||||
|
let currentImageIndex = 0;
|
||||||
|
const mooknoteImages = document.querySelectorAll('.carousel-image');
|
||||||
|
const totalImages = mooknoteImages.length;
|
||||||
|
|
||||||
|
// 全屏预览相关变量
|
||||||
|
let lightboxImageIndex = 0;
|
||||||
|
const lightboxImgUrls = [
|
||||||
|
'./static/img/my_work/mooknote/banner.jpg',
|
||||||
|
'./static/img/my_work/mooknote/1.jpg',
|
||||||
|
'./static/img/my_work/mooknote/2.jpg',
|
||||||
|
'./static/img/my_work/mooknote/3.jpg',
|
||||||
|
'./static/img/my_work/mooknote/4.jpg',
|
||||||
|
'./static/img/my_work/mooknote/5.jpg',
|
||||||
|
'./static/img/my_work/mooknote/6.jpg'
|
||||||
|
];
|
||||||
|
|
||||||
|
// 初始化指示器
|
||||||
|
function initCarouselIndicators() {
|
||||||
|
const indicatorsContainer = document.querySelector('.carousel-indicators');
|
||||||
|
if (!indicatorsContainer) return;
|
||||||
|
|
||||||
|
indicatorsContainer.innerHTML = '';
|
||||||
|
for (let i = 0; i < totalImages; i++) {
|
||||||
|
const indicator = document.createElement('div');
|
||||||
|
indicator.className = 'carousel-indicator' + (i === 0 ? ' active' : '');
|
||||||
|
indicator.onclick = () => goToMooknoteImage(i);
|
||||||
|
indicatorsContainer.appendChild(indicator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换到指定图片
|
||||||
|
function goToMooknoteImage(index) {
|
||||||
|
if (index < 0) index = totalImages - 1;
|
||||||
|
if (index >= totalImages) index = 0;
|
||||||
|
|
||||||
|
mooknoteImages.forEach((img, i) => {
|
||||||
|
img.classList.toggle('active', i === index);
|
||||||
|
});
|
||||||
|
|
||||||
|
const indicators = document.querySelectorAll('.carousel-indicator');
|
||||||
|
indicators.forEach((indicator, i) => {
|
||||||
|
indicator.classList.toggle('active', i === index);
|
||||||
|
});
|
||||||
|
|
||||||
|
currentImageIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上一张图片
|
||||||
|
window.prevMooknoteImage = function() {
|
||||||
|
goToMooknoteImage(currentImageIndex - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下一张图片
|
||||||
|
window.nextMooknoteImage = function() {
|
||||||
|
goToMooknoteImage(currentImageIndex + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 全屏预览功能
|
||||||
|
window.openMooknoteLightbox = function(index) {
|
||||||
|
lightboxImageIndex = index;
|
||||||
|
const lightbox = document.querySelector('.mooknote-lightbox');
|
||||||
|
const img = lightbox.querySelector('.lightbox-img');
|
||||||
|
const counter = lightbox.querySelector('.lightbox-counter');
|
||||||
|
|
||||||
|
img.src = lightboxImgUrls[index];
|
||||||
|
counter.textContent = `${index + 1} / ${lightboxImgUrls.length}`;
|
||||||
|
lightbox.classList.add('active');
|
||||||
|
};
|
||||||
|
|
||||||
|
window.closeMooknoteLightbox = function() {
|
||||||
|
const lightbox = document.querySelector('.mooknote-lightbox');
|
||||||
|
lightbox.classList.remove('active');
|
||||||
|
};
|
||||||
|
|
||||||
|
window.prevLightboxImage = function() {
|
||||||
|
lightboxImageIndex = (lightboxImageIndex - 1 + lightboxImgUrls.length) % lightboxImgUrls.length;
|
||||||
|
updateLightboxImage();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.nextLightboxImage = function() {
|
||||||
|
lightboxImageIndex = (lightboxImageIndex + 1) % lightboxImgUrls.length;
|
||||||
|
updateLightboxImage();
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateLightboxImage() {
|
||||||
|
const lightbox = document.querySelector('.mooknote-lightbox');
|
||||||
|
const img = lightbox.querySelector('.lightbox-img');
|
||||||
|
const counter = lightbox.querySelector('.lightbox-counter');
|
||||||
|
|
||||||
|
img.src = lightboxImgUrls[lightboxImageIndex];
|
||||||
|
counter.textContent = `${lightboxImageIndex + 1} / ${lightboxImgUrls.length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 键盘事件支持
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
const lightbox = document.querySelector('.mooknote-lightbox');
|
||||||
|
if (!lightbox || !lightbox.classList.contains('active')) return;
|
||||||
|
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
closeMooknoteLightbox();
|
||||||
|
} else if (e.key === 'ArrowLeft') {
|
||||||
|
prevLightboxImage();
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
nextLightboxImage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function openMookNoteModal() {
|
||||||
|
const modal = document.getElementById('mooknote-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('active');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
// 重置轮播到第一张
|
||||||
|
currentImageIndex = 0;
|
||||||
|
initCarouselIndicators();
|
||||||
|
goToMooknoteImage(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mooknoteClose = document.getElementById('mooknote-close');
|
||||||
|
if (mooknoteClose) {
|
||||||
|
mooknoteClose.addEventListener('click', () => {
|
||||||
|
const modal = document.getElementById('mooknote-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||