前言

Cloud Mail 是一个基于 Cloudflare Workers 的邮箱服务项目,本文记录了对该项目的 UI 优化实践,包括主题系统升级、侧边栏改造和深色模式适配。

本文使用Minimax-2.7模型生成


一、主题系统升级

1.1 问题

原项目仅支持浅色/深色手动切换,无法跟随系统偏好,且状态管理混乱。

1.2 解决方案

新增 useTheme Composable

// src/composables/useTheme.js
export function useTheme() {
  const uiStore = useUiStore()
  let mediaQuery = null

  const applyTheme = () => {
    const isDark = uiStore.isDark
    document.documentElement.classList.toggle('dark', isDark)
  }

  onMounted(() => {
    mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
    uiStore.updateSystemTheme(mediaQuery.matches)
    mediaQuery.addEventListener('change', (e) => {
      uiStore.updateSystemTheme(e.matches)
    })
    applyTheme()
  })

  watch(() => uiStore.isDark, applyTheme)
}

重构状态管理

// src/store/ui.js
state: () => ({
  themeMode: 'system',  // 'light' | 'dark' | 'system'
  systemPrefersDark: window.matchMedia('(prefers-color-scheme: dark)').matches
}),
getters: {
  isDark: (state) => {
    if (state.themeMode === 'system') return state.systemPrefersDark
    return state.themeMode === 'dark'
  }
}

1.3 效果

  • 支持浅色、深色、跟随系统三种模式
  • 系统主题变化时实时响应
  • 用户偏好自动持久化

二、侧边栏优化

2.1 问题

原设计折叠后仅缩小为 60px 图标模式,仍占用空间影响布局。

2.2 解决方案

分离桌面端与移动端逻辑

// src/layout/index.vue
const asideClass = computed(() => {
  if (isMobile.value) {
    return uiStore.asideShow ? 'aside-drawer-open' : 'aside-drawer-hide'
  }
  return uiStore.asideCollapsed ? 'aside-collapsed' : 'aside-expanded'
})

使用 Transform 动画替代 Width 动画

.aside-collapsed {
  transform: translateX(-100%);
  width: 0 !important;
}

.aside-expanded {
  transform: translateX(0);
  width: 260px;
  transition: transform 150ms ease;
}

2.3 效果

对比项优化前优化后
折叠状态60px 图标残留完全隐藏
动画方式width 变化transform GPU 加速
动画时长200ms150ms

三、CSS 变量化改造

3.1 问题

硬编码颜色散落在各处,主题切换时无法统一响应。

3.2 解决方案

定义完整的主题变量系统

:root {
  --aside-background: #ffffff;
  --aside-menu-text: #1f1f1f;
  --aside-hover-bg: #f0f2f5;
  --aside-active-bg: rgba(24, 144, 255, 0.1);
}

.dark {
  --aside-background: #141414;
  --aside-menu-text: #ffffff;
  --aside-hover-bg: #2a2a2a;
  --aside-active-bg: rgba(255, 255, 255, 0.08);
}

组件中使用变量

<el-menu :background-color="menuBgColor">
<!--
  menuBgColor = computed(() => 'var(--aside-background)')
-->

3.3 效果

  • 全部颜色值通过变量统一管理
  • 主题切换时自动响应
  • 代码可维护性大幅提升

四、深色模式邮件内容适配

4.1 问题

邮件正文使用 Shadow DOM 渲染,原有样式硬编码白色背景,无法适配深色模式。

4.2 解决方案

传递 dark prop 动态设置样式

// src/components/shadow-html/index.vue
const props = defineProps({
  html: { type: String, required: true },
  dark: { type: Boolean, default: false }
})

function updateContent() {
  const bgColor = props.dark ? '#1a1a1a' : '#FFFFFF'
  const textColor = props.dark ? '#e0e0e0' : '#13181D'
  const linkColor = props.dark ? '#6db3f2' : '#0E70DF'
  // ... 动态生成样式
}

父组件传递状态

<ShadowHtml :html="content" :dark="uiStore.isDark" />

4.3 深色配色方案

元素浅色深色
背景#FFFFFF#1a1a1a
文字#13181D#e0e0e0
链接#0E70DF#6db3f2

五、修改文件清单

文件修改类型说明
src/store/ui.js重构新增 themeMode 状态和 isDark getter
src/composables/useTheme.js新增统一管理主题逻辑
src/App.vue修改引入 useTheme
src/layout/index.vue修改侧边栏折叠逻辑优化
src/layout/aside/index.vue修改CSS 变量化
src/layout/header/index.vue修改主题选择下拉菜单
src/style.css修改主题变量定义、卡片样式
src/components/shadow-html/index.vue修改深色模式支持
src/views/content/index.vue修改传递 dark prop
src/i18n/zh.js修改新增主题相关翻译
src/i18n/en.js修改新增主题相关翻译

六、总结

本次优化实现了:

  1. 主题系统升级 - 三种模式 + 系统跟随 + 持久化
  2. 侧边栏改造 - 完全隐藏 + GPU 加速动画
  3. CSS 架构优化 - 变量化 + 统一管理
  4. 深色模式完善 - Shadow DOM 内容适配

所有修改均保持了原有功能不变,仅涉及 UI 层。