Files
short-drama-agent-front/src/components/ui/ThemeToggle.vue
T

45 lines
1.7 KiB
Vue

<script setup lang="ts">
import { computed, h, ref } from 'vue'
import { NButton, NDropdown } from 'naive-ui'
import { Monitor, Moon, Sun } from '@lucide/vue'
import type { ThemePreference } from '../../composables/useTheme'
/** 图标表示用户偏好;保留系统跟随选项,不以简单二态切换覆盖偏好。 */
const preference = defineModel<ThemePreference>({ required: true })
const open = ref(false)
const modes = [
{ key: 'light', label: '浅色模式', icon: Sun },
{ key: 'dark', label: '暗黑模式', icon: Moon },
{ key: 'system', label: '跟随系统', icon: Monitor }
] as const
const current = computed(() => modes.find(mode => mode.key === preference.value) ?? modes[2])
const options = modes.map(mode => ({ ...mode, icon: () => h(mode.icon, { size: 16 }) }))
/** Dropdown 的键类型较宽,只接收受支持的主题值。 */
function selectTheme(key: string | number) {
if (key === 'light' || key === 'dark' || key === 'system') preference.value = key
}
</script>
<template>
<NDropdown v-model:show="open" trigger="click" :value="preference" :options="options" @select="selectTheme">
<NButton
quaternary
class="theme-toggle icon-button"
:aria-label="`主题模式:${current.label}`"
aria-haspopup="menu"
:aria-expanded="open"
>
<template #icon><component :is="current.icon" :size="18" aria-hidden="true" /></template>
</NButton>
</NDropdown>
</template>
<style>
/* 本组件专属布局与 Naive 内部结构覆盖。 */
@reference "../../styles/styles.css";
.n-button.theme-toggle {
@apply shrink-0 w-[34px] h-[34px];
}
</style>