新增: 相册展示模块(左侧幻灯片+进度条+目录选择)

- 左侧固定卡片展示照片幻灯片,淡入淡出切换
- 进度条动画显示当前照片剩余时间
- 设置窗口支持选择图片目录和切换间隔(5/10/15/20/30/60秒)
- Win32 SHBrowseForFolderW 目录选择对话框
- Go 端管理幻灯片状态,按间隔推送照片 data URI
This commit is contained in:
2026-05-26 04:45:58 +08:00
parent eed461e325
commit 8e7ec8424d
7 changed files with 378 additions and 0 deletions

View File

@@ -366,6 +366,55 @@ body.hide-ainews #card-ainews,
body.hide-ainews #info .ainews-section { display: none !important; }
body.hide-knowledge #card-knowledge,
body.hide-knowledge #info .knowledge-section { display: none !important; }
/* ===== 相册 ===== */
#card-photo {
position: fixed;
top: 40px;
left: 40px;
width: calc(50vw - 80px);
z-index: 10;
padding: 16px;
}
.photo-wrap {
position: relative;
border-radius: 12px;
overflow: hidden;
}
#card-photo img {
width: 100%;
max-height: 450px;
object-fit: cover;
display: block;
border-radius: 12px;
transition: opacity 0.5s ease;
}
.photo-info {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 20px 14px 10px;
background: linear-gradient(transparent, rgba(0,0,0,0.5));
}
.photo-counter {
font-size: 11px;
opacity: 0.7;
display: block;
margin-bottom: 6px;
}
.photo-progress {
height: 3px;
background: rgba(255,255,255,0.15);
border-radius: 2px;
overflow: hidden;
}
.photo-progress-bar {
height: 100%;
width: 0%;
background: rgba(255,255,255,0.6);
border-radius: 2px;
}
</style>
</head>
<body class="layout-{{LAYOUT}}">
@@ -429,6 +478,16 @@ body.hide-knowledge #info .knowledge-section { display: none !important; }
</div>
</div>
<div id="card-photo" class="card" style="display:none">
<div class="photo-wrap">
<img id="photoImg" src="" alt="">
<div class="photo-info">
<span class="photo-counter" id="photoCounter"></span>
<div class="photo-progress"><div class="photo-progress-bar" id="photoProgress"></div></div>
</div>
</div>
</div>
<div id="author">绝尘</div>
<script>
@@ -663,6 +722,31 @@ window.updateWeatherFromGo=function(data){
updateTime();
setInterval(updateTime,1000);
window.updatePhotoFromGo=function(data){
if(typeof data==='string') data=JSON.parse(data);
var card=document.getElementById('card-photo');
if(!card) return;
if(!data||!data.src){card.style.display='none';return;}
card.style.display='';
var img=document.getElementById('photoImg');
var counter=document.getElementById('photoCounter');
var bar=document.getElementById('photoProgress');
img.style.opacity='0';
setTimeout(function(){
img.src=data.src;
img.onload=function(){img.style.opacity='1';};
},300);
if(data.counter) counter.textContent=data.counter;
bar.style.transition='none';
bar.style.width='0%';
requestAnimationFrame(function(){
requestAnimationFrame(function(){
bar.style.transition='width '+(data.interval||15)+'s linear';
bar.style.width='100%';
});
});
};
</script>
</body>
</html>

View File

@@ -330,6 +330,31 @@ input[type="text"]:focus { border-color: var(--input-border-focus); }
</div>
</div>
<!-- 相册 -->
<div class="section">
<div class="section-label">相册</div>
<div class="card">
<div class="item">
<div class="item-desc" id="photoDirDisplay">未选择目录</div>
<div class="btn-group">
<button class="btn" id="btnPickPhotoDir">选择目录</button>
<button class="btn btn-sm" id="btnClearPhotoDir" style="display:none">清除</button>
</div>
</div>
<div class="item">
<div class="item-label">切换间隔</div>
<select id="photoInterval">
<option value="5">5 秒</option>
<option value="10">10 秒</option>
<option value="15" selected>15 秒</option>
<option value="20">20 秒</option>
<option value="30">30 秒</option>
<option value="60">60 秒</option>
</select>
</div>
</div>
</div>
<!-- 星座 + 城市 -->
<div class="section">
<div class="section-label">个性化</div>
@@ -462,6 +487,26 @@ document.getElementById('themeSelect').addEventListener('change', function() {
document.getElementById('textInputRow').style.display = this.value === 'text' ? 'flex' : 'none';
});
document.getElementById('btnPickPhotoDir').addEventListener('click', function() {
if (!window.pickPhotoDir) return;
window.pickPhotoDir().then(function(dir) {
if (dir) {
document.getElementById('photoDirDisplay').textContent = dir;
document.getElementById('btnClearPhotoDir').style.display = '';
}
});
});
document.getElementById('btnClearPhotoDir').addEventListener('click', function() {
if (!window.clearPhotoDir) return;
window.clearPhotoDir().then(function() {
document.getElementById('photoDirDisplay').textContent = '未选择目录';
document.getElementById('btnClearPhotoDir').style.display = 'none';
});
});
document.getElementById('photoInterval').addEventListener('change', function() {
if (window.savePhotoInterval) window.savePhotoInterval(parseInt(this.value));
});
var textTimer = null;
document.getElementById('wallpaperText').addEventListener('input', function() {
clearTimeout(textTimer);
@@ -672,6 +717,12 @@ if (window.loadAllSettings) {
if (s.knowledgeKeyword) document.getElementById('knowledgeKeyword').value = s.knowledgeKeyword;
if (s.knowledgePrompt) document.getElementById('knowledgePrompt').value = s.knowledgePrompt;
if (s.theme === 'text') document.getElementById('textInputRow').style.display = 'flex';
// Photo state
if (s.photoDir) {
document.getElementById('photoDirDisplay').textContent = s.photoDir;
document.getElementById('btnClearPhotoDir').style.display = '';
}
if (s.photoInterval) document.getElementById('photoInterval').value = s.photoInterval;
// Color state
if (s.color1) { currentColor1 = s.color1; currentColor2 = s.color2 || ''; currentGradient = s.colorGradient || false; }
if (s.wallpaperType === 'color') updateColorPreview();