Files
aue/docs/superpowers/specs/2026-05-14-sidebar-navigation-design.md
2026-06-03 13:16:30 +08:00

25 KiB
Raw Blame History

侧边栏导航系统设计方案

项目名称: 制度文件管理学习AI智能体 - 前端优化
设计日期: 2026-05-14
版本: v1.0
状态: 已批准


1. 设计背景与目标

1.1 当前问题

现有系统采用顶部水平导航栏布局,存在以下问题:

  • 空间利用率低: 7 个功能模块占用顶部空间,在宽屏显示器上浪费水平空间
  • 可扩展性差: 新增功能模块会导致顶部导航拥挤
  • 视觉层级不清晰: 系统标题、导航、用户信息混在同一行,缺乏层次感
  • 移动端体验差: 水平导航在小屏幕上需要滚动或换行

1.2 设计目标

将现有的顶部 header 导航重构为左侧固定侧边栏系统,实现:

  1. 提升空间利用率: 垂直导航释放顶部和水平空间
  2. 增强可扩展性: 支持更多功能模块而不影响布局
  3. 改善视觉层级: 清晰分离品牌区、导航区、用户区
  4. 优化响应式体验: 桌面/平板/移动端均有最佳表现
  5. 保持一致性: 遵循已有的白色体系设计规范

2. 设计决策记录

2.1 关键选择

决策项 选择 理由
侧边栏位置 左侧 符合主流商业系统习惯VS Code、Notion、Slack
标题与用户信息位置 侧边栏内 整体感强,减少页面元素碎片化
默认宽度规格 展开时 200px / 收起时 56px 紧凑型设计,最大化主内容区空间
实现方案 方案 A经典固定侧边栏 实现简单、性能优、符合企业级应用标准

2.2 未采用的替代方案

  • 方案 B可拖拽调整宽度: 实现复杂度高,当前需求不需要此功能
  • 方案 C全响应式混合导航: 过度工程化,增加维护成本
  • 右侧边栏: 不符合用户阅读习惯(从左到右)

3. 整体布局架构

3.1 布局结构图

桌面端≥768px:
┌─────────────────────────────────────────────────────┐
│                                                     │
│  ┌──────────┬──────────────────────────────────┐    │
│  │          │                                  │    │
│  │  侧边栏   │         主内容区域               │    │
│  │ (200px)  │     (剩余所有空间)              │    │
│  │          │                                  │    │
│  │ [品牌区]  │   - ReadModule                  │    │
│  │ [导航项]  │   - ManageModule                │    │
│  │ [用户区]  │   - QAModule                    │    │
│  │          │   - ExamModule                   │    │
│  └──────────┴──────────────────────────────────┘    │
│                                                     │
└─────────────────────────────────────────────────────┘

收起状态56px:
┌────────┬───────────────────────────────────────────┐
│        │                                           │
│ 56px   │           主内容区域                       │
│        │       (宽度 = 视口宽度 - 56px)            │
│ 图标   │                                           │
│ 仅显示  │                                           │
└────────┴───────────────────────────────────────────┘

