This commit is contained in:
2026-07-15 01:18:20 +08:00
parent c514a7aced
commit 29a5f456cb
27 changed files with 189 additions and 109 deletions
+71 -33
View File
@@ -7,12 +7,20 @@ import { Button } from '@/components/ui/button'
import { useAppStore, type Theme, type EffectType } from '@/stores/appStore'
import { useSearchStore } from '@/stores/searchStore'
import { invoke } from '@tauri-apps/api/core'
import { onMounted } from 'vue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
const appStore = useAppStore()
const searchStore = useSearchStore()
// 始终反映系统真实的深浅色偏好,用于“跟随系统”卡片色块
const systemDark = ref(window.matchMedia('(prefers-color-scheme: dark)').matches)
const systemMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleSystemMediaChange = (e: MediaQueryListEvent) => {
systemDark.value = e.matches
}
onMounted(() => {
systemMediaQuery.addEventListener('change', handleSystemMediaChange)
searchStore.registerAction('settings', 0, () => appStore.setTheme('light'))
searchStore.registerAction('settings', 1, () => appStore.setTheme('dark'))
searchStore.registerAction('settings', 2, () => appStore.setTheme('system'))
@@ -22,21 +30,50 @@ onMounted(() => {
searchStore.registerAction('settings', 6, () => appStore.toggleAutoStart())
})
onUnmounted(() => {
systemMediaQuery.removeEventListener('change', handleSystemMediaChange)
})
const themes: Array<{ id: Theme; name: string; color: string; icon: typeof Sun }> = [
{ id: 'light', name: '浅色模式', color: '#f8fafc', icon: Sun },
{ id: 'dark', name: '深色模式', color: '#1e293b', icon: Moon },
{ id: 'system', name: '跟随系统', color: '#64748b', icon: Monitor }
]
const effects: Array<{ id: EffectType; name: string; color: string; description: string }> = [
{ id: 'normal', name: '普通模式', color: '#ffffff', description: '标准背景效果' },
{ id: 'mica', name: 'Win 云母', color: '#e2e8f0', description: 'Windows 11 云母效果' },
{ id: 'acrylic', name: 'Win 亚克力', color: '#cbd5e1', description: 'Windows 11 亚克力效果' }
const effects: Array<{ id: EffectType; name: string; color: string; darkColor: string; description: string }> = [
{ id: 'normal', name: '普通模式', color: '#ffffff', darkColor: '#0f172a', description: '标准背景效果' },
{ id: 'mica', name: 'Win 云母', color: '#f1f5f9', darkColor: '#1e293b', description: 'Windows 11 云母效果' },
{ id: 'acrylic', name: 'Win 亚克力', color: '#cbd5e1', darkColor: '#18181b', description: '仅跟随系统主题可用' }
]
// 亚克力仅在"跟随系统"主题下能正确同步深浅色(无深浅枚举,依赖系统级主题广播刷新)
const isAcrylicDisabled = () => appStore.theme !== 'system'
// 应用当前是否为深色模式(响应式,随主题与系统偏好变化)
const isAppDark = computed(() => {
if (appStore.theme === 'dark') return true
if (appStore.theme === 'light') return false
return systemDark.value
})
// 色块阴影:浅色主题用黑色阴影;深色主题黑色阴影不可见,改用微弱亮色高光模拟凸起感
const blockShadow = computed(() =>
isAppDark.value
? '0 4px 10px rgba(255, 255, 255, 0.08)'
: '0 4px 10px rgba(0, 0, 0, 0.2)'
)
// 效果卡片色块预览色:根据当前深浅模式返回对应颜色,尽量接近实际材质效果
const getEffectColor = (effectId: EffectType) => {
const eff = effects.find(e => e.id === effectId)
if (!eff) return '#ffffff'
return isAppDark.value ? eff.darkColor : eff.color
}
const getThemeColor = (themeId: Theme) => {
if (themeId === 'system') {
return appStore.theme === 'dark' ? '#1e293b' : '#f8fafc'
// 始终使用系统真实的深浅色,而非应用当前主题
return systemDark.value ? '#1e293b' : '#f8fafc'
}
const theme = themes.find(t => t.id === themeId)
return theme?.color || '#f8fafc'
@@ -87,24 +124,27 @@ const quitApp = async () => {
:class="appStore.theme === theme.id ? 'border-primary shadow-md' : 'border-border hover:border-primary/50'"
@click="appStore.setTheme(theme.id)"
>
<div
<div
class="h-16 w-full flex items-center justify-center transition-all duration-300"
:style="{ backgroundColor: getThemeColor(theme.id) }"
:style="{
backgroundColor: getThemeColor(theme.id),
boxShadow: blockShadow
}"
>
<component
:is="theme.icon"
<component
:is="theme.icon"
class="h-8 w-8 transition-colors duration-300"
:class="theme.id === 'dark' || (theme.id === 'system' && appStore.theme === 'dark') ? 'text-white' : 'text-gray-800'"
:class="theme.id === 'dark' || (theme.id === 'system' && systemDark) ? 'text-white' : 'text-gray-800'"
/>
</div>
<div class="h-10 flex items-center justify-center bg-card">
<div class="h-10 flex items-center justify-center">
<span class="text-sm font-medium">{{ theme.name }}</span>
</div>
<div
v-if="appStore.theme === theme.id"
class="absolute top-2 right-2 w-5 h-5 bg-primary rounded-full flex items-center justify-center"
class="absolute top-2 right-2 w-5 h-5 bg-primary dark:bg-white rounded-full flex items-center justify-center"
>
<svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-3 h-3 text-white dark:text-slate-900" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
</svg>
</div>
@@ -125,34 +165,32 @@ const quitApp = async () => {
<button
v-for="effect in effects"
:key="effect.id"
class="group relative rounded-lg overflow-hidden border-2 transition-all duration-300 hover:shadow-lg"
:class="appStore.effect === effect.id ? 'border-primary shadow-md' : 'border-border hover:border-primary/50'"
:disabled="effect.id === 'acrylic' && isAcrylicDisabled()"
class="group relative rounded-lg overflow-hidden border-2 transition-all duration-300"
:class="[
appStore.effect === effect.id ? 'border-primary shadow-md' : 'border-border',
effect.id === 'acrylic' && isAcrylicDisabled()
? 'opacity-50 cursor-not-allowed'
: 'hover:shadow-lg hover:border-primary/50'
]"
@click="appStore.setEffect(effect.id)"
>
<div
class="h-16 w-full flex items-center justify-center transition-all duration-300"
:style="{
backgroundColor: effect.id === 'normal' ? '#ffffff' : effect.id === 'mica' ? '#e2e8f040' : '#cbd5e120',
backdropFilter: effect.id === 'mica' ? 'blur(12px)' : effect.id === 'acrylic' ? 'blur(20px)' : 'none'
<div
class="h-16 w-full transition-all duration-300"
:style="{
backgroundColor: getEffectColor(effect.id),
boxShadow: blockShadow
}"
>
<div
class="w-12 h-12 rounded-lg border border-border/50"
:style="{
backgroundColor: effect.color,
backdropFilter: effect.id === 'mica' ? 'blur(8px)' : effect.id === 'acrylic' ? 'blur(16px)' : 'none'
}"
></div>
</div>
<div class="h-14 flex flex-col items-center justify-center bg-card p-2">
></div>
<div class="h-14 flex flex-col items-center justify-center p-2">
<span class="text-sm font-medium">{{ effect.name }}</span>
<span class="text-xs text-muted-foreground">{{ effect.description }}</span>
</div>
<div
v-if="appStore.effect === effect.id"
class="absolute top-2 right-2 w-5 h-5 bg-primary rounded-full flex items-center justify-center"
class="absolute top-2 right-2 w-5 h-5 bg-primary dark:bg-white rounded-full flex items-center justify-center"
>
<svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg class="w-3 h-3 text-white dark:text-slate-900" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
</svg>
</div>