mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-02-19 14:20:51 +08:00
- 新增 useWrappedTheme:主题状态全局共享、localStorage 持久化,支持 F1-F4 快捷键与循环切换 - 新增主题切换器组件(Modern/Game Boy/DOS/VHS)与主题化年份选择器 - 年度总结页接入 themeClass/currentBg;CRT 叠加层支持 VHS 效果(REC/时间戳/跟踪线) - 补充主题全局样式与卡片/控制面板主题适配
182 lines
5.8 KiB
Vue
182 lines
5.8 KiB
Vue
<template>
|
||
<div class="bg-white rounded-2xl border border-[#EDEDED] p-5 sm:p-6 controls-panel">
|
||
<div class="flex flex-col gap-4">
|
||
<div class="flex flex-col sm:flex-row gap-3 sm:items-end sm:justify-between">
|
||
<div class="flex flex-col sm:flex-row gap-3 sm:items-end">
|
||
<div v-if="showAccount">
|
||
<div class="wrapped-label text-xs text-[#00000099] mb-1 controls-label">Account</div>
|
||
<select
|
||
class="w-full sm:w-56 px-3 py-2 rounded-lg border border-[#EDEDED] bg-white text-sm wrapped-body focus:outline-none focus:ring-2 focus:ring-[#07C160] controls-select"
|
||
:disabled="accountsLoading || accounts.length === 0"
|
||
:value="modelAccount"
|
||
@change="$emit('update:account', $event.target.value || '')"
|
||
>
|
||
<option value="" :disabled="accounts.length > 0">默认(自动选择)</option>
|
||
<option v-for="a in accounts" :key="a" :value="a">{{ a }}</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="wrapped-label text-xs text-[#00000099] mb-1 controls-label">Year</div>
|
||
<select
|
||
class="w-full sm:w-40 px-3 py-2 rounded-lg border border-[#EDEDED] bg-white text-sm wrapped-body focus:outline-none focus:ring-2 focus:ring-[#07C160] controls-select"
|
||
:value="String(modelYear)"
|
||
@change="$emit('update:year', Number($event.target.value))"
|
||
>
|
||
<option v-for="y in yearOptions" :key="y" :value="String(y)">{{ y }}年</option>
|
||
</select>
|
||
</div>
|
||
|
||
<label class="inline-flex items-center gap-2 select-none">
|
||
<input
|
||
type="checkbox"
|
||
class="h-4 w-4 rounded border-[#EDEDED] text-[#07C160] focus:ring-[#07C160] controls-checkbox"
|
||
:checked="modelRefresh"
|
||
@change="$emit('update:refresh', !!$event.target.checked)"
|
||
/>
|
||
<span class="wrapped-body text-sm text-[#7F7F7F] controls-hint">强制刷新(忽略缓存)</span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="flex gap-2 items-end">
|
||
<WrappedThemeSwitcher />
|
||
<button
|
||
class="inline-flex items-center justify-center px-4 py-2 rounded-lg bg-[#07C160] text-white text-sm wrapped-label hover:bg-[#06AD56] disabled:opacity-60 disabled:cursor-not-allowed transition controls-btn"
|
||
:disabled="loading"
|
||
@click="$emit('reload')"
|
||
>
|
||
<span v-if="!loading">Generate</span>
|
||
<span v-else>Loading...</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="accountsLoading" class="wrapped-body text-xs text-[#7F7F7F] controls-hint">
|
||
{{ showAccount ? '正在加载账号列表...' : '正在检查数据...' }}
|
||
</div>
|
||
<div v-else-if="accounts.length === 0" class="wrapped-body text-xs text-[#B37800] controls-warning">
|
||
{{ showAccount ? '未发现已解密账号(请先解密数据库)。' : '未发现可用数据(请先解密数据库)。' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
const props = defineProps({
|
||
accounts: { type: Array, default: () => [] },
|
||
accountsLoading: { type: Boolean, default: false },
|
||
loading: { type: Boolean, default: false },
|
||
modelYear: { type: Number, required: true },
|
||
modelAccount: { type: String, default: '' },
|
||
modelRefresh: { type: Boolean, default: false },
|
||
showAccount: { type: Boolean, default: true }
|
||
})
|
||
|
||
defineEmits(['update:year', 'update:account', 'update:refresh', 'reload'])
|
||
|
||
const yearOptions = computed(() => {
|
||
const now = new Date().getFullYear()
|
||
const years = []
|
||
for (let i = 0; i < 8; i++) years.push(now - i)
|
||
// Ensure selected year is present
|
||
if (props.modelYear && !years.includes(props.modelYear)) years.unshift(props.modelYear)
|
||
return years
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 复古模式 - 控制面板样式 */
|
||
.wrapped-retro .controls-panel {
|
||
background-color: var(--wrapped-card-bg);
|
||
border-color: var(--wrapped-border);
|
||
}
|
||
|
||
.wrapped-retro .controls-label {
|
||
color: var(--wrapped-text-secondary);
|
||
}
|
||
|
||
.wrapped-retro .controls-select {
|
||
background-color: var(--wrapped-bg);
|
||
border-color: var(--wrapped-border);
|
||
color: var(--wrapped-text);
|
||
}
|
||
|
||
.wrapped-retro .controls-select:focus {
|
||
--tw-ring-color: var(--wrapped-accent);
|
||
}
|
||
|
||
.wrapped-retro .controls-checkbox {
|
||
border-color: var(--wrapped-border);
|
||
color: var(--wrapped-accent);
|
||
}
|
||
|
||
.wrapped-retro .controls-checkbox:focus {
|
||
--tw-ring-color: var(--wrapped-accent);
|
||
}
|
||
|
||
.wrapped-retro .controls-hint {
|
||
color: var(--wrapped-text-secondary);
|
||
}
|
||
|
||
.wrapped-retro .controls-warning {
|
||
color: var(--wrapped-warning);
|
||
}
|
||
|
||
.wrapped-retro .controls-btn {
|
||
background-color: var(--wrapped-accent);
|
||
color: var(--wrapped-bg);
|
||
}
|
||
|
||
.wrapped-retro .controls-btn:hover:not(:disabled) {
|
||
filter: brightness(1.1);
|
||
}
|
||
|
||
/* DOS 特殊样式 */
|
||
.wrapped-theme-dos .controls-panel {
|
||
border-radius: 0;
|
||
border: 2px solid #33ff33;
|
||
box-shadow: 0 0 10px rgba(51, 255, 51, 0.3);
|
||
}
|
||
|
||
.wrapped-theme-dos .controls-select {
|
||
border-radius: 0;
|
||
border: 1px solid #33ff33;
|
||
text-shadow: 0 0 5px #33ff33;
|
||
}
|
||
|
||
.wrapped-theme-dos .controls-btn {
|
||
border-radius: 0;
|
||
background-color: #33ff33;
|
||
color: #000000;
|
||
text-shadow: none;
|
||
}
|
||
|
||
.wrapped-theme-dos .controls-btn:hover:not(:disabled) {
|
||
background-color: #44ff44;
|
||
}
|
||
|
||
/* VHS 特殊样式 */
|
||
.wrapped-theme-vhs .controls-panel {
|
||
border-radius: 4px;
|
||
background: linear-gradient(180deg, #16213e 0%, #1a1a2e 100%);
|
||
border-color: #0f3460;
|
||
}
|
||
|
||
.wrapped-theme-vhs .controls-select {
|
||
border-radius: 2px;
|
||
background: #1a1a2e;
|
||
border-color: #0f3460;
|
||
color: #eaeaea;
|
||
}
|
||
|
||
.wrapped-theme-vhs .controls-btn {
|
||
border-radius: 4px;
|
||
background: linear-gradient(135deg, #e94560 0%, #0f3460 100%);
|
||
color: #ffffff;
|
||
}
|
||
|
||
.wrapped-theme-vhs .controls-btn:hover:not(:disabled) {
|
||
box-shadow: 0 4px 15px rgba(233, 69, 96, 0.5);
|
||
}
|
||
</style>
|