移动端(<768px:
┌──────────────────────────┐
│ ☰  制度文件管理学习AI智能体 │  ← 顶部栏固定48px高
├──────────────────────────┤
│                          │
│                          │
│     主内容区域            │  ← 全屏显示
│                          │
│                          │
└──────────────────────────┘

点击 ☰ 后Overlay模式:
┌────┬─────────────────────┐
│    │  ✕                  │
│ 侧 │                      │
│ 边 │   主内容区(暗化)    │
│ 栏 │                      │
│    │                      │
└────┴─────────────────────┘

3.2 技术实现要点

  • 布局方式: Flexbox外层容器 display: flex
  • 侧边栏定位: 固定定位或正常流(flex-shrink: 0
  • 主内容区: flex: 1; overflow: auto
  • 高度控制: 侧边栏 height: 100vh

4. 侧边栏内部组件设计

4.1 组件架构

AppSidebar.vue (主容器)
├── SidebarHeader.vue (品牌区)
│   ├── Logo/图标
│   ├── 系统名称
│   └── 折叠按钮
├── SidebarNav.vue (导航菜单)
│   └── SidebarNavItem.vue × N (单个导航项)
│       ├── 图标
│       ├── 文字标签
│       └── Tooltip (收起状态)
├── <div class="sidebar-spacer"> (弹性空间)
└── SidebarUser.vue (用户信息区)
    ├── 用户头像
    ├── 用户名 + 角色
    └── 操作按钮 (设置/退出)

4.2 品牌区设计 (SidebarHeader)

展开状态 (200px):

┌──────────────────┐
│  🤖  制度文件管理  │
│     学习AI智能体   │
│           [←]    │
└──────────────────┘

收起状态 (56px):

┌────┐
│ 🤖 │
│ [→] │
└────┘

组件接口:

<SidebarHeader 
  :collapsed="Boolean" 
  @toggle="Function" 
/>

样式规范:

.sidebar-header {
  height: var(--sidebar-header-height, 64px);
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-bottom: 1px solid var(--border-sidebar);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

.brand-icon {
  font-size: 28px;
  line-height: 1;
  flex-shrink: 0;
}

.brand-text {
  font-size: 14px;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
}

.collapse-btn {
  align-self: flex-end;
  margin-top: 8px;
  width: 24px;
  height: 24px;
  border: none;
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  transition: all var(--transition-fast);
}

.collapse-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

4.3 导航菜单设计 (SidebarNav)

导航项配置数据结构:

const navItems = [
  { key: 'read', label: '文件查看', icon: '📄', permission: 'read' },
  { key: 'manage', label: '文件管理', icon: '📁', permission: 'manage' },
  { key: 'qa', label: '知识问答', icon: '💬', permission: 'qa' },
  { key: 'exam', label: '考察训练', icon: '🔍', permission: 'exam' },
  { key: 'mind', label: '纲要学习', icon: '📋', permission: 'mind' },
  { key: 'dashboard', label: '综合看板', icon: '📊', permission: 'dashboard' },
  { key: 'permission', label: '权限管理', icon: '👥', permission: 'permission' }
]

视觉状态对比:

状态 展开时 (200px) 收起时 (56px)
默认 📄 文件查看 (灰色文字) 📄 (灰色图标)
悬停 浅灰背景 + 深色文字 浅灰背景 + Tooltip 显示 "文件查看"
激活 蓝色浅背景 + 蓝色粗体文字 + 左侧蓝色指示条 蓝色图标 + 左侧蓝色指示条

组件接口:

<SidebarNav 
  :items="Array" 
  :active-key="String"
  :collapsed="Boolean"
  @select="Function(key)"
/>

样式规范:

.sidebar-nav {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  height: var(--sidebar-nav-item-height, 40px);
  padding: 0 16px;
  margin: 2px 8px;
  border-radius: 6px;
  cursor: pointer;
  transition: all var(--transition-fast);
  position: relative;
  color: var(--text-secondary);
  background: transparent;
}

.nav-item:hover {
  background: var(--bg-sidebar-hover, #f5f7fa);
  color: var(--text-primary);
}

.nav-item.active {
  background: var(--bg-sidebar-active, #e6f4ff);
  color: var(--color-primary, #3b82f6);
  font-weight: 600;
}

.nav-item.active::before {
  content: '';
  position: absolute;
  left: -8px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 20px;
  background: var(--color-primary);
  border-radius: 2px;
}

.nav-icon {
  font-size: 20px;
  line-height: 1;
  flex-shrink: 0;
}

.nav-label {
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

Tooltip 实现(收起状态):

.nav-tooltip {
  position: absolute;
  left: calc(100% + 12px);
  top: 50%;
  transform: translateY(-50%) translateX(-4px);
  padding: 6px 12px;
  background: rgba(26, 26, 46, 0.92);
  color: #ffffff;
  font-size: 13px;
  border-radius: 6px;
  white-space: nowrap;
  z-index: 1000;
  pointer-events: none;
  opacity: 0;
  transition: all 200ms ease;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.nav-tooltip::before {
  content: '';
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  border: 6px solid transparent;
  border-right-color: rgba(26, 26, 46, 0.92);
}

.nav-item:hover .nav-tooltip {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

4.4 用户信息区设计 (SidebarUser)

展开状态:

┌──────────────────┐
│ ┌─┐              │
│ │张│ 系统管理员   │
│ └─┘ 角色:管理员  │
│   ⚙️ 设置  🚪退出 │
└──────────────────┘

收起状态:

┌────┐
│ 👤 │  ← hover: tooltip "系统管理员"
│ ⚙️ │
│ 🚪 │
└────┘

组件接口:

<SidebarUser 
  :collapsed="Boolean"
  :user-info="Object"
  :role-label="String"
  @logout="Function"
  @settings="Function"
/>

样式规范:

.sidebar-user {
  padding: 16px;
  border-top: 1px solid var(--border-sidebar);
  background: var(--bg-elevated);
}

.user-info {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.user-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--color-primary);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  flex-shrink: 0;
}

.user-details {
  overflow: hidden;
}

.user-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-role {
  font-size: 12px;
  color: var(--text-secondary);
}

.user-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

.action-btn {
  padding: 6px 12px;
  border: none;
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text-secondary);
  transition: all var(--transition-fast);
}

.action-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

5. 交互细节与动画效果

5.1 展开/收起切换机制

触发方式:

  1. 主要方式: 点击侧边栏顶部的折叠按钮(箭头图标)
  2. 辅助方式(可选): 键盘快捷键 Ctrl + B

状态管理逻辑:

// composables/useSidebar.js
import { ref, watch, onMounted, onUnmounted } from 'vue'

export function useSidebar() {
  const collapsed = ref(false)
  const isMobile = ref(false)
  const mobileOpen = ref(false)

  // 从 localStorage 恢复状态
  onMounted(() => {
    const savedState = localStorage.getItem('sidebar-collapsed')
    if (savedState !== null) {
      collapsed.value = savedState === 'true'
    }
    
    // 初始化移动端检测
    checkMobile()
    window.addEventListener('resize', debounce(checkMobile, 150))
  })

  // 监听变化并持久化
  watch(collapsed, (val) => {
    localStorage.setItem('sidebar-collapsed', String(val))
  }, { immediate: false })

  const checkMobile = () => {
    isMobile.value = window.innerWidth < 768
    if (isMobile.value && mobileOpen.value) {
      mobileOpen.value = false
    }
  }

  const toggleCollapse = () => {
    collapsed.value = !collapsed.value
  }

  const openMobile = () => {
    mobileOpen.value = true
  }

  const closeMobile = () => {
    mobileOpen.value = false
  }

  return {
    collapsed,
    isMobile,
    mobileOpen,
    toggleCollapse,
    openMobile,
    closeMobile
  }
}

动画时间线:

时间轴(展开 → 收起):
0ms      ──── 点击折叠按钮 ────→
         ↓
0-280ms  CSS transition: width 200px → 56px
         ↓
100ms    Vue transition: 文字 opacity 1 → 0 (提前消失)
         ↓
280ms    动画完成,进入完全收起状态

时间轴(收起 → 展开):
0ms      ──── 点击展开按钮 ────→
         ↓
0-280ms  CSS transition: width 56px → 200px
         ↓
180ms    Vue transition: 文字 opacity 0 → 1 (延迟出现)
         ↓
280ms    动画完成,进入完全展开状态

CSS 过渡定义:

.sidebar {
  width: var(--sidebar-width, 200px);
  transition: width var(--sidebar-transition-duration) var(--sidebar-transition-easing);
  will-change: width;
}

.sidebar.collapsed {
  width: var(--sidebar-collapsed-width, 56px);
}

/* 文字淡入淡出 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 200ms ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/* 主内容区适配 */
.main-content {
  margin-left: var(--sidebar-width, 200px);
  transition: margin-left var(--sidebar-transition-duration) var(--sidebar-transition-easing);
  will-change: margin-left;
}

.main-content.sidebar-collapsed {
  margin-left: var(--sidebar-collapsed-width, 56px);
}

5.2 移动端适配策略

断点定义:

设备类型 屏幕宽度 行为
桌面端 ≥ 1024px 完整侧边栏200px支持手动折叠
平板端 768px - 1023px 默认收起56px悬停自动展开
移动端 < 768px 隐藏侧边栏汉堡菜单按钮呼出overlay

移动端交互流程:

  1. 初始状态: 侧边栏隐藏,左上角显示 ☰ 按钮
  2. 打开侧边栏: 点击 ☰ → 侧边栏从左侧滑入 + 半透明遮罩层
  3. 使用导航: 点击导航项 → 切换模块 + 自动关闭侧边栏
  4. 关闭侧边栏:
    • 点击遮罩层
    • 点击 ✕ 关闭按钮
    • 在侧边栏内向右滑动(可选手势)

移动端样式实现:

@media (max-width: 767px) {
  .mobile-menu-btn {
    position: fixed;
    top: 12px;
    left: 12px;
    z-index: 1002;
    width: 40px;
    height: 40px;
    border: none;
    background: var(--bg-card);
    border-radius: 8px;
    box-shadow: var(--shadow-md);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
  }
  
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    z-index: 1001;
    transform: translateX(-100%);
    transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: var(--shadow-lg);
  }
  
  .sidebar.mobile-open {
    transform: translateX(0);
  }
  
  .mobile-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1000;
    opacity: 0;
    pointer-events: none;
    transition: opacity 300ms ease;
  }
  
  .mobile-overlay.active {
    opacity: 1;
    pointer-events: auto;
  }
  
  .main-content {
    margin-left: 0 !important;
  }
}

6. 性能优化策略

6.1 渲染性能优化

优化技术 应用场景 预期收益
GPU 加速动画 侧边栏宽度过渡、主内容区 margin 过渡 避免 layout thrashing
will-change 提示 .sidebar, .main-content 元素 提前创建合成层
事件防抖 resize 事件监听器 减少不必要的重计算
被动监听 touch 事件、scroll 事件 提升滚动流畅度
按需加载 MobileMenuButton 组件 减少首屏加载体积

6.2 内存优化

  • 避免内存泄漏: 在 onUnmounted 中移除事件监听器
  • 合理使用 ref/computed: 避免不必要的响应式依赖
  • 虚拟列表(未来扩展): 如果导航项超过 20 个,使用虚拟滚动

6.3 可访问性 (Accessibility)

  • 键盘导航: 支持 Tab 键切换导航项Enter 键激活
  • ARIA 标签: 为侧边栏添加 role="navigation"aria-label
  • 焦点管理: 打开/关闭侧边栏时正确管理焦点陷阱
  • 屏幕阅读器: 为图标添加 aria-label 或隐藏的文字标签
  • 颜色对比度: 确保所有文本满足 WCAG 2.1 AA 标准4.5:1

7. 文件结构与组件清单

7.1 新增文件

src/
├── components/
│   └── layout/
│       ├── AppSidebar.vue          # 侧边栏主组件(~200行
│       ├── SidebarHeader.vue       # 品牌区组件(~80行
│       ├── SidebarNav.vue          # 导航菜单容器(~60行
│       ├── SidebarNavItem.vue      # 单个导航项(~100行
│       ├── SidebarUser.vue         # 用户信息区(~120行
│       └── MobileMenuButton.vue    # 移动端汉堡按钮(~40行
│
├── composables/
│   └── useSidebar.js               # 侧边栏状态管理(~80行
│
└── utils/
    └── debounce.js                 # 防抖工具函数(~15行

7.2 修改文件

src/
├── views/
│   └── Home.vue                    # 重构:移除 header引入侧边栏布局
│
├── design-tokens.css               # 扩展:添加侧边栏相关 CSS 变量
│
└── style.css                       # 更新:可能需要微调全局样式

7.3 组件职责划分

组件 职责 Props Events
AppSidebar 侧边栏主容器,协调子组件 collapsed, navItems, activeKey, userInfo, isMobile, mobileOpen update:collapsed, navigate, logout, settings
SidebarHeader 显示品牌标识和折叠按钮 collapsed toggle
SidebarNav 渲染导航项列表 items[], activeKey, collapsed select(key)
SidebarNavItem 单个导航项的展示和交互 item{key,label,icon}, active, collapsed click
SidebarUser 显示用户信息和操作按钮 collapsed, userInfo{}, roleLabel logout, settings
MobileMenuButton 移动端的菜单触发按钮 click

8. 样式变量扩展

在现有的 src/design-tokens.css 中添加以下变量:

/* ========== 侧边栏系统 ========== */
:root {
  /* 尺寸规范 */
  --sidebar-width: 200px;
  --sidebar-collapsed-width: 56px;
  --sidebar-nav-item-height: 40px;
  --sidebar-header-height: 64px;
  --sidebar-user-height: auto;
  
  /* 颜色规范 */
  --bg-sidebar: #ffffff;
  --bg-sidebar-hover: #f5f7fa;
  --bg-sidebar-active: #e6f4ff;
  --border-sidebar: #e5e7eb;
  
  /* 动画规范 */
  --sidebar-transition-duration: 280ms;
  --sidebar-transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
  
  /* 响应式断点 */
  --breakpoint-mobile: 768px;
  --breakpoint-tablet: 1024px;
  
  /* 圆角规范 */
  --radius-nav-item: 6px;
  --radius-tooltip: 6px;
  
  /* 阴影规范 */
  --shadow-sidebar: 0 2px 8px rgba(0, 0, 0, 0.08);
  --shadow-mobile-overlay: 0 4px 16px rgba(0, 0, 0, 0.12);
}

9. 测试策略

9.1 单元测试

  • useSidebar composable:

    • 测试初始状态从 localStorage 正确恢复
    • 测试 toggleCollapse 函数切换状态
    • 测试移动端检测逻辑
    • 测试状态变更后正确保存到 localStorage
  • 各组件渲染测试:

    • 测试展开/收起状态下正确的 DOM 结构
    • 测试 props 正确传递给子组件
    • 测试 events 正确触发

9.2 集成测试

  • Home.vue 集成:
    • 测试侧边栏与主内容区的协同工作
    • 测试导航切换功能是否正常
    • 测试用户登出流程

9.3 视觉回归测试

  • 使用 Playwright 或 Cypress 进行截图对比
  • 测试不同断点下的布局表现
  • 验证动画流畅性

9.4 手动测试清单

  • 桌面端:侧边栏正常展开/收起
  • 桌面端:点击导航项正确切换模块
  • 桌面端:刷新页面后保持上次的状态
  • 平板端:默认收起,悬停显示 tooltip
  • 移动端:汉堡菜单按钮可见且可点击
  • 移动端:侧边栏以 overlay 模式滑出
  • 移动端:点击遮罩层可关闭侧边栏
  • 键盘导航Tab 键可在导航项间切换
  • 无障碍:屏幕阅读器可正确朗读导航项

10. 迁移计划与风险控制

10.1 分阶段实施

Phase 1: 基础设施搭建(预计 2 小时)

  • 创建组件目录结构
  • 实现 useSidebar composable
  • 扩展 design-tokens.css

Phase 2: 组件开发(预计 3 小时)

  • 实现 AppSidebar 及其子组件
  • 实现展开/收起动画
  • 实现 Tooltip 功能

Phase 3: 集成与适配(预计 2 小时)

  • 修改 Home.vue移除旧 header
  • 集成新的侧边栏布局
  • 实现移动端响应式

Phase 4: 测试与优化(预计 1 小时)

  • 手动测试所有场景
  • 性能优化
  • 修复边界情况 bug

总预计工时: ~8 小时

10.2 风险识别与缓解

风险 可能性 影响 缓解措施
与现有样式冲突 使用 scoped styles + BEM 命名
移动端兼容性问题 充分测试主流设备
性能问题(大量 DOM 操作) 使用 Vue 的 transition 组件
用户习惯改变导致困惑 提供引导提示(首次使用)
localStorage 不可用 极低 try-catch 包裹,降级处理

10.3 回滚方案

如果新版本出现严重问题,可以通过 Git 快速回滚到上一个稳定版本:

git revert <commit-hash>
# 或
git reset --hard <previous-stable-commit>

建议在合并前打 tag 以便快速回滚:

git tag -a v1.0-before-sidebar-refactor -m "Pre sidebar refactor"

11. 成功标准

11.1 功能完整性

  • 所有 7 个功能模块均可通过侧边栏访问
  • 展开/收起功能正常工作
  • 状态记忆功能正常localStorage
  • Tooltip 在收起状态下正确显示
  • 移动端 overlay 模式正常工作

11.2 性能指标

  • 展开/收起动画帧率 ≥ 60fps
  • 首屏加载时间增加 < 200ms
  • 内存占用增加 < 5MB
  • 无明显的布局抖动CLS < 0.1

11.3 用户体验指标

  • 用户可在 3 秒内理解如何使用侧边栏
  • 导航切换操作步骤 ≤ 2 步
  • 视觉风格与整体白色体系一致
  • 不同屏幕尺寸下均表现良好

12. 未来迭代方向(超出本次范围)

12.1 短期增强(可选)

  1. 拖拽调整宽度(方案 B 特性)
  2. 多级嵌套菜单(如果功能模块有子分类)
  3. 搜索框集成(在侧边栏顶部添加全局搜索)
  4. 通知徽章(在导航项右上角显示未读数量)

12.2 中期优化

  1. 主题定制(深色模式、自定义配色)
  2. 键盘快捷键完整支持
  3. 手势导航增强(边缘滑动、长按预览)
  4. 国际化i18n支持

12.3 长期规划

  1. 插件系统(允许第三方扩展侧边栏功能)
  2. AI 辅助导航(根据使用频率智能排序)
  3. 跨应用同步(多标签页间同步侧边栏状态)

附录 A: 参考资料


附录 B: 术语表

术语 定义
Sidebar 侧边栏,垂直排列的导航面板
Collapsed 收起状态,仅显示图标
Expanded 展开状态,显示图标+文字
Overlay 遮罩层,半透明背景覆盖主内容区
Tooltip 工具提示,鼠标悬停时显示的文字说明
Responsive 响应式,适应不同屏幕尺寸
localStorage 浏览器本地存储,用于持久化用户偏好

文档维护者: AI Assistant
最后更新: 2026-05-14
下次评审日期: 实施完成后