feat(wrapped-ui): 新增 Win98 主题与桌面化外观

- 主题系统新增 win98(显示名 Windows 98,快捷键扩展到 F1-F4),并区分 retro(pixel/CRT) 与桌面 GUI 主题
- 年度总结页新增 Win98 桌面背景与底部任务栏(背景色/视口高度适配)
- 封面与卡片 slide 形态支持 Win98 窗口外观(title bar/icon/controls)
- 主题切换器补充 Win98 选项并新增 Win98 专属切换器
- 新增 Win98 图标资源(Start + 桌面图标)
This commit is contained in:
2977094657
2026-02-02 00:04:54 +08:00
parent 7ce6abecca
commit 980f15d0a4
19 changed files with 767 additions and 29 deletions

View File

@@ -7,7 +7,8 @@
>
<!-- PPT 风格单张卡片占据全页面鼠标滚轮切换 -->
<WrappedDeckBackground />
<WrappedCRTOverlay v-if="isRetro" />
<!-- CRT 叠加层仅用于像素屏/终端类主题Win98 等桌面 GUI 主题不应开启 -->
<WrappedCRTOverlay v-if="theme === 'gameboy' || theme === 'dos'" />
<!-- 左上角刷新 + 复古模式开关 -->
<div class="absolute top-6 left-6 z-20 select-none">
@@ -164,6 +165,12 @@
</WrappedCardShell>
</section>
</div>
<!-- Win98底部任务栏 -->
<WrappedWin98Taskbar
v-if="theme === 'win98'"
:title="taskbarTitle"
/>
</div>
</template>
@@ -219,7 +226,8 @@ let navUnlockTimer = null
const THEME_BG = {
off: '#F3FFF8', // Modern: 浅绿
gameboy: '#9bbc0f', // Game Boy: 亮绿
dos: '#0a0a0a' // DOS: 黑色
dos: '#0a0a0a', // DOS: 黑色
win98: '#008080' // Win98: 经典桌面青色
}
const slides = computed(() => {
@@ -229,6 +237,14 @@ const slides = computed(() => {
return out
})
const taskbarTitle = computed(() => {
if (theme.value !== 'win98') return ''
if (activeIndex.value === 0) return `${year.value} WeChat Wrapped`
const idx = activeIndex.value - 1
const c = report.value?.cards?.[idx]
return String(c?.title || 'WeChat Wrapped')
})
const currentBg = computed(() => THEME_BG[theme.value] || THEME_BG.off)
const slideStyle = computed(() => (
@@ -356,8 +372,11 @@ const onTouchEnd = (e) => {
const updateViewport = () => {
const h = deckEl.value?.clientHeight || window.innerHeight || 0
if (!h) return
// Reserve space for the Win98 taskbar at the bottom.
const offset = theme.value === 'win98' ? 40 : 0
const effective = Math.max(0, h - offset)
// Avoid endless reflows from 1px rounding errors (especially in Electron).
if (Math.abs(viewportHeight.value - h) > 1) viewportHeight.value = h
if (Math.abs(viewportHeight.value - effective) > 1) viewportHeight.value = effective
}
const loadAccounts = async () => {
@@ -515,6 +534,11 @@ onMounted(async () => {
}
})
// Theme switch may change reserved UI space (e.g., Win98 taskbar)
watch(theme, () => {
updateViewport()
})
onBeforeUnmount(() => {
window.removeEventListener('resize', updateViewport)
deckEl.value?.removeEventListener('wheel', onWheel)