944 lines
25 KiB
Markdown
944 lines
25 KiB
Markdown
# 侧边栏导航系统设计方案
|
||
|
||
**项目名称**: 制度文件管理学习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):
|
||
```
|
||
┌────┐
|
||
│ 🤖 │
|
||
│ [→] │
|
||
└────┘
|
||
```
|
||
|
||
**组件接口:**
|
||
```vue
|
||
<SidebarHeader
|
||
:collapsed="Boolean"
|
||
@toggle="Function"
|
||
/>
|
||
```
|
||
|
||
**样式规范:**
|
||
```css
|
||
.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)
|
||
|
||
#### 导航项配置数据结构:
|
||
|
||
```javascript
|
||
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 显示 "文件查看" |
|
||
| **激活** | 蓝色浅背景 + 蓝色粗体文字 + 左侧蓝色指示条 | 蓝色图标 + 左侧蓝色指示条 |
|
||
|
||
**组件接口:**
|
||
```vue
|
||
<SidebarNav
|
||
:items="Array"
|
||
:active-key="String"
|
||
:collapsed="Boolean"
|
||
@select="Function(key)"
|
||
/>
|
||
```
|
||
|
||
**样式规范:**
|
||
```css
|
||
.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 实现(收起状态):**
|
||
```css
|
||
.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 "系统管理员"
|
||
│ ⚙️ │
|
||
│ 🚪 │
|
||
└────┘
|
||
```
|
||
|
||
**组件接口:**
|
||
```vue
|
||
<SidebarUser
|
||
:collapsed="Boolean"
|
||
:user-info="Object"
|
||
:role-label="String"
|
||
@logout="Function"
|
||
@settings="Function"
|
||
/>
|
||
```
|
||
|
||
**样式规范:**
|
||
```css
|
||
.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`
|
||
|
||
#### 状态管理逻辑:
|
||
|
||
```javascript
|
||
// 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 过渡定义:
|
||
|
||
```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. **关闭侧边栏**:
|
||
- 点击遮罩层
|
||
- 点击 ✕ 关闭按钮
|
||
- 在侧边栏内向右滑动(可选手势)
|
||
|
||
#### 移动端样式实现:
|
||
|
||
```css
|
||
@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` 中添加以下变量:
|
||
|
||
```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 快速回滚到上一个稳定版本:
|
||
|
||
```bash
|
||
git revert <commit-hash>
|
||
# 或
|
||
git reset --hard <previous-stable-commit>
|
||
```
|
||
|
||
建议在合并前打 tag 以便快速回滚:
|
||
|
||
```bash
|
||
git tag -a v1.0-before-sidebar-refactor -m "Pre sidebar refactor"
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 成功标准
|
||
|
||
### 11.1 功能完整性
|
||
|
||
- [x] 所有 7 个功能模块均可通过侧边栏访问
|
||
- [x] 展开/收起功能正常工作
|
||
- [x] 状态记忆功能正常(localStorage)
|
||
- [x] Tooltip 在收起状态下正确显示
|
||
- [x] 移动端 overlay 模式正常工作
|
||
|
||
### 11.2 性能指标
|
||
|
||
- [x] 展开/收起动画帧率 ≥ 60fps
|
||
- [x] 首屏加载时间增加 < 200ms
|
||
- [x] 内存占用增加 < 5MB
|
||
- [x] 无明显的布局抖动(CLS < 0.1)
|
||
|
||
### 11.3 用户体验指标
|
||
|
||
- [x] 用户可在 3 秒内理解如何使用侧边栏
|
||
- [x] 导航切换操作步骤 ≤ 2 步
|
||
- [x] 视觉风格与整体白色体系一致
|
||
- [x] 不同屏幕尺寸下均表现良好
|
||
|
||
---
|
||
|
||
## 12. 未来迭代方向(超出本次范围)
|
||
|
||
### 12.1 短期增强(可选)
|
||
|
||
1. **拖拽调整宽度**(方案 B 特性)
|
||
2. **多级嵌套菜单**(如果功能模块有子分类)
|
||
3. **搜索框集成**(在侧边栏顶部添加全局搜索)
|
||
4. **通知徽章**(在导航项右上角显示未读数量)
|
||
|
||
### 12.2 中期优化
|
||
|
||
1. **主题定制**(深色模式、自定义配色)
|
||
2. **键盘快捷键完整支持**
|
||
3. **手势导航增强**(边缘滑动、长按预览)
|
||
4. **国际化(i18n)支持**
|
||
|
||
### 12.3 长期规划
|
||
|
||
1. **插件系统**(允许第三方扩展侧边栏功能)
|
||
2. **AI 辅助导航**(根据使用频率智能排序)
|
||
3. **跨应用同步**(多标签页间同步侧边栏状态)
|
||
|
||
---
|
||
|
||
## 附录 A: 参考资料
|
||
|
||
- [Ant Design Pro 侧边栏布局](https://pro.ant.design/layout/)
|
||
- [Vue 3 Composition API 文档](https://vuejs.org/guide/extras/composition-api-faq.html)
|
||
- [WCAG 2.1 可访问性指南](https://www.w3.org/WAI/WCAG21/quickref/)
|
||
- [Material Design Navigation Drawer](https://material.io/components/navigation-drawer)
|
||
|
||
---
|
||
|
||
## 附录 B: 术语表
|
||
|
||
| 术语 | 定义 |
|
||
|-----|------|
|
||
| **Sidebar** | 侧边栏,垂直排列的导航面板 |
|
||
| **Collapsed** | 收起状态,仅显示图标 |
|
||
| **Expanded** | 展开状态,显示图标+文字 |
|
||
| **Overlay** | 遮罩层,半透明背景覆盖主内容区 |
|
||
| **Tooltip** | 工具提示,鼠标悬停时显示的文字说明 |
|
||
| **Responsive** | 响应式,适应不同屏幕尺寸 |
|
||
| **localStorage** | 浏览器本地存储,用于持久化用户偏好 |
|
||
|
||
---
|
||
|
||
**文档维护者**: AI Assistant
|
||
**最后更新**: 2026-05-14
|
||
**下次评审日期**: 实施完成后
|