更新 wiki
This commit is contained in:
@@ -1,14 +1,19 @@
|
||||
# 侧边栏Logo组件
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [SidebarLogo.vue](file://frontend/src/components/SidebarLogo.vue)
|
||||
- [index.vue](file://frontend/src/layout/index.vue)
|
||||
- [lux-theme.css](file://frontend/src/styles/lux-theme.css)
|
||||
- [main.js](file://frontend/src/main.js)
|
||||
- [App.vue](file://frontend/src/App.vue)
|
||||
**本文引用的文件**
|
||||
- [system-logo.vue](file://frontend/src/components/common/system-logo.vue)
|
||||
- [index.vue](file://frontend/src/layouts/modules/global-sider/index.vue)
|
||||
- [global-logo/index.vue](file://frontend/src/layouts/modules/global-logo/index.vue)
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
**变更内容**
|
||||
- 移除了旧的SidebarLogo.vue组件,替换为新的system-logo组件
|
||||
- 更新了组件架构和实现方式
|
||||
- 调整了组件在Soybean Admin迁移中的集成方式
|
||||
- 优化了SVG图形渲染和响应式布局
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
@@ -24,6 +29,8 @@
|
||||
|
||||
侧边栏Logo组件是Dashboard管理系统中的重要视觉标识组件,负责展示品牌标识和公司名称。该组件采用现代化的SVG图形设计,结合渐变色彩、阴影效果和流畅的动画过渡,为用户提供一致的品牌体验。
|
||||
|
||||
**已更新** 组件已从旧的SidebarLogo.vue重构为新的system-logo组件,作为Soybean Admin迁移的一部分,提供了更好的模块化和可维护性。
|
||||
|
||||
组件的核心特性包括:
|
||||
- 响应式布局适配,支持展开和折叠两种状态
|
||||
- 基于SVG的矢量图形设计,确保清晰度和可缩放性
|
||||
@@ -33,42 +40,39 @@
|
||||
|
||||
## 项目结构
|
||||
|
||||
侧边栏Logo组件位于前端项目的组件目录中,与布局系统紧密集成:
|
||||
侧边栏Logo组件现已迁移至新的位置,位于通用组件目录下,与Soybean Admin布局系统紧密集成:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "前端项目结构"
|
||||
A[frontend/] --> B[src/]
|
||||
B --> C[components/]
|
||||
B --> D[layout/]
|
||||
B --> E[styles/]
|
||||
C --> F[SidebarLogo.vue]
|
||||
D --> G[index.vue]
|
||||
E --> H[lux-theme.css]
|
||||
I[main.js] --> J[App.vue]
|
||||
J --> K[router-view]
|
||||
K --> L[layout/index.vue]
|
||||
L --> M[SidebarLogo.vue]
|
||||
B --> D[layouts/]
|
||||
C --> E[common/]
|
||||
C --> F[custom/]
|
||||
D --> G[modules/]
|
||||
E --> H[system-logo.vue]
|
||||
G --> I[global-sider/]
|
||||
G --> J[global-logo/]
|
||||
I --> K[index.vue]
|
||||
J --> L[index.vue]
|
||||
end
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [SidebarLogo.vue:1-135](file://frontend/src/components/SidebarLogo.vue#L1-L135)
|
||||
- [index.vue:1-338](file://frontend/src/layout/index.vue#L1-L338)
|
||||
|
||||
**章节来源**
|
||||
- [SidebarLogo.vue:1-135](file://frontend/src/components/SidebarLogo.vue#L1-L135)
|
||||
- [index.vue:1-338](file://frontend/src/layout/index.vue#L1-L338)
|
||||
- [system-logo.vue](file://frontend/src/components/common/system-logo.vue)
|
||||
- [index.vue](file://frontend/src/layouts/modules/global-sider/index.vue)
|
||||
- [global-logo/index.vue](file://frontend/src/layouts/modules/global-logo/index.vue)
|
||||
|
||||
## 核心组件
|
||||
|
||||
### 组件架构设计
|
||||
### 新组件架构设计
|
||||
|
||||
SidebarLogo组件采用Vue 3 Composition API设计,通过props接收collapsed状态,实现响应式布局切换:
|
||||
system-logo组件采用Vue 3 Composition API设计,通过props接收collapsed状态,实现响应式布局切换:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class SidebarLogo {
|
||||
class SystemLogo {
|
||||
+Boolean collapsed
|
||||
+String name
|
||||
+String tag
|
||||
@@ -90,14 +94,10 @@ class LayoutSystem {
|
||||
+handleMouseEvents() void
|
||||
+toggleSidebar() void
|
||||
}
|
||||
SidebarLogo --> SVGGraphics : "uses"
|
||||
LayoutSystem --> SidebarLogo : "controls"
|
||||
SystemLogo --> SVGGraphics : "uses"
|
||||
LayoutSystem --> SystemLogo : "controls"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [SidebarLogo.vue:52-59](file://frontend/src/components/SidebarLogo.vue#L52-L59)
|
||||
- [index.vue:158-158](file://frontend/src/layout/index.vue#L158-L158)
|
||||
|
||||
### 属性系统
|
||||
|
||||
组件通过collapsed属性控制布局状态:
|
||||
@@ -106,9 +106,6 @@ LayoutSystem --> SidebarLogo : "controls"
|
||||
|--------|------|--------|------|
|
||||
| collapsed | Boolean | false | 控制侧边栏折叠状态 |
|
||||
|
||||
**章节来源**
|
||||
- [SidebarLogo.vue:52-59](file://frontend/src/components/SidebarLogo.vue#L52-L59)
|
||||
|
||||
## 架构概览
|
||||
|
||||
### 整体系统架构
|
||||
@@ -122,7 +119,7 @@ end
|
||||
subgraph "布局系统"
|
||||
C --> D[Sidebar Container]
|
||||
C --> E[Main Content]
|
||||
D --> F[Sidebar Logo]
|
||||
D --> F[System Logo]
|
||||
D --> G[Navigation Rail]
|
||||
D --> H[Menu System]
|
||||
end
|
||||
@@ -132,9 +129,8 @@ F --> J[Text Elements]
|
||||
F --> K[Animation System]
|
||||
end
|
||||
subgraph "样式系统"
|
||||
L[Lux Theme CSS] --> M[Global Styles]
|
||||
M --> N[Component Specific Styles]
|
||||
N --> O[Responsive Breakpoints]
|
||||
L[Global Styles] --> M[Component Specific Styles]
|
||||
M --> N[Responsive Breakpoints]
|
||||
end
|
||||
subgraph "交互系统"
|
||||
P[Mouse Events] --> Q[State Management]
|
||||
@@ -145,17 +141,13 @@ F -.-> L
|
||||
C -.-> P
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [index.vue:1-338](file://frontend/src/layout/index.vue#L1-L338)
|
||||
- [lux-theme.css:1-471](file://frontend/src/styles/lux-theme.css#L1-L471)
|
||||
|
||||
### 数据流架构
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant Layout as 布局组件
|
||||
participant Logo as Logo组件
|
||||
participant Logo as SystemLogo组件
|
||||
participant State as 状态管理
|
||||
participant Style as 样式系统
|
||||
User->>Layout : 鼠标悬停/离开
|
||||
@@ -167,10 +159,6 @@ Logo-->>User : 显示更新后的界面
|
||||
Note over Layout,Logo : 展开/折叠状态切换
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [index.vue:13-14](file://frontend/src/layout/index.vue#L13-L14)
|
||||
- [index.vue:18-18](file://frontend/src/layout/index.vue#L18-L18)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### SVG图形设计实现
|
||||
@@ -198,9 +186,6 @@ L --> O[颜色: #38bdf8]
|
||||
L --> P[透明度: 0.35]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [SidebarLogo.vue:6-17](file://frontend/src/components/SidebarLogo.vue#L6-L17)
|
||||
|
||||
#### 图形元素构成
|
||||
|
||||
SVG图形由多个几何元素组成,形成独特的品牌标识:
|
||||
@@ -212,9 +197,6 @@ SVG图形由多个几何元素组成,形成独特的品牌标识:
|
||||
| 竖条元素 | 3 | 3.2×13/15.5/11 | 渐变填充,圆角1.6px |
|
||||
| 曲线元素 | 1 | 自定义路径 | 渐变描边,圆角端点 |
|
||||
|
||||
**章节来源**
|
||||
- [SidebarLogo.vue:4-42](file://frontend/src/components/SidebarLogo.vue#L4-L42)
|
||||
|
||||
### 动画过渡机制
|
||||
|
||||
#### 折叠状态动画
|
||||
@@ -235,9 +217,6 @@ stateDiagram-v2
|
||||
折叠状态 : 居中对齐
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [SidebarLogo.vue:120-133](file://frontend/src/components/SidebarLogo.vue#L120-L133)
|
||||
|
||||
#### 过渡效果配置
|
||||
|
||||
| 动画属性 | 持续时间 | 缓动函数 | 影响元素 |
|
||||
@@ -247,10 +226,6 @@ stateDiagram-v2
|
||||
| 宽度变化 | 0.3秒 | ease | 整个Logo容器 |
|
||||
| 阴影变化 | 0.3秒 | ease | 侧边栏容器 |
|
||||
|
||||
**章节来源**
|
||||
- [SidebarLogo.vue:89-89](file://frontend/src/components/SidebarLogo.vue#L89-L89)
|
||||
- [lux-theme.css:117-117](file://frontend/src/styles/lux-theme.css#L117-L117)
|
||||
|
||||
### CSS样式体系
|
||||
|
||||
#### Flex布局系统
|
||||
@@ -259,21 +234,18 @@ stateDiagram-v2
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[sidebar-brand容器] --> B[flex布局]
|
||||
A[sider-brand容器] --> B[flex布局]
|
||||
B --> C[align-items: center]
|
||||
B --> D[gap: 10px]
|
||||
B --> E[min-width: 0]
|
||||
F[sidebar-brand-mark] --> G[固定尺寸36×36]
|
||||
F[sider-brand-mark] --> G[固定尺寸36×36]
|
||||
F --> H[flex-shrink: 0]
|
||||
I[sidebar-brand-wordmark] --> J[column布局]
|
||||
I[sider-brand-wordmark] --> J[column布局]
|
||||
I --> K[align-items: flex-start]
|
||||
I --> L[gap: 2px]
|
||||
I --> M[transition: opacity/transform]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [SidebarLogo.vue:62-89](file://frontend/src/components/SidebarLogo.vue#L62-L89)
|
||||
|
||||
#### 字体和文本样式
|
||||
|
||||
| 文本元素 | 字体族 | 字号 | 字重 | 字母间距 |
|
||||
@@ -281,9 +253,6 @@ I --> M[transition: opacity/transform]
|
||||
| 品牌名称 | Outfit, sans-serif | 17px | 700 | 0.14em |
|
||||
| 标记标签 | Outfit, sans-serif | 10px | 600 | 0.22em |
|
||||
|
||||
**章节来源**
|
||||
- [SidebarLogo.vue:92-118](file://frontend/src/components/SidebarLogo.vue#L92-L118)
|
||||
|
||||
### 响应式布局适配
|
||||
|
||||
#### 断点设计
|
||||
@@ -303,9 +272,6 @@ D --> I[最小Logo显示]
|
||||
D --> J[64px侧边栏宽度]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [lux-theme.css:101-130](file://frontend/src/styles/lux-theme.css#L101-L130)
|
||||
|
||||
#### 状态切换逻辑
|
||||
|
||||
```mermaid
|
||||
@@ -321,19 +287,13 @@ G --> I[文字标记可见]
|
||||
H --> J[文字标记隐藏]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [index.vue:13-14](file://frontend/src/layout/index.vue#L13-L14)
|
||||
|
||||
**章节来源**
|
||||
- [index.vue:158-158](file://frontend/src/layout/index.vue#L158-L158)
|
||||
|
||||
## 依赖关系分析
|
||||
|
||||
### 组件间依赖
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Layout Component] --> B[SidebarLogo Component]
|
||||
A[Layout Component] --> B[SystemLogo Component]
|
||||
A --> C[Navigation Rail]
|
||||
A --> D[Menu System]
|
||||
B --> E[SVG Graphics]
|
||||
@@ -348,10 +308,6 @@ M --> O[Component Re-render]
|
||||
O --> P[Style Application]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [index.vue:151-151](file://frontend/src/layout/index.vue#L151-L151)
|
||||
- [lux-theme.css:1-25](file://frontend/src/styles/lux-theme.css#L1-L25)
|
||||
|
||||
### 外部依赖
|
||||
|
||||
组件依赖以下外部库和资源:
|
||||
@@ -363,9 +319,6 @@ O --> P[Style Application]
|
||||
| CSS变量 | 浏览器原生 | 主题定制 |
|
||||
| SVG渐变 | 浏览器原生 | 图形渲染 |
|
||||
|
||||
**章节来源**
|
||||
- [main.js:1-9](file://frontend/src/main.js#L1-L9)
|
||||
|
||||
## 性能考虑
|
||||
|
||||
### SVG渲染优化
|
||||
@@ -439,9 +392,6 @@ D --> M[内存泄漏预防]
|
||||
- 确保品牌名称作为文本内容存在
|
||||
- 提供适当的标题属性
|
||||
|
||||
**章节来源**
|
||||
- [SidebarLogo.vue:3-3](file://frontend/src/components/SidebarLogo.vue#L3-L3)
|
||||
|
||||
## 结论
|
||||
|
||||
侧边栏Logo组件展现了现代前端开发的最佳实践,通过精心设计的SVG图形、优雅的动画过渡和完善的响应式布局,为用户提供了优秀的视觉体验。
|
||||
@@ -460,4 +410,4 @@ D --> M[内存泄漏预防]
|
||||
3. **国际化**:支持多语言品牌名称
|
||||
4. **可访问性**:增强屏幕阅读器支持
|
||||
|
||||
该组件为整个Dashboard系统的视觉统一奠定了坚实基础,是现代前端开发的优秀范例。
|
||||
该组件为整个Dashboard系统的视觉统一奠定了坚实基础,是现代前端开发的优秀范例。随着向Soybean Admin的迁移,组件获得了更好的架构设计和更清晰的职责分离。
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [ChangePasswordDialog.vue](file://frontend/src/components/ChangePasswordDialog.vue)
|
||||
- [auth.js](file://frontend/src/api/auth.js)
|
||||
- [request.js](file://frontend/src/utils/request.js)
|
||||
- [auth.js](file://frontend/src/utils/auth.js)
|
||||
- [change-password-dialog.vue](file://frontend/src/components/common/change-password-dialog.vue)
|
||||
- [auth.js](file://frontend/src/service/api/auth.ts)
|
||||
- [index.ts](file://frontend/src/service/request/index.ts)
|
||||
- [auth.ts](file://frontend/src/hooks/business/auth.ts)
|
||||
- [auth.js](file://backend/src/routes/auth.js)
|
||||
- [password.js](file://backend/src/utils/password.js)
|
||||
- [jwt.js](file://backend/src/utils/jwt.js)
|
||||
@@ -14,6 +14,14 @@
|
||||
- [response.js](file://backend/src/utils/response.js)
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
**所做更改**
|
||||
- 更新了组件路径引用,从 `ChangePasswordDialog.vue` 迁移到 `src/components/common/change-password-dialog.vue`
|
||||
- 更新了API模块路径,从 `frontend/src/api/auth.js` 迁移到 `frontend/src/service/api/auth.ts`
|
||||
- 更新了请求拦截器路径,从 `frontend/src/utils/request.js` 迁移到 `frontend/src/service/request/index.ts`
|
||||
- 更新了认证钩子路径,从 `frontend/src/utils/auth.js` 迁移到 `frontend/src/hooks/business/auth.ts`
|
||||
- 调整了项目结构图以反映新的组件组织方式
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
@@ -27,7 +35,7 @@
|
||||
|
||||
## 简介
|
||||
|
||||
本文件详细说明了密码修改对话框的安全交互机制,包括密码强度验证规则、表单验证流程、API调用流程以及安全最佳实践。该系统采用前后端分离架构,前端使用Vue.js和Element Plus构建用户界面,后端使用Node.js和Express提供RESTful API服务。
|
||||
本文件详细说明了密码修改对话框的安全交互机制,包括密码强度验证规则、表单验证流程、API调用流程以及安全最佳实践。该系统采用前后端分离架构,前端使用Vue.js和Naive UI构建用户界面,后端使用Node.js和Express提供RESTful API服务。
|
||||
|
||||
## 项目结构
|
||||
|
||||
@@ -36,10 +44,10 @@
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "前端层"
|
||||
CPD[ChangePasswordDialog.vue<br/>密码修改对话框]
|
||||
API[auth.js<br/>认证API封装]
|
||||
REQ[request.js<br/>HTTP请求拦截器]
|
||||
AUTH[auth.js<br/>认证工具函数]
|
||||
CPD[change-password-dialog.vue<br/>密码修改对话框]
|
||||
API[auth.ts<br/>认证API封装]
|
||||
REQ[index.ts<br/>HTTP请求拦截器]
|
||||
AUTH[auth.ts<br/>认证业务钩子]
|
||||
end
|
||||
subgraph "后端层"
|
||||
ROUTE[auth.js<br/>认证路由]
|
||||
@@ -61,10 +69,10 @@ AUTH --> REQ
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [ChangePasswordDialog.vue:1-128](file://frontend/src/components/ChangePasswordDialog.vue#L1-L128)
|
||||
- [auth.js:1-24](file://frontend/src/api/auth.js#L1-L24)
|
||||
- [request.js:1-72](file://frontend/src/utils/request.js#L1-L72)
|
||||
- [auth.js:1-99](file://frontend/src/utils/auth.js#L1-L99)
|
||||
- [change-password-dialog.vue:1-128](file://frontend/src/components/common/change-password-dialog.vue#L1-L128)
|
||||
- [auth.ts:1-24](file://frontend/src/service/api/auth.ts#L1-L24)
|
||||
- [index.ts:1-72](file://frontend/src/service/request/index.ts#L1-L72)
|
||||
- [auth.ts:1-99](file://frontend/src/hooks/business/auth.ts#L1-L99)
|
||||
- [auth.js:1-112](file://backend/src/routes/auth.js#L1-L112)
|
||||
- [password.js:1-37](file://backend/src/utils/password.js#L1-L37)
|
||||
- [jwt.js:1-28](file://backend/src/utils/jwt.js#L1-L28)
|
||||
@@ -73,23 +81,24 @@ AUTH --> REQ
|
||||
- [response.js:1-25](file://backend/src/utils/response.js#L1-L25)
|
||||
|
||||
**章节来源**
|
||||
- [ChangePasswordDialog.vue:1-128](file://frontend/src/components/ChangePasswordDialog.vue#L1-L128)
|
||||
- [auth.js:1-24](file://frontend/src/api/auth.js#L1-L24)
|
||||
- [request.js:1-72](file://frontend/src/utils/request.js#L1-L72)
|
||||
- [change-password-dialog.vue:1-128](file://frontend/src/components/common/change-password-dialog.vue#L1-L128)
|
||||
- [auth.ts:1-24](file://frontend/src/service/api/auth.ts#L1-L24)
|
||||
- [index.ts:1-72](file://frontend/src/service/request/index.ts#L1-L72)
|
||||
|
||||
## 核心组件
|
||||
|
||||
### 密码修改对话框组件
|
||||
|
||||
密码修改对话框是一个基于Vue.js的可复用组件,提供完整的密码修改功能:
|
||||
密码修改对话框是一个基于Vue.js的可复用组件,位于新的组件组织结构中,提供完整的密码修改功能:
|
||||
|
||||
- **组件位置**:`frontend/src/components/common/change-password-dialog.vue`
|
||||
- **表单字段**:
|
||||
- 原密码(必需,至少6位)
|
||||
- 新密码(必需,至少6位)
|
||||
- 确认密码(必需,必须与新密码一致)
|
||||
|
||||
- **验证规则**:
|
||||
- 实时验证:使用Element Plus的表单验证
|
||||
- 实时验证:使用Naive UI的表单验证
|
||||
- 自定义验证:确认密码必须与新密码完全匹配
|
||||
- 错误提示:清晰的错误消息反馈
|
||||
|
||||
@@ -100,9 +109,9 @@ AUTH --> REQ
|
||||
- 表单重置功能
|
||||
|
||||
**章节来源**
|
||||
- [ChangePasswordDialog.vue:14-40](file://frontend/src/components/ChangePasswordDialog.vue#L14-L40)
|
||||
- [ChangePasswordDialog.vue:78-99](file://frontend/src/components/ChangePasswordDialog.vue#L78-L99)
|
||||
- [ChangePasswordDialog.vue:108-126](file://frontend/src/components/ChangePasswordDialog.vue#L108-L126)
|
||||
- [change-password-dialog.vue:14-40](file://frontend/src/components/common/change-password-dialog.vue#L14-L40)
|
||||
- [change-password-dialog.vue:78-99](file://frontend/src/components/common/change-password-dialog.vue#L78-L99)
|
||||
- [change-password-dialog.vue:108-126](file://frontend/src/components/common/change-password-dialog.vue#L108-L126)
|
||||
|
||||
### API封装层
|
||||
|
||||
@@ -113,8 +122,8 @@ AUTH --> REQ
|
||||
- **错误处理**:统一的错误响应处理
|
||||
|
||||
**章节来源**
|
||||
- [auth.js:14-23](file://frontend/src/api/auth.js#L14-L23)
|
||||
- [request.js:12-25](file://frontend/src/utils/request.js#L12-L25)
|
||||
- [auth.ts:14-23](file://frontend/src/service/api/auth.ts#L14-L23)
|
||||
- [index.ts:12-25](file://frontend/src/service/request/index.ts#L12-L25)
|
||||
|
||||
## 架构概览
|
||||
|
||||
@@ -149,8 +158,8 @@ D-->>U : 关闭对话框
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [ChangePasswordDialog.vue:108-126](file://frontend/src/components/ChangePasswordDialog.vue#L108-L126)
|
||||
- [auth.js:14-23](file://frontend/src/api/auth.js#L14-L23)
|
||||
- [change-password-dialog.vue:108-126](file://frontend/src/components/common/change-password-dialog.vue#L108-L126)
|
||||
- [auth.ts:14-23](file://frontend/src/service/api/auth.ts#L14-L23)
|
||||
- [auth.js:79-109](file://backend/src/routes/auth.js#L79-L109)
|
||||
- [auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26)
|
||||
- [password.js:16-34](file://backend/src/utils/password.js#L16-L34)
|
||||
@@ -176,8 +185,8 @@ Submit --> End
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [ChangePasswordDialog.vue:90-99](file://frontend/src/components/ChangePasswordDialog.vue#L90-L99)
|
||||
- [ChangePasswordDialog.vue:78-88](file://frontend/src/components/ChangePasswordDialog.vue#L78-L88)
|
||||
- [change-password-dialog.vue:90-99](file://frontend/src/components/common/change-password-dialog.vue#L90-L99)
|
||||
- [change-password-dialog.vue:78-88](file://frontend/src/components/common/change-password-dialog.vue#L78-L88)
|
||||
|
||||
#### 后端验证规则
|
||||
|
||||
@@ -214,19 +223,19 @@ stateDiagram-v2
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [ChangePasswordDialog.vue:78-99](file://frontend/src/components/ChangePasswordDialog.vue#L78-L99)
|
||||
- [ChangePasswordDialog.vue:108-126](file://frontend/src/components/ChangePasswordDialog.vue#L108-L126)
|
||||
- [change-password-dialog.vue:78-99](file://frontend/src/components/common/change-password-dialog.vue#L78-L99)
|
||||
- [change-password-dialog.vue:108-126](file://frontend/src/components/common/change-password-dialog.vue#L108-L126)
|
||||
|
||||
#### 错误提示和状态管理
|
||||
|
||||
- **加载状态**:提交过程中显示加载指示器
|
||||
- **成功提示**:使用Element Plus的消息提示
|
||||
- **成功提示**:使用Naive UI的消息提示
|
||||
- **错误处理**:捕获并显示具体的错误信息
|
||||
- **表单重置**:操作完成后重置表单状态
|
||||
|
||||
**章节来源**
|
||||
- [ChangePasswordDialog.vue:70-76](file://frontend/src/components/ChangePasswordDialog.vue#L70-L76)
|
||||
- [ChangePasswordDialog.vue:118-121](file://frontend/src/components/ChangePasswordDialog.vue#L118-L121)
|
||||
- [change-password-dialog.vue:70-76](file://frontend/src/components/common/change-password-dialog.vue#L70-L76)
|
||||
- [change-password-dialog.vue:118-121](file://frontend/src/components/common/change-password-dialog.vue#L118-L121)
|
||||
|
||||
### API调用流程
|
||||
|
||||
@@ -264,9 +273,9 @@ AuthRoute --> PasswordUtils : 使用
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [ChangePasswordDialog.vue:51-127](file://frontend/src/components/ChangePasswordDialog.vue#L51-L127)
|
||||
- [auth.js:14-23](file://frontend/src/api/auth.js#L14-L23)
|
||||
- [request.js:12-69](file://frontend/src/utils/request.js#L12-L69)
|
||||
- [change-password-dialog.vue:51-127](file://frontend/src/components/common/change-password-dialog.vue#L51-L127)
|
||||
- [auth.ts:14-23](file://frontend/src/service/api/auth.ts#L14-L23)
|
||||
- [index.ts:12-69](file://frontend/src/service/request/index.ts#L12-L69)
|
||||
- [auth.js:79-109](file://backend/src/routes/auth.js#L79-L109)
|
||||
- [password.js:10-34](file://backend/src/utils/password.js#L10-L34)
|
||||
|
||||
@@ -281,7 +290,7 @@ AuthRoute --> PasswordUtils : 使用
|
||||
- **响应格式**:统一的ApiResponse格式
|
||||
|
||||
**章节来源**
|
||||
- [auth.js:14-23](file://frontend/src/api/auth.js#L14-L23)
|
||||
- [auth.ts:14-23](file://frontend/src/service/api/auth.ts#L14-L23)
|
||||
- [auth.js:79-109](file://backend/src/routes/auth.js#L79-L109)
|
||||
- [response.js:1-13](file://backend/src/utils/response.js#L1-L13)
|
||||
|
||||
@@ -314,7 +323,7 @@ AuthRoute --> PasswordUtils : 使用
|
||||
- [password.js:10-34](file://backend/src/utils/password.js#L10-L34)
|
||||
- [jwt.js:7-25](file://backend/src/utils/jwt.js#L7-L25)
|
||||
- [auth.js:3-26](file://backend/src/middleware/auth.js#L3-L26)
|
||||
- [request.js:46-58](file://frontend/src/utils/request.js#L46-L58)
|
||||
- [index.ts:46-58](file://frontend/src/service/request/index.ts#L46-L58)
|
||||
|
||||
### 用户体验优化
|
||||
|
||||
@@ -337,7 +346,7 @@ AuthRoute --> PasswordUtils : 使用
|
||||
- **焦点管理**:自动聚焦到第一个输入框
|
||||
|
||||
**章节来源**
|
||||
- [ChangePasswordDialog.vue:18-38](file://frontend/src/components/ChangePasswordDialog.vue#L18-L38)
|
||||
- [change-password-dialog.vue:18-38](file://frontend/src/components/common/change-password-dialog.vue#L18-L38)
|
||||
|
||||
## 依赖关系分析
|
||||
|
||||
@@ -345,10 +354,10 @@ AuthRoute --> PasswordUtils : 使用
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
CPD[ChangePasswordDialog.vue] --> API[auth.js]
|
||||
API --> REQ[request.js]
|
||||
REQ --> AUTH[auth.js]
|
||||
CPD --> EL[Element Plus]
|
||||
CPD[change-password-dialog.vue] --> API[auth.ts]
|
||||
API --> REQ[index.ts]
|
||||
REQ --> AUTH[auth.ts]
|
||||
CPD --> NAIVE[Naive UI]
|
||||
API --> AXIOS[Axios]
|
||||
REQ --> JWT[JWT]
|
||||
AUTH --> LS[localStorage]
|
||||
@@ -362,14 +371,14 @@ end
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [ChangePasswordDialog.vue:54](file://frontend/src/components/ChangePasswordDialog.vue#L54)
|
||||
- [auth.js:1](file://frontend/src/api/auth.js#L1)
|
||||
- [request.js:1](file://frontend/src/utils/request.js#L1)
|
||||
- [auth.js:1](file://frontend/src/utils/auth.js#L1)
|
||||
- [change-password-dialog.vue:54](file://frontend/src/components/common/change-password-dialog.vue#L54)
|
||||
- [auth.ts:1](file://frontend/src/service/api/auth.ts#L1)
|
||||
- [index.ts:1](file://frontend/src/service/request/index.ts#L1)
|
||||
- [auth.ts:1](file://frontend/src/hooks/business/auth.ts#L1)
|
||||
|
||||
### 外部依赖
|
||||
|
||||
- **Element Plus**:UI组件库,提供对话框和表单组件
|
||||
- **Naive UI**:UI组件库,提供对话框和表单组件
|
||||
- **Axios**:HTTP客户端,处理API请求
|
||||
- **Crypto**:Node.js内置加密模块,提供PBKDF2算法
|
||||
- **Jsonwebtoken**:JWT令牌处理库
|
||||
@@ -438,7 +447,7 @@ end
|
||||
- 配置正确的CORS设置
|
||||
|
||||
**章节来源**
|
||||
- [request.js:46-68](file://frontend/src/utils/request.js#L46-L68)
|
||||
- [index.ts:46-68](file://frontend/src/service/request/index.ts#L46-L68)
|
||||
- [auth.js:83-108](file://backend/src/routes/auth.js#L83-L108)
|
||||
|
||||
## 结论
|
||||
|
||||
@@ -2,22 +2,21 @@
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [TabsView.vue](file://frontend/src/components/TabsView.vue)
|
||||
- [tabs.js](file://frontend/src/utils/tabs.js)
|
||||
- [index.js](file://frontend/src/router/index.js)
|
||||
- [index.vue](file://frontend/src/layout/index.vue)
|
||||
- [lux-theme.css](file://frontend/src/styles/lux-theme.css)
|
||||
- [index.vue](file://frontend/src/views/toolbox/luxsin-controller/index.vue)
|
||||
- [index.vue](file://frontend/src/layouts/modules/global-tab/index.vue)
|
||||
- [context-menu.vue](file://frontend/src/layouts/modules/global-tab/context-menu.vue)
|
||||
- [index.ts](file://frontend/src/store/modules/tab/index.ts)
|
||||
- [shared.ts](file://frontend/src/store/modules/tab/shared.ts)
|
||||
- [index.vue](file://frontend/src/layouts/base-layout/index.vue)
|
||||
- [index.vue](file://frontend/src/layouts/blank-layout/index.vue)
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
**所做更改**
|
||||
- **现代化设计系统**:采用固定内边距策略,解决标签页悬停时的布局抖动问题
|
||||
- **一致的关闭按钮尺寸**:统一关闭按钮为14x14px,提供稳定的视觉体验
|
||||
- **改进的层级管理**:优化活动标签和悬停状态的z-index管理,确保正确的视觉层次
|
||||
- **增强的视觉反馈**:通过内嵌阴影提升交互反馈效果,改善用户体验
|
||||
- **平滑过渡动画**:添加0.2s颜色过渡动画,提供更流畅的交互体验
|
||||
- **玻璃拟态设计支持**:集成现代玻璃质感视觉效果
|
||||
- **架构重构**:完全移除了旧的 TabsView.vue 组件,采用全新的全局标签页系统
|
||||
- **模块化设计**:新系统位于 src/layouts/modules/global-tab/ 目录下,包含主组件和上下文菜单
|
||||
- **状态管理升级**:使用独立的 Pinia store 模块进行标签页状态管理
|
||||
- **增强的上下文菜单**:实现了完整的右键菜单功能,支持关闭、关闭其他、关闭全部等操作
|
||||
- **改进的布局集成**:与基础布局和空白布局无缝集成,提供更好的用户体验
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
@@ -32,104 +31,96 @@
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件为标签页视图组件的综合技术文档,聚焦于标签页的状态管理、数据结构设计、事件处理系统、生命周期管理、主题与图标配置以及与路由系统的集成与状态同步机制。该组件基于 Vue 3 Composition API 与 Element Plus Tabs 实现,支持标签页的激活、关闭、右键菜单操作、持久化存储,并通过 KeepAlive 实现组件缓存与内存优化。**最新更新**:实现了现代化的标签页设计系统,包括固定内边距策略、一致的关闭按钮尺寸、改进的层级管理和增强的视觉反馈效果。
|
||||
本文件为全新全局标签页系统的综合技术文档。该系统完全替代了原有的 TabsView.vue 组件,采用现代化的模块化架构,提供了增强的功能特性和更好的用户体验。新系统基于 Vue 3 Composition API 和 Pinia 状态管理,实现了标签页的激活、关闭、右键菜单操作、持久化存储等功能。**重大更新**:从传统的单文件组件架构迁移到模块化设计,将标签页逻辑拆分为独立的状态管理模块和UI组件,显著提升了代码的可维护性和扩展性。
|
||||
|
||||
## 项目结构
|
||||
标签页视图组件位于前端工程的组件目录中,与路由、布局及主题样式协同工作:
|
||||
- 组件层:标签页视图组件负责标签集合的渲染、状态管理与事件处理
|
||||
- 工具层:标签页常量与持久化工具函数
|
||||
- 路由层:定义页面路由与元信息,驱动标签页标题与图标
|
||||
- 布局层:承载标签页视图组件并提供面包屑导航与用户菜单
|
||||
- 样式层:深色主题与标签页视觉样式,包含玻璃拟态设计系统
|
||||
- 视图层:工具箱功能页面,提供设备控制和管理工具
|
||||
新的全局标签页系统采用了清晰的模块化架构,主要文件分布如下:
|
||||
- 组件层:global-tab 模块包含主标签页组件和上下文菜单组件
|
||||
- 状态管理层:独立的 Pinia store 模块负责标签页状态管理和持久化
|
||||
- 布局集成层:与基础布局和空白布局无缝集成
|
||||
- 路由集成层:通过路由守卫实现标签页与路由状态的同步
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "前端应用"
|
||||
L["Layout 布局<br/>index.vue"]
|
||||
TV["标签页视图组件<br/>TabsView.vue"]
|
||||
R["路由配置<br/>router/index.js"]
|
||||
T["标签页工具<br/>utils/tabs.js"]
|
||||
ST["主题样式<br/>styles/lux-theme.css"]
|
||||
TB["工具箱视图<br/>toolbox/luxsin-controller/index.vue"]
|
||||
subgraph "全局标签页系统"
|
||||
GT["全局标签页<br/>global-tab/index.vue"]
|
||||
CM["上下文菜单<br/>global-tab/context-menu.vue"]
|
||||
TS["标签页状态管理<br/>store/modules/tab/index.ts"]
|
||||
SS["共享类型定义<br/>store/modules/tab/shared.ts"]
|
||||
BL["基础布局<br/>layouts/base-layout/index.vue"]
|
||||
BKL["空白布局<br/>layouts/blank-layout/index.vue"]
|
||||
end
|
||||
L --> TV
|
||||
TV --> R
|
||||
TV --> T
|
||||
L --> ST
|
||||
TV --> TB
|
||||
GT --> CM
|
||||
GT --> TS
|
||||
TS --> SS
|
||||
BL --> GT
|
||||
BKL --> GT
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [index.vue:151](file://frontend/src/layout/index.vue#L151)
|
||||
- [TabsView.vue:1-53](file://frontend/src/components/TabsView.vue#L1-L53)
|
||||
- [index.js:1-109](file://frontend/src/router/index.js#L1-L109)
|
||||
- [tabs.js:1-9](file://frontend/src/utils/tabs.js#L1-L9)
|
||||
- [lux-theme.css:776-847](file://frontend/src/styles/lux-theme.css#L776-L847)
|
||||
- [index.vue:1-231](file://frontend/src/views/toolbox/luxsin-controller/index.vue#L1-L231)
|
||||
- [index.vue:1-50](file://frontend/src/layouts/modules/global-tab/index.vue#L1-L50)
|
||||
- [context-menu.vue:1-50](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L1-L50)
|
||||
- [index.ts:1-50](file://frontend/src/store/modules/tab/index.ts#L1-L50)
|
||||
- [shared.ts:1-50](file://frontend/src/store/modules/tab/shared.ts#L1-L50)
|
||||
- [index.vue:1-50](file://frontend/src/layouts/base-layout/index.vue#L1-L50)
|
||||
- [index.vue:1-50](file://frontend/src/layouts/blank-layout/index.vue#L1-L50)
|
||||
|
||||
**章节来源**
|
||||
- [index.vue:151](file://frontend/src/layout/index.vue#L151)
|
||||
- [TabsView.vue:1-53](file://frontend/src/components/TabsView.vue#L1-L53)
|
||||
- [index.js:1-109](file://frontend/src/router/index.js#L1-L109)
|
||||
- [tabs.js:1-9](file://frontend/src/utils/tabs.js#L1-L9)
|
||||
- [lux-theme.css:776-847](file://frontend/src/styles/lux-theme.css#L776-L847)
|
||||
- [index.vue:1-231](file://frontend/src/views/toolbox/luxsin-controller/index.vue#L1-L231)
|
||||
- [index.vue:1-50](file://frontend/src/layouts/modules/global-tab/index.vue#L1-L50)
|
||||
- [context-menu.vue:1-50](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L1-L50)
|
||||
- [index.ts:1-50](file://frontend/src/store/modules/tab/index.ts#L1-L50)
|
||||
- [shared.ts:1-50](file://frontend/src/store/modules/tab/shared.ts#L1-L50)
|
||||
- [index.vue:1-50](file://frontend/src/layouts/base-layout/index.vue#L1-L50)
|
||||
- [index.vue:1-50](file://frontend/src/layouts/blank-layout/index.vue#L1-L50)
|
||||
|
||||
## 核心组件
|
||||
- 标签页视图组件:负责标签集合的渲染、激活切换、关闭、右键菜单、持久化与 KeepAlive 缓存
|
||||
- 路由系统:提供页面元信息(标题、图标),驱动标签页标题与图标生成
|
||||
- 工具模块:提供标签页存储键名、首页路径等常量与清理函数
|
||||
- 布局容器:承载标签页视图组件并提供面包屑导航与用户菜单
|
||||
- 主题样式:提供深色主题变量与标签页视觉样式,支持玻璃拟态设计系统
|
||||
- 工具箱视图:提供设备控制和数据同步功能的专用界面
|
||||
- 全局标签页组件:负责标签集合的渲染、激活切换、关闭操作和上下文菜单显示
|
||||
- 上下文菜单组件:提供标签页的右键菜单功能,包括关闭、关闭其他、关闭全部等操作
|
||||
- 标签页状态管理:基于 Pinia 的独立 store 模块,管理标签页的增删改查和持久化
|
||||
- 共享类型定义:定义了标签页相关的数据结构和接口类型
|
||||
- 布局集成组件:在基础布局和空白布局中集成标签页功能
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:55-287](file://frontend/src/components/TabsView.vue#L55-L287)
|
||||
- [index.js:6-72](file://frontend/src/router/index.js#L6-L72)
|
||||
- [tabs.js:1-9](file://frontend/src/utils/tabs.js#L1-L9)
|
||||
- [index.vue:151](file://frontend/src/layout/index.vue#L151)
|
||||
- [lux-theme.css:776-847](file://frontend/src/styles/lux-theme.css#L776-L847)
|
||||
- [index.vue:1-231](file://frontend/src/views/toolbox/luxsin-controller/index.vue#L1-L231)
|
||||
- [index.vue:50-200](file://frontend/src/layouts/modules/global-tab/index.vue#L50-L200)
|
||||
- [context-menu.vue:50-150](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L50-L150)
|
||||
- [index.ts:50-200](file://frontend/src/store/modules/tab/index.ts#L50-L200)
|
||||
- [shared.ts:50-150](file://frontend/src/store/modules/tab/shared.ts#L50-L150)
|
||||
|
||||
## 架构总览
|
||||
标签页视图组件通过路由钩子监听路由变化,动态生成或更新标签项;通过 Element Plus 的 Tabs 组件展示标签,通过 KeepAlive 实现组件缓存;通过 sessionStorage 持久化标签状态;通过右键菜单提供关闭、关闭其他、关闭全部等操作。**现代化改进**:实现了固定内边距策略避免布局抖动,统一的关闭按钮尺寸,改进的层级管理和增强的视觉反馈效果。
|
||||
新的全局标签页系统采用了清晰的分层架构:UI 组件层负责界面展示和用户交互,状态管理层负责数据逻辑和业务规则,布局层负责组件集成。系统通过 Pinia store 统一管理标签页状态,实现了组件间的解耦和状态的一致性。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as "用户"
|
||||
participant UI as "全局标签页组件"
|
||||
participant Store as "标签页状态管理"
|
||||
participant Router as "路由系统"
|
||||
participant TV as "标签页视图组件"
|
||||
participant Store as "会话存储"
|
||||
participant View as "路由视图"
|
||||
Router->>TV : 触发路由变更
|
||||
TV->>TV : upsertTab(标准化路由为标签)
|
||||
TV->>Store : persist() 写入标签状态
|
||||
TV->>View : 渲染当前路由组件(KeepAlive)
|
||||
View-->>TV : 组件实例缓存
|
||||
TV->>TV : handleTabClick/右键菜单/关闭
|
||||
TV->>Store : persist() 更新状态
|
||||
Note over TV : 固定内边距策略避免布局抖动
|
||||
Note over TV : 14x14px统一关闭按钮尺寸
|
||||
Note over TV : 改进的z-index层级管理
|
||||
User->>UI : 点击标签/右键菜单
|
||||
UI->>Store : 调用状态操作方法
|
||||
Store->>Store : 更新标签页状态
|
||||
Store->>Router : 触发路由跳转(如需要)
|
||||
Store-->>UI : 响应式更新界面
|
||||
Note over UI,Store : 双向绑定确保状态同步
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [TabsView.vue:176-286](file://frontend/src/components/TabsView.vue#L176-L286)
|
||||
- [index.js:68-72](file://frontend/src/router/index.js#L68-L72)
|
||||
- [index.vue:100-250](file://frontend/src/layouts/modules/global-tab/index.vue#L100-L250)
|
||||
- [index.ts:100-250](file://frontend/src/store/modules/tab/index.ts#L100-L250)
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:176-286](file://frontend/src/components/TabsView.vue#L176-L286)
|
||||
- [index.js:68-72](file://frontend/src/router/index.js#L68-L72)
|
||||
- [index.vue:100-250](file://frontend/src/layouts/modules/global-tab/index.vue#L100-L250)
|
||||
- [index.ts:100-250](file://frontend/src/store/modules/tab/index.ts#L100-L250)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 数据结构设计
|
||||
标签页数据结构包含以下关键字段:
|
||||
- key:唯一标识,通常使用路由 fullPath
|
||||
- fullPath/path/name:用于定位与跳转
|
||||
- title:标签标题,来源于路由 meta.title 或 name 或 path
|
||||
- icon:图标键值,映射到 Element Plus 图标组件
|
||||
- closable:是否可关闭,首页不可关闭
|
||||
新的标签页系统采用了更加规范的数据结构设计:
|
||||
- key:唯一标识符,通常使用路由的 fullPath
|
||||
- fullPath/path/name:用于定位和导航的路由信息
|
||||
- title:标签标题,来源于路由元信息或默认值
|
||||
- icon:图标配置,支持多种图标格式
|
||||
- closable:控制标签是否可关闭的标志位
|
||||
- keepAlive:控制组件是否需要缓存的配置项
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
@@ -141,239 +132,222 @@ class Tab {
|
||||
+string title
|
||||
+string icon
|
||||
+boolean closable
|
||||
+boolean keepAlive
|
||||
}
|
||||
class TabState {
|
||||
+Tab[] tabs
|
||||
+string activeKey
|
||||
+boolean contextMenuVisible
|
||||
+number contextMenuX
|
||||
+number contextMenuY
|
||||
}
|
||||
TabState --> Tab
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [TabsView.vue:88-103](file://frontend/src/components/TabsView.vue#L88-L103)
|
||||
- [TabsView.vue:151-162](file://frontend/src/components/TabsView.vue#L151-L162)
|
||||
- [shared.ts:1-100](file://frontend/src/store/modules/tab/shared.ts#L1-L100)
|
||||
- [index.ts:1-100](file://frontend/src/store/modules/tab/index.ts#L1-L100)
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:88-103](file://frontend/src/components/TabsView.vue#L88-L103)
|
||||
- [TabsView.vue:151-162](file://frontend/src/components/TabsView.vue#L151-L162)
|
||||
- [shared.ts:1-100](file://frontend/src/store/modules/tab/shared.ts#L1-L100)
|
||||
- [index.ts:1-100](file://frontend/src/store/modules/tab/index.ts#L1-L100)
|
||||
|
||||
### 状态管理机制
|
||||
- 激活状态:activeKey 双向绑定至 Element Plus Tabs,点击标签即激活
|
||||
- 关闭操作:removeTabByKey 根据 key 移除标签,若关闭的是当前激活标签则自动切换到相邻标签或首页
|
||||
- 重新加载:通过 router-view 的 key 基于 fullPath 变更触发组件重新渲染(非标签页层面的"刷新")
|
||||
新的系统使用了 Pinia 进行状态管理,提供了更强大的响应式能力:
|
||||
- 激活状态:通过 activeKey 属性管理当前激活的标签
|
||||
- 标签操作:提供 addTab、removeTab、closeOthers、closeAll 等标准化方法
|
||||
- 上下文菜单:管理菜单的显示状态和位置信息
|
||||
- 持久化:自动将标签页状态保存到本地存储
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["开始"]) --> Click["点击标签"]
|
||||
Click --> Activate["设置 activeKey 并匹配当前路由"]
|
||||
Activate --> Push{"需要路由跳转?"}
|
||||
Push --> |是| RoutePush["router.push(fullPath)"]
|
||||
Push --> |否| End
|
||||
RoutePush --> End(["结束"])
|
||||
Start(["初始化"]) --> Load["加载持久化状态"]
|
||||
Load --> Watch["监听路由变化"]
|
||||
Watch --> AddTab{"标签是否存在?"}
|
||||
AddTab --> |否| Create["创建新标签"]
|
||||
AddTab --> |是| Activate["激活标签"]
|
||||
Create --> Update["更新状态"]
|
||||
Activate --> Update
|
||||
Update --> Persist["持久化保存"]
|
||||
Persist --> End(["完成"])
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [TabsView.vue:193-200](file://frontend/src/components/TabsView.vue#L193-L200)
|
||||
- [index.ts:100-300](file://frontend/src/store/modules/tab/index.ts#L100-L300)
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:193-200](file://frontend/src/components/TabsView.vue#L193-L200)
|
||||
- [index.ts:100-300](file://frontend/src/store/modules/tab/index.ts#L100-L300)
|
||||
|
||||
### 事件处理系统
|
||||
- 标签点击:handleTabClick -> activateByKey
|
||||
- 标签移除:handleTabRemove -> removeTabByKey
|
||||
- 右键菜单:上下文菜单命令 close/close_others/close_all -> 对应处理函数
|
||||
- 路由变化:watch(route.fullPath) -> upsertTab
|
||||
新的系统实现了完整的事件处理机制:
|
||||
- 标签点击:handleTabClick 方法处理标签激活逻辑
|
||||
- 右键菜单:handleContextMenu 方法控制菜单显示和位置
|
||||
- 菜单命令:handleContextCommand 处理各种菜单操作
|
||||
- 路由监听:watch 监听路由变化自动更新标签状态
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as "用户"
|
||||
participant Tabs as "Element Plus Tabs"
|
||||
participant TV as "标签页视图组件"
|
||||
User->>Tabs : 点击标签
|
||||
Tabs-->>TV : @tab-click
|
||||
TV->>TV : handleTabClick(key)
|
||||
TV->>TV : activateByKey(key)
|
||||
User->>Tabs : 右键标签
|
||||
Tabs-->>TV : 上下文菜单命令
|
||||
TV->>TV : handleContextCommand(cmd, key)
|
||||
alt 关闭
|
||||
TV->>TV : removeTabByKey(key)
|
||||
else 关闭其他
|
||||
TV->>TV : handleCloseOthers(currentKey)
|
||||
else 关闭全部
|
||||
TV->>TV : handleCloseAll()
|
||||
end
|
||||
participant Component as "全局标签页组件"
|
||||
participant Menu as "上下文菜单"
|
||||
participant Store as "状态管理"
|
||||
User->>Component : 左键点击标签
|
||||
Component->>Store : activateTab(key)
|
||||
Store->>Store : 更新 activeKey
|
||||
User->>Component : 右键点击标签
|
||||
Component->>Menu : showContextMenu(x,y)
|
||||
Menu->>Component : 选择菜单命令
|
||||
Component->>Store : executeCommand(command,key)
|
||||
Store->>Store : 执行相应操作
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [TabsView.vue:202-268](file://frontend/src/components/TabsView.vue#L202-L268)
|
||||
- [index.vue:150-350](file://frontend/src/layouts/modules/global-tab/index.vue#L150-L350)
|
||||
- [context-menu.vue:100-200](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L100-L200)
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:202-268](file://frontend/src/components/TabsView.vue#L202-L268)
|
||||
- [index.vue:150-350](file://frontend/src/layouts/modules/global-tab/index.vue#L150-L350)
|
||||
- [context-menu.vue:100-200](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L100-L200)
|
||||
|
||||
### 生命周期管理与缓存策略
|
||||
- 组件挂载:onMounted 中恢复标签状态并确保当前路由存在对应标签
|
||||
- KeepAlive 缓存:computed 计算 keepAliveInclude,基于所有有 name 的标签 name 去重后传入 KeepAlive include
|
||||
- 内存优化:通过 include 白名单控制缓存范围,减少不必要的组件实例保活
|
||||
- 性能监控:watch 监听 tabs 与 activeKey 变化以触发持久化,避免频繁写入
|
||||
新的系统优化了组件的生命周期管理:
|
||||
- 初始化:在 onMounted 钩子中恢复持久化的标签状态
|
||||
- 路由监听:通过 watch 监听路由变化自动同步标签状态
|
||||
- 缓存策略:根据标签的 keepAlive 配置决定是否缓存组件
|
||||
- 清理机制:在组件卸载时清理相关资源
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Mount["onMounted"] --> Restore["restore() 恢复标签状态"]
|
||||
Restore --> Upsert["upsertTab 当前路由"]
|
||||
Watch["watch(route.fullPath)"] --> Upsert
|
||||
Watch2["watch(tabs + activeKey)"] --> Persist["persist() 持久化"]
|
||||
KeepAlive["computed keepAliveInclude"] --> Cache["KeepAlive include"]
|
||||
Mount["组件挂载"] --> Restore["恢复持久化状态"]
|
||||
RouteChange["路由变化"] --> Sync["同步标签状态"]
|
||||
KeepAlive["计算缓存列表"] --> Cache["应用 KeepAlive"]
|
||||
Unmount["组件卸载"] --> Cleanup["清理资源"]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [TabsView.vue:270-286](file://frontend/src/components/TabsView.vue#L270-L286)
|
||||
- [TabsView.vue:120-123](file://frontend/src/components/TabsView.vue#L120-L123)
|
||||
- [index.vue:200-400](file://frontend/src/layouts/modules/global-tab/index.vue#L200-L400)
|
||||
- [index.ts:200-400](file://frontend/src/store/modules/tab/index.ts#L200-L400)
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:270-286](file://frontend/src/components/TabsView.vue#L270-L286)
|
||||
- [TabsView.vue:120-123](file://frontend/src/components/TabsView.vue#L120-L123)
|
||||
- [index.vue:200-400](file://frontend/src/layouts/modules/global-tab/index.vue#L200-L400)
|
||||
- [index.ts:200-400](file://frontend/src/store/modules/tab/index.ts#L200-L400)
|
||||
|
||||
### 与路由系统的集成与状态同步
|
||||
- 路由元信息:路由 meta.title 提供标签标题,meta.icon 提供图标键值
|
||||
- 首页特殊处理:HOME_PATH 对应标签不可关闭
|
||||
- 路由跳转:激活标签时若 fullPath 与当前不同则执行 router.push
|
||||
- 子路由处理:根布局路由(path='/')不加入标签,children 的实际页面才加入
|
||||
- **工具箱集成**:工具箱路由 `/toolbox/luxsin-controller` 配置了 `icon: 'tools'` 元信息,支持在标签页中显示工具箱图标
|
||||
新的系统与路由系统深度集成:
|
||||
- 路由守卫:在路由变更前检查并更新标签状态
|
||||
- 元信息提取:从路由 meta 中提取标题、图标等信息
|
||||
- 导航同步:标签激活时自动同步路由状态
|
||||
- 首页保护:特殊处理首页标签的不可关闭特性
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Router as "路由系统"
|
||||
participant TV as "标签页视图组件"
|
||||
participant Route as "当前路由"
|
||||
Router->>TV : beforeEach/to
|
||||
TV->>TV : normalizeTabFromRoute(Route)
|
||||
TV->>TV : upsertTab(标准化后的标签)
|
||||
TV->>TV : activateByKey(匹配 key)
|
||||
alt 需要跳转
|
||||
TV->>Router : router.push(fullPath)
|
||||
end
|
||||
Note over TV : 工具箱路由支持 tools 图标
|
||||
participant Guard as "路由守卫"
|
||||
participant TabStore as "标签页状态"
|
||||
participant UI as "标签页UI"
|
||||
Router->>Guard : beforeEach(to, from)
|
||||
Guard->>TabStore : upsertTab(to)
|
||||
TabStore->>UI : 响应式更新
|
||||
Router->>TabStore : 获取当前激活标签
|
||||
TabStore->>Router : 返回路由信息
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [TabsView.vue:88-103](file://frontend/src/components/TabsView.vue#L88-L103)
|
||||
- [TabsView.vue:180-191](file://frontend/src/components/TabsView.vue#L180-191)
|
||||
- [TabsView.vue:193-200](file://frontend/src/components/TabsView.vue#L193-L200)
|
||||
- [index.js:68-72](file://frontend/src/router/index.js#L68-L72)
|
||||
- [index.ts:300-500](file://frontend/src/store/modules/tab/index.ts#L300-L500)
|
||||
- [index.vue:300-500](file://frontend/src/layouts/modules/global-tab/index.vue#L300-L500)
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:88-103](file://frontend/src/components/TabsView.vue#L88-L103)
|
||||
- [TabsView.vue:180-191](file://frontend/src/components/TabsView.vue#L180-191)
|
||||
- [TabsView.vue:193-200](file://frontend/src/components/TabsView.vue#L193-L200)
|
||||
- [index.js:68-72](file://frontend/src/router/index.js#L68-L72)
|
||||
- [index.ts:300-500](file://frontend/src/store/modules/tab/index.ts#L300-L500)
|
||||
- [index.vue:300-500](file://frontend/src/layouts/modules/global-tab/index.vue#L300-L500)
|
||||
|
||||
### 现代化设计系统与视觉增强
|
||||
### 增强的上下文菜单功能
|
||||
新的系统实现了功能完整的上下文菜单:
|
||||
- 菜单定位:智能计算菜单位置避免超出屏幕边界
|
||||
- 操作选项:支持关闭、关闭其他、关闭全部等常用操作
|
||||
- 动态禁用:根据标签状态动态控制菜单选项的可用性
|
||||
- 视觉反馈:提供悬停和选中状态的视觉反馈
|
||||
|
||||
**重大更新** 实现了现代化的标签页设计系统,解决了传统卡片标签的布局抖动问题
|
||||
```mermaid
|
||||
flowchart TD
|
||||
RightClick["右键点击标签"] --> Position["计算菜单位置"]
|
||||
Position --> Show["显示上下文菜单"]
|
||||
Show --> Select["选择菜单选项"]
|
||||
Select --> Execute["执行对应操作"]
|
||||
Execute --> Hide["隐藏菜单"]
|
||||
```
|
||||
|
||||
- **固定内边距策略**:通过固定 padding-left 和 padding-right 为16px,彻底解决了 Element Plus 卡片标签在悬停时因关闭按钮展开导致的布局抖动问题
|
||||
- **一致的关闭按钮尺寸**:统一关闭按钮尺寸为14x14px,确保所有标签的视觉一致性
|
||||
- **改进的层级管理**:活动标签 z-index 设置为2,悬停标签 z-index 也设置为2,确保正确的视觉层次关系
|
||||
- **增强的视觉反馈**:活动标签添加青色内嵌阴影 `inset 0 2px 0 var(--lux-cyan)`,提供清晰的激活状态指示
|
||||
- **平滑过渡动画**:标签背景色、颜色、边框颜色和阴影都添加了0.2s的平滑过渡效果
|
||||
- **玻璃拟态设计**:标签页采用半透明背景和模糊效果,与现代设计系统保持一致
|
||||
- **优化的间距管理**:标签间间距固定为6px,避免动态变化导致的布局不稳定
|
||||
|
||||
**新增特性**:
|
||||
- 固定内边距策略消除布局抖动
|
||||
- 统一的14x14px关闭按钮尺寸
|
||||
- 改进的z-index层级管理系统
|
||||
- 青色内嵌阴影激活指示器
|
||||
- 0.2s平滑过渡动画效果
|
||||
- 玻璃拟态视觉风格集成
|
||||
**图表来源**
|
||||
- [context-menu.vue:150-300](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L150-L300)
|
||||
|
||||
**章节来源**
|
||||
- [lux-theme.css:776-847](file://frontend/src/styles/lux-theme.css#L776-L847)
|
||||
- [TabsView.vue:320-324](file://frontend/src/components/TabsView.vue#L320-L324)
|
||||
- [TabsView.vue:64-76](file://frontend/src/components/TabsView.vue#L64-L76)
|
||||
- [TabsView.vue:46-50](file://frontend/src/components/TabsView.vue#L46-L50)
|
||||
- [index.js:68-72](file://frontend/src/router/index.js#L68-L72)
|
||||
- [context-menu.vue:150-300](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L150-L300)
|
||||
|
||||
## 依赖关系分析
|
||||
- 组件依赖:TabsView.vue 依赖 vue-router、@element-plus/icons-vue、utils/tabs 常量
|
||||
- 路由依赖:路由配置提供 meta.title/meta.icon,影响标签标题与图标
|
||||
- 布局依赖:Layout 将 TabsView 作为主内容区的一部分
|
||||
- **工具箱依赖**:工具箱视图组件独立实现设备控制功能,通过路由系统集成到标签页系统中
|
||||
新的系统采用了松耦合的依赖关系:
|
||||
- 组件依赖:全局标签页组件依赖上下文菜单组件和状态管理模块
|
||||
- 状态依赖:状态管理模块依赖共享类型定义和持久化工具
|
||||
- 布局依赖:基础布局和空白布局依赖全局标签页组件
|
||||
- 路由依赖:所有组件都依赖 vue-router 进行导航控制
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
TV["TabsView.vue"] --> VR["vue-router"]
|
||||
TV --> EP["@element-plus/icons-vue"]
|
||||
TV --> UT["utils/tabs.js"]
|
||||
UT --> C["常量: TABS_STORAGE_KEY, HOME_PATH"]
|
||||
TV --> RT["router/index.js"]
|
||||
L["layout/index.vue"] --> TV
|
||||
TB["toolbox/luxsin-controller/index.vue"] --> TV
|
||||
GC["全局标签页组件<br/>global-tab/index.vue"] --> CM["上下文菜单<br/>global-tab/context-menu.vue"]
|
||||
GC --> TS["状态管理<br/>store/modules/tab/index.ts"]
|
||||
TS --> ST["共享类型<br/>store/modules/tab/shared.ts"]
|
||||
BL["基础布局<br/>base-layout/index.vue"] --> GC
|
||||
BKL["空白布局<br/>blank-layout/index.vue"] --> GC
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [TabsView.vue:56-59](file://frontend/src/components/TabsView.vue#L56-L59)
|
||||
- [tabs.js:1-9](file://frontend/src/utils/tabs.js#L1-L9)
|
||||
- [index.js:1-109](file://frontend/src/router/index.js#L1-L109)
|
||||
- [index.vue:151](file://frontend/src/layout/index.vue#L151)
|
||||
- [index.vue:1-231](file://frontend/src/views/toolbox/luxsin-controller/index.vue#L1-L231)
|
||||
- [index.vue:1-100](file://frontend/src/layouts/modules/global-tab/index.vue#L1-L100)
|
||||
- [context-menu.vue:1-100](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L1-L100)
|
||||
- [index.ts:1-100](file://frontend/src/store/modules/tab/index.ts#L1-L100)
|
||||
- [shared.ts:1-100](file://frontend/src/store/modules/tab/shared.ts#L1-L100)
|
||||
- [index.vue:1-100](file://frontend/src/layouts/base-layout/index.vue#L1-L100)
|
||||
- [index.vue:1-100](file://frontend/src/layouts/blank-layout/index.vue#L1-L100)
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:56-59](file://frontend/src/components/TabsView.vue#L56-L59)
|
||||
- [tabs.js:1-9](file://frontend/src/utils/tabs.js#L1-L9)
|
||||
- [index.js:1-109](file://frontend/src/router/index.js#L1-L109)
|
||||
- [index.vue:151](file://frontend/src/layout/index.vue#L151)
|
||||
- [index.vue:1-231](file://frontend/src/views/toolbox/luxsin-controller/index.vue#L1-L231)
|
||||
- [index.vue:1-100](file://frontend/src/layouts/modules/global-tab/index.vue#L1-L100)
|
||||
- [context-menu.vue:1-100](file://frontend/src/layouts/modules/global-tab/context-menu.vue#L1-L100)
|
||||
- [index.ts:1-100](file://frontend/src/store/modules/tab/index.ts#L1-L100)
|
||||
- [shared.ts:1-100](file://frontend/src/store/modules/tab/shared.ts#L1-L100)
|
||||
- [index.vue:1-100](file://frontend/src/layouts/base-layout/index.vue#L1-L100)
|
||||
- [index.vue:1-100](file://frontend/src/layouts/blank-layout/index.vue#L1-L100)
|
||||
|
||||
## 性能考量
|
||||
- KeepAlive 缓存:仅缓存有 name 的标签组件,避免无意义的实例保活
|
||||
- 持久化频率:通过 watch 监听 tabs 与 activeKey 变化进行持久化,减少频繁写入
|
||||
- DOM 重绘:标签标题与图标采用固定宽度与省略号,避免布局抖动
|
||||
- 路由跳转:仅在 fullPath 与当前不同才执行跳转,避免重复导航
|
||||
- **动画性能**:使用 CSS transition 而非 JavaScript 动画,确保0.2s过渡动画的流畅性
|
||||
- **布局稳定性**:固定内边距策略消除了悬停时的布局重排,提升滚动性能
|
||||
- **工具箱性能**:工具箱视图组件使用独立的请求处理和错误处理机制,避免影响主标签页性能
|
||||
新的系统在性能方面进行了多项优化:
|
||||
- 响应式更新:使用 Pinia 的响应式特性,只更新必要的 DOM 节点
|
||||
- 懒加载:标签页组件按需加载,减少初始渲染时间
|
||||
- 内存管理:及时清理不需要的标签页实例,防止内存泄漏
|
||||
- 事件优化:使用防抖和节流技术优化高频事件处理
|
||||
- 缓存策略:智能缓存活跃标签页,提升二次访问速度
|
||||
|
||||
## 故障排查指南
|
||||
- 标签无法关闭:检查 closable 字段,首页标签默认不可关闭
|
||||
- 标题显示异常:检查路由 meta.title 是否存在,否则回退到 name 或 path
|
||||
- 图标不显示:检查 meta.icon 键值是否正确且在 iconMap 中有对应映射
|
||||
- 标签状态丢失:检查 sessionStorage 写入权限与容量限制
|
||||
- 路由跳转无效:检查 fullPath 与当前路由是否一致,避免不必要的 router.push
|
||||
- **布局抖动问题**:确认使用了固定内边距策略,避免 Element Plus 默认的动态padding行为
|
||||
- **关闭按钮尺寸不一致**:检查CSS规则是否正确应用了14x14px的统一尺寸
|
||||
- **层级显示异常**:确认活动标签和悬停标签的z-index设置正确
|
||||
- **动画卡顿**:检查浏览器是否支持CSS transition和backdrop-filter属性
|
||||
- **工具箱图标问题**:确认工具箱路由配置了正确的 `icon: 'tools'` 元信息,且 iconMap 中包含 `tools: Tools` 映射
|
||||
|
||||
**章节来源**
|
||||
- [TabsView.vue:101-101](file://frontend/src/components/TabsView.vue#L101-L101)
|
||||
- [TabsView.vue:78-81](file://frontend/src/components/TabsView.vue#L78-L81)
|
||||
- [TabsView.vue:64-76](file://frontend/src/components/TabsView.vue#L64-L76)
|
||||
- [TabsView.vue:125-141](file://frontend/src/components/TabsView.vue#L125-L141)
|
||||
- [TabsView.vue:197-199](file://frontend/src/components/TabsView.vue#L197-L199)
|
||||
- [lux-theme.css:776-847](file://frontend/src/styles/lux-theme.css#L776-L847)
|
||||
- [index.js:68-72](file://frontend/src/router/index.js#L68-L72)
|
||||
- 标签页不显示:检查路由配置是否正确,确认标签页状态是否正常初始化
|
||||
- 右键菜单异常:检查上下文菜单的定位逻辑,确认鼠标坐标计算正确
|
||||
- 状态不同步:检查路由守卫是否正确调用标签页状态更新方法
|
||||
- 持久化失败:检查本地存储权限和容量限制
|
||||
- 组件缓存问题:检查 keepAlive 配置是否正确设置
|
||||
- 布局集成问题:确认在布局文件中正确引入了全局标签页组件
|
||||
|
||||
## 结论
|
||||
标签页视图组件通过清晰的数据结构、完善的事件处理与路由集成,实现了标签的激活、关闭、右键菜单与持久化。**现代化改进**:通过实现固定内边距策略、统一的关闭按钮尺寸、改进的层级管理和增强的视觉反馈效果,显著提升了用户体验和界面稳定性。结合 KeepAlive 缓存与现代化主题样式,提供了优秀的交互性能和美观的界面表现。工具箱功能的集成进一步完善了系统的功能完整性,为用户提供了便捷的设备管理和数据同步工具。建议在扩展新功能时遵循现有模式,保持标签状态与路由状态的一致性,并注意缓存范围与持久化策略的平衡。
|
||||
新的全局标签页系统通过模块化架构设计和增强的功能特性,显著提升了代码的可维护性和用户体验。相比旧版本的单文件组件,新系统提供了更好的状态管理能力、更丰富的交互功能和更清晰的代码结构。Pinia 状态管理的引入使得标签页状态的管理更加直观和高效,而上下文菜单功能的完善则为用户提供了更加便捷的操作方式。建议在后续开发中继续遵循这一模块化设计理念,保持代码结构的清晰和功能的可扩展性。
|
||||
|
||||
## 附录
|
||||
- 常用配置项
|
||||
- 存储键名:TABS_STORAGE_KEY
|
||||
- 首页路径:HOME_PATH
|
||||
- 图标映射:document/list/upload/collection/home/user/tools
|
||||
- **现代化设计配置**
|
||||
- 固定内边距:padding-left: 16px, padding-right: 16px
|
||||
- 关闭按钮尺寸:width: 14px, height: 14px
|
||||
- 层级管理:活动标签 z-index: 2, 悬停标签 z-index: 2
|
||||
- 过渡动画:transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease
|
||||
- 激活指示器:box-shadow: inset 0 2px 0 var(--lux-cyan)
|
||||
- **工具箱功能配置**
|
||||
- 路由路径:/toolbox/luxsin-controller
|
||||
- 图标配置:icon: 'tools'
|
||||
- 功能描述:设备数据同步和控制工具
|
||||
- **最佳实践**
|
||||
- 在路由 meta 中提供 title 与 icon,确保标签显示一致性
|
||||
- 控制 KeepAlive include 范围,避免过度缓存
|
||||
- 使用 sessionStorage 进行轻量级状态持久化,避免频繁写入
|
||||
- 利用 CSS transition 实现流畅的用户交互反馈
|
||||
- 为新功能模块配置合适的图标和元信息,确保在标签页中正确显示
|
||||
- 使用固定内边距策略避免标签页布局抖动
|
||||
- 统一关闭按钮尺寸确保视觉一致性
|
||||
- 核心文件路径
|
||||
- 主组件:src/layouts/modules/global-tab/index.vue
|
||||
- 上下文菜单:src/layouts/modules/global-tab/context-menu.vue
|
||||
- 状态管理:src/store/modules/tab/index.ts
|
||||
- 类型定义:src/store/modules/tab/shared.ts
|
||||
- 主要功能特性
|
||||
- 标签页的增删改查操作
|
||||
- 右键上下文菜单支持
|
||||
- 路由状态同步
|
||||
- 本地持久化存储
|
||||
- 组件缓存管理
|
||||
- 最佳实践建议
|
||||
- 在路由配置中提供完整的元信息
|
||||
- 合理使用组件缓存策略
|
||||
- 注意标签页状态的持久化
|
||||
- 优化上下文菜单的用户体验
|
||||
- 保持代码模块化和职责分离
|
||||
Reference in New Issue
Block a user