From 08d60d9dad5a126755a19a1ffc3301e408145d1b Mon Sep 17 00:00:00 2001 From: estel <690930@qq.com> Date: Thu, 24 Jul 2025 23:09:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=A7=8B=E4=B8=BB=E9=A2=98=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/app.config.ts | 2 +- app/components/AppHeader.vue | 121 +++++++------- app/components/ThemeSettings.vue | 223 +++++++++++++++++++++++++ app/composables/useTheme.ts | 274 +++++++++++++++++++++++++++++++ app/layouts/default.vue | 15 +- app/pages/login.vue | 96 +++++++++++ app/pages/register.vue | 117 +++++++++++++ 7 files changed, 785 insertions(+), 63 deletions(-) create mode 100644 app/components/ThemeSettings.vue create mode 100644 app/composables/useTheme.ts create mode 100644 app/pages/login.vue create mode 100644 app/pages/register.vue diff --git a/app/app.config.ts b/app/app.config.ts index 4a9aa2c..3a93719 100644 --- a/app/app.config.ts +++ b/app/app.config.ts @@ -17,7 +17,7 @@ export default defineAppConfig({ siteName: 'Nuxt Docs Template' }, header: { - title: '', + title: 'Estel Docs', to: '/', logo: { alt: '', diff --git a/app/components/AppHeader.vue b/app/components/AppHeader.vue index 9986b40..1501534 100644 --- a/app/components/AppHeader.vue +++ b/app/components/AppHeader.vue @@ -1,65 +1,72 @@ - - - - + + + + + + + + + + + + + + + + + + + + + 登录 + + + 注册 + + + + + 登录 + + + 注册 + + + + + + + - - - {{ header.title }} - + - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/app/components/ThemeSettings.vue b/app/components/ThemeSettings.vue new file mode 100644 index 0000000..d40e9ad --- /dev/null +++ b/app/components/ThemeSettings.vue @@ -0,0 +1,223 @@ + + + + + + + + 页面设置 + + + + + + + + + 主题 + + + + + + + + 字体 + + + + + + + + 字号 + + + + + + + + 主题色 + + + + + + {{ color.label }} + + + + + + + 自定义主题色 + + + + + + + + + 代码块主题 + + + + + + 图注格式 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/composables/useTheme.ts b/app/composables/useTheme.ts new file mode 100644 index 0000000..aaf838b --- /dev/null +++ b/app/composables/useTheme.ts @@ -0,0 +1,274 @@ +/** + * @file app/composables/useTheme.ts + * @description + * + * `useTheme` 是一个全局的主题管理"管家"(Composable)。它采用单例模式,确保在整个应用中只有一份主题状态。 + * 这个模块负责: + * 1. **状态管理**:集中管理所有与主题相关的响应式状态,如主题模式(经典、优雅、简洁)、主题色、字体、字号等。 + * 2. **持久化**:将用户的设置保存到 localStorage,以便在下次访问时恢复。 + * 3. **应用样式**:动态地将主题设置应用到 `` 根元素上,通过 CSS 变量和类名来控制全局样式。 + * 4. **提供接口**:向外暴露所有状态和方法,方便任何组件读取和修改主题设置。 + * + * --- + * + * @usage + * + * **自动导入**: + * 由于这个文件位于 `composables/` 目录下,Nuxt 会自动导入 `useTheme` 函数。 + * 你可以在任何 `.vue` 组件的 ` + * + * + * + * 当前主题: {{ selectedTheme }} + * 重置设置 + * + * + * ``` + */ +import { ref, watch } from 'vue'; + +// 定义各种选项 +const themes = [ + { label: '经典', value: 'classic' }, + { label: '优雅', value: 'elegant' }, + { label: '简洁', value: 'minimal' } +]; + +const fonts = [ + { label: '无衬线', value: 'sans-serif' }, + { label: '衬线', value: 'serif' }, + { label: '等宽', value: 'monospace' } +]; + +const fontSizes = [ + { label: '更小', value: 'xs' }, + { label: '稍小', value: 'sm' }, + { label: '推荐', value: 'base' }, + { label: '稍大', value: 'lg' }, + { label: '更大', value: 'xl' } +]; + +const themeColors = [ + { label: '经典蓝', value: 'blue', color: '#3B82F6' }, + { label: '翡翠绿', value: 'emerald', color: '#10B981' }, + { label: '活力橙', value: 'orange', color: '#F97316' }, + { label: '柠檬黄', value: 'yellow', color: '#EAB308' }, + { label: '薰衣紫', value: 'violet', color: '#8B5CF6' }, + { label: '天空蓝', value: 'sky', color: '#0EA5E9' }, + { label: '玫瑰金', value: 'rose', color: '#F43F5E' }, + { label: '橄榄绿', value: 'lime', color: '#84CC16' }, + { label: '石墨黑', value: 'gray', color: '#6B7280' }, + { label: '雾烟灰', value: 'slate', color: '#64748B' }, + { label: '樱花粉', value: 'pink', color: '#EC4899' } +]; + +const codeThemes = [ + { label: 'dark', value: 'dark' }, + { label: 'light', value: 'light' }, + { label: 'github-dark', value: 'github-dark' }, + { label: 'github-light', value: 'github-light' }, + { label: 'one-dark-pro', value: 'one-dark-pro' }, + { label: 'material-theme', value: 'material-theme' } +]; + +const captionFormats = [ + { label: 'title 优先', value: 'title' }, + { label: 'alt 优先', value: 'alt' }, + { label: '只显示 title', value: 'title-only' }, + { label: '只显示 alt', value: 'alt-only' } +]; + +// 默认设置 +const defaultSettings = { + theme: 'elegant', + font: 'sans-serif', + fontSize: 'base', + themeColor: 'blue', + codeTheme: 'dark', + captionFormat: 'none' +}; + +// 响应式状态 +const selectedTheme = ref(defaultSettings.theme); +const selectedFont = ref(defaultSettings.font); +const selectedFontSize = ref(defaultSettings.fontSize); +const selectedThemeColor = ref(defaultSettings.themeColor); +const customColor = ref('#3B82F6'); +const selectedCodeTheme = ref(defaultSettings.codeTheme); +const selectedCaptionFormat = ref(defaultSettings.captionFormat); + +// 这是一个单例,确保在整个应用中只有一个主题状态实例 +let isInitialized = false; + +export function useTheme() { + const colorMode = useColorMode(); + + const initializeTheme = () => { + if (import.meta.client && !isInitialized) { + selectedTheme.value = localStorage.getItem('app-theme') || defaultSettings.theme; + selectedFont.value = localStorage.getItem('app-font') || defaultSettings.font; + selectedFontSize.value = localStorage.getItem('app-font-size') || defaultSettings.fontSize; + selectedThemeColor.value = localStorage.getItem('app-theme-color') || defaultSettings.themeColor; + selectedCodeTheme.value = localStorage.getItem('app-code-theme') || defaultSettings.codeTheme; + selectedCaptionFormat.value = localStorage.getItem('app-caption-format') || defaultSettings.captionFormat; + + // 找到自定义颜色对应的主题色值 + const initialColor = themeColors.find(c => c.value === selectedThemeColor.value)?.color || '#3B82F6'; + customColor.value = localStorage.getItem('app-custom-color') || initialColor; + + applyThemeVariables(); + isInitialized = true; + console.log('主题已初始化'); + } + }; + + const applyThemeVariables = () => { + if (import.meta.client) { + const root = document.documentElement; + + // 颜色映射到 @nuxt/ui 支持的颜色 + const colorMapping: { [key: string]: string } = { + 'blue': 'blue', + 'emerald': 'emerald', + 'orange': 'orange', + 'yellow': 'yellow', + 'violet': 'violet', + 'sky': 'sky', + 'rose': 'rose', + 'lime': 'lime', + 'gray': 'gray', + 'slate': 'slate', + 'pink': 'pink' + }; + + // 使用 @nuxt/ui 的主题系统 + if (selectedThemeColor.value !== 'custom') { + const mappedColor = colorMapping[selectedThemeColor.value]; + if (mappedColor) { + const appConfig = useAppConfig(); + if (!appConfig.ui.colors) appConfig.ui.colors = {}; + appConfig.ui.colors.primary = mappedColor; + } + } + + // 字体设置保持不变 + const fontMapping = { + 'sans-serif': 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif', + 'serif': 'ui-serif, Georgia, Cambria, "Times New Roman", Times, serif', + 'monospace': 'ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace' + }; + root.style.setProperty('--font-family', fontMapping[selectedFont.value as keyof typeof fontMapping]); + + const fontSizeMapping = { + 'xs': '0.75rem', 'sm': '0.875rem', 'base': '1rem', 'lg': '1.125rem', 'xl': '1.25rem' + }; + root.style.setProperty('--font-size-base', fontSizeMapping[selectedFontSize.value as keyof typeof fontSizeMapping]); + + root.classList.remove('theme-classic', 'theme-elegant', 'theme-minimal'); + root.classList.add(`theme-${selectedTheme.value}`); + } + }; + + /** + * 应用自定义主色调 + * + * Nuxt UI v3 不支持运行时动态自定义颜色,因此我们使用 CSS 变量覆盖的方法: + * 1. 保持 Nuxt UI 使用有效的颜色系统(这里使用 'blue' 作为基础) + * 2. 通过 CSS 变量 --ui-primary 覆盖实际显示的颜色 + * 3. 这样既保证了 Nuxt UI 系统正常工作,又实现了自定义颜色功能 + */ + const applyCustomColor = () => { + if (import.meta.client) { + selectedThemeColor.value = 'custom'; + const root = document.documentElement; + + // 使用 Nuxt UI v3 推荐的 CSS 变量覆盖方法 + // 设置主色调的 CSS 变量,这会影响所有使用 primary 颜色的组件 + root.style.setProperty('--ui-primary', customColor.value); + + // 保持 Nuxt UI 使用有效的颜色系统 + const appConfig = useAppConfig(); + if (!appConfig.ui.colors) appConfig.ui.colors = {}; + appConfig.ui.colors.primary = 'blue'; // 使用有效的颜色名称作为基础 + } + }; + + const resetSettings = () => { + selectedTheme.value = defaultSettings.theme; + selectedFont.value = defaultSettings.font; + selectedFontSize.value = defaultSettings.fontSize; + selectedThemeColor.value = defaultSettings.themeColor; + const defaultColor = themeColors.find(c => c.value === defaultSettings.themeColor)?.color || '#3B82F6'; + customColor.value = defaultColor; + selectedCodeTheme.value = defaultSettings.codeTheme; + selectedCaptionFormat.value = defaultSettings.captionFormat; + + colorMode.preference = 'system'; + + applyThemeVariables(); + }; + + // 监听预设主题色的变化,并同步更新自定义颜色选择器的值 + watch(selectedThemeColor, (newThemeColor) => { + if (newThemeColor !== 'custom') { + const colorObject = themeColors.find(c => c.value === newThemeColor); + if (colorObject) { + customColor.value = colorObject.color; + //应用颜色 + applyCustomColor(); + } + } + }); + + watch([selectedTheme, selectedFont, selectedFontSize, selectedThemeColor, selectedCodeTheme, selectedCaptionFormat, customColor], () => { + applyThemeVariables(); + + if (import.meta.client) { + localStorage.setItem('app-theme', selectedTheme.value); + localStorage.setItem('app-font', selectedFont.value); + localStorage.setItem('app-font-size', selectedFontSize.value); + localStorage.setItem('app-theme-color', selectedThemeColor.value); + localStorage.setItem('app-code-theme', selectedCodeTheme.value); + localStorage.setItem('app-caption-format', selectedCaptionFormat.value); + if (selectedThemeColor.value === 'custom') { + localStorage.setItem('app-custom-color', customColor.value); + } + } + }); + + return { + initializeTheme, + themes, + fonts, + fontSizes, + themeColors, + codeThemes, + captionFormats, + selectedTheme, + selectedFont, + selectedFontSize, + selectedThemeColor, + customColor, + selectedCodeTheme, + selectedCaptionFormat, + applyCustomColor, + resetSettings, + colorMode + }; +} \ No newline at end of file diff --git a/app/layouts/default.vue b/app/layouts/default.vue index 5d009e1..7fdbfea 100644 --- a/app/layouts/default.vue +++ b/app/layouts/default.vue @@ -1,5 +1,14 @@ + + - + diff --git a/app/pages/login.vue b/app/pages/login.vue new file mode 100644 index 0000000..4ac1fa6 --- /dev/null +++ b/app/pages/login.vue @@ -0,0 +1,96 @@ + + + + + + 登录您的账户 + + + 或 + + 创建新账户 + + + + + + + 邮箱地址 + + + + 密码 + + + + + + + + + 记住我 + + + + + + 忘记密码? + + + + + + + + + + + + 登录 + + + + + + + + \ No newline at end of file diff --git a/app/pages/register.vue b/app/pages/register.vue new file mode 100644 index 0000000..eabe636 --- /dev/null +++ b/app/pages/register.vue @@ -0,0 +1,117 @@ + + + + + + 创建新账户 + + + 或 + + 登录现有账户 + + + + + + + 用户名 + + + + 邮箱地址 + + + + 密码 + + + + 确认密码 + + + + + + + + 我同意 + 服务条款 + 和 + 隐私政策 + + + + + + 创建账户 + + + + + + + + \ No newline at end of file
当前主题: {{ selectedTheme }}
+ 或 + + 创建新账户 + +
+ 或 + + 登录现有账户 + +