feat: 添加员工门户项目及相关后端改造
- 新增 hzhub-portal-employee 员工门户前端项目(基于 Vue3 + Element Plus) - 后端登录接口增加返回 nickName 字段 - 移除 KnowledgeInfoController 的 @SaCheckPermission 注解 - 删除 hzhub-portal-company 旧门户项目 - 更新项目文档和架构说明 - 添加后台运行管理脚本(start-all.sh / status-all.sh / stop-all.sh) - 更新 docker-compose 配置 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
358
hzhub-portal-employee/docs/TABBAR_FEATURE.md
Normal file
358
hzhub-portal-employee/docs/TABBAR_FEATURE.md
Normal file
@@ -0,0 +1,358 @@
|
||||
# 标签页功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
已将 `hzhub-admin` 中的标签页导航功能移植到 `hzhub-portal-employee`,使用 Element Plus 组件重新实现。
|
||||
|
||||
## 主要功能
|
||||
|
||||
### 1. 标签页管理
|
||||
|
||||
- 自动添加标签页:路由切换时自动创建对应标签页
|
||||
- 关闭标签页:点击关闭按钮关闭当前标签页
|
||||
- 固定标签页:固定后的标签页不可关闭,以不同颜色标识
|
||||
- 标签页持久化:标签页状态自动保存到本地存储
|
||||
|
||||
### 2. 工具栏功能
|
||||
|
||||
- 更多操作菜单:
|
||||
- 关闭其他标签页
|
||||
- 关闭右侧标签页
|
||||
- 关闭所有标签页
|
||||
- 全屏切换:切换浏览器全屏模式
|
||||
|
||||
### 3. 视觉效果
|
||||
|
||||
- 当前激活标签页高亮显示(主题色)
|
||||
- 固定标签页以警告色标识
|
||||
- 悬停显示关闭按钮
|
||||
- 平滑过渡动画
|
||||
- 标签页图标支持(可选)
|
||||
|
||||
## 技术实现
|
||||
|
||||
### UI 框架适配
|
||||
|
||||
从 `hzhub-admin` (Ant Design Vue) 到 `hzhub-portal-employee` (Element Plus) 的适配:
|
||||
|
||||
| 功能 | hzhub-admin | hzhub-portal-employee |
|
||||
|------|-------------|----------------------|
|
||||
| UI 框架 | Ant Design Vue + Vben | Element Plus |
|
||||
| 图标组件 | `<VbenIcon>` | `<el-icon>` |
|
||||
| 下拉菜单 | `<VbenDropdownMenu>` | `<el-dropdown>` |
|
||||
| CSS 系统 | TailwindCSS | SCSS + Element Plus 变量 |
|
||||
| 图标库 | Vben Icons | Element Plus Icons |
|
||||
|
||||
### 文件结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── stores/
|
||||
│ ├── modules/
|
||||
│ │ └── tabbar.ts # 标签页状态管理
|
||||
│ └── index.ts # 导出 tabbar store
|
||||
├── hooks/
|
||||
│ └── useTabbar.ts # 标签页操作 Hook
|
||||
├── layouts/
|
||||
│ ├── components/
|
||||
│ │ └── TabsView/
|
||||
│ │ └── index.vue # 标签页视图组件
|
||||
│ └── LayoutVertical/
|
||||
│ └── index.vue # 集成标签页的布局
|
||||
└── routers/
|
||||
└ modules/
|
||||
└── staticRouter.ts # 路由配置
|
||||
```
|
||||
|
||||
### 核心代码
|
||||
|
||||
#### Tabbar Store
|
||||
|
||||
```typescript
|
||||
export interface TabItem {
|
||||
path: string; // 路由路径
|
||||
name: string; // 路由名称
|
||||
title: string; // 标签页标题
|
||||
icon?: string; // 图标
|
||||
affix?: boolean; // 是否固定
|
||||
query?: any; // 路由参数
|
||||
}
|
||||
|
||||
export const useTabbarStore = defineStore('tabbar', {
|
||||
state: (): TabbarState => ({
|
||||
tabs: [], // 标签页列表
|
||||
activeTab: '/', // 当前激活的标签页
|
||||
cachedTabs: new Set(), // 缓存的组件名称
|
||||
}),
|
||||
|
||||
actions: {
|
||||
addTab(route), // 添加标签页
|
||||
closeTab(path), // 关闭标签页
|
||||
closeOtherTabs(path), // 关闭其他标签页
|
||||
closeAllTabs(), // 关闭所有标签页
|
||||
closeRightTabs(path), // 关闭右侧标签页
|
||||
toggleAffixTab(path), // 固定/取消固定
|
||||
initAffixTabs(routes), // 初始化固定标签页
|
||||
},
|
||||
|
||||
persist: {
|
||||
key: 'hzhub-employee-tabs',
|
||||
paths: ['tabs', 'activeTab'],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### useTabbar Hook
|
||||
|
||||
```typescript
|
||||
export function useTabbar() {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const tabbarStore = useTabbarStore();
|
||||
|
||||
// 监听路由变化,自动添加标签页
|
||||
watch(() => route.path, () => {
|
||||
if (route.name) {
|
||||
tabbarStore.addTab(route);
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 初始化固定标签页
|
||||
watch(() => router.getRoutes(), () => {
|
||||
tabbarStore.initAffixTabs(router.getRoutes());
|
||||
}, { immediate: true });
|
||||
|
||||
return {
|
||||
currentActive, // 当前激活的标签页
|
||||
currentTabs, // 标签页列表
|
||||
handleClick, // 点击标签页
|
||||
handleClose, // 关闭标签页
|
||||
handleUnpin, // 固定/取消固定
|
||||
closeOtherTabs, // 关闭其他标签页
|
||||
closeAllTabs, // 关闭所有标签页
|
||||
closeRightTabs, // 关闭右侧标签页
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### TabsView 组件
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useTabbarStore } from '@/stores';
|
||||
|
||||
const tabbarStore = useTabbarStore();
|
||||
const activeTabPath = computed(() => tabbarStore.activeTab);
|
||||
const tabs = computed(() => tabbarStore.tabs);
|
||||
const isFullscreen = ref(false);
|
||||
|
||||
// 点击标签页
|
||||
const handleTabClick = (tab: TabItem) => {
|
||||
tabbarStore.setActiveTab(tab.path);
|
||||
router.push(tab.path);
|
||||
};
|
||||
|
||||
// 关闭标签页
|
||||
const handleTabClose = (path: string) => {
|
||||
tabbarStore.closeTab(path);
|
||||
};
|
||||
|
||||
// 切换全屏
|
||||
const toggleFullscreen = () => {
|
||||
isFullscreen.value = !isFullscreen.value;
|
||||
if (isFullscreen.value) {
|
||||
document.documentElement.requestFullscreen();
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 布局集成
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import TabsView from '@/layouts/components/TabsView/index.vue';
|
||||
import { useTabbar } from '@/hooks/useTabbar';
|
||||
|
||||
const { handleClose, handleUnpin } = useTabbar();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<Aside />
|
||||
<div class="main-area">
|
||||
<Header />
|
||||
<TabsView
|
||||
:show-icon="true"
|
||||
@close="handleClose"
|
||||
@unpin="handleUnpin"
|
||||
/>
|
||||
<main class="page-content">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 固定标签页
|
||||
|
||||
在路由配置中添加 `meta.affix` 属性:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'dashboard',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
icon: 'Odometer',
|
||||
affix: true, // 固定标签页
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 隐藏标签页
|
||||
|
||||
在路由配置中添加 `meta.isHide` 属性:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/profile',
|
||||
name: 'profile',
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
isHide: '1', // 不显示在标签页中
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### 禁用缓存
|
||||
|
||||
在路由配置中添加 `meta.isKeepAlive` 属性:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/example',
|
||||
name: 'example',
|
||||
meta: {
|
||||
title: '示例页面',
|
||||
isKeepAlive: '0', // 不缓存组件
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 样式定制
|
||||
|
||||
标签页样式使用 Element Plus CSS 变量系统:
|
||||
|
||||
- **激活状态**: `--el-color-primary` (主题色)
|
||||
- **固定状态**: `--el-color-warning` (警告色)
|
||||
- **悬停状态**: `--el-color-primary-light-9`
|
||||
- **背景色**: `--el-bg-color-page`
|
||||
- **边框色**: `--el-border-color-light`
|
||||
|
||||
可在 `src/styles/var.scss` 中自定义这些变量值。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **标签页限制**:
|
||||
- 固定的标签页不能关闭
|
||||
- 至少保留一个标签页
|
||||
- 标签页标题过长会自动截断
|
||||
|
||||
2. **路由要求**:
|
||||
- 路由必须设置 `name` 属性
|
||||
- 路由必须设置 `meta.title` 属性
|
||||
- 隐藏路由(`isHide='1'`)不会添加到标签页
|
||||
|
||||
3. **持久化配置**:
|
||||
- 使用 `pinia-plugin-persistedstate` 自动保存
|
||||
- 保存路径:`['tabs', 'activeTab']`
|
||||
- 不保存 `cachedTabs` (组件缓存)
|
||||
|
||||
4. **性能优化**:
|
||||
- 使用 `keep-alive` 缓存组件
|
||||
- 标签页列表使用 `Set` 优化查找
|
||||
- 懒加载图标组件
|
||||
|
||||
## 与 hzhub-admin 的差异
|
||||
|
||||
### 保留的功能
|
||||
|
||||
- ✅ 标签页自动添加/关闭
|
||||
- ✅ 标签页固定/取消固定
|
||||
- ✅ 批量关闭操作
|
||||
- ✅ 标签页持久化
|
||||
- ✅ 全屏切换
|
||||
- ✅ 标签页图标显示
|
||||
|
||||
### 简化的功能
|
||||
|
||||
- ❌ 拖拽排序(暂未实现)
|
||||
- ❌ 滚轮滚动(暂未实现)
|
||||
- ❌ 中键关闭(暂未实现)
|
||||
- ❌ 右键菜单(暂未实现)
|
||||
- ❌ 多种标签页样式(plain/brisk/card)
|
||||
- ❌ 国际化标题切换
|
||||
|
||||
### 新增的功能
|
||||
|
||||
- ✅ 简化的工具栏设计
|
||||
- ✅ Element Plus 原生样式
|
||||
- ✅ 更简洁的代码结构
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **交互增强**:
|
||||
- 添加右键菜单功能
|
||||
- 支持拖拽排序标签页
|
||||
- 支持鼠标滚轮横向滚动
|
||||
- 支持中键点击关闭
|
||||
|
||||
2. **样式定制**:
|
||||
- 提供多种标签页样式(chrome/card/plain)
|
||||
- 支持自定义标签页主题色
|
||||
- 标签页宽度自适应
|
||||
|
||||
3. **功能扩展**:
|
||||
- 标签页分组功能
|
||||
- 标签页搜索功能
|
||||
- 最近访问的标签页历史
|
||||
- 标签页快捷键操作
|
||||
|
||||
4. **性能优化**:
|
||||
- 虚拟滚动优化大量标签页
|
||||
- 标签页懒加载优化
|
||||
- 组件缓存策略优化
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 功能测试
|
||||
|
||||
1. **标签页管理**:
|
||||
- 切换路由自动添加标签页
|
||||
- 关闭标签页后自动切换到相邻标签页
|
||||
- 固定标签页不可关闭
|
||||
- 刷新后标签页状态保持
|
||||
|
||||
2. **批量操作**:
|
||||
- 关闭其他标签页
|
||||
- 关闭右侧标签页
|
||||
- 关闭所有标签页
|
||||
|
||||
3. **全屏功能**:
|
||||
- 点击全屏按钮进入全屏模式
|
||||
- 再次点击退出全屏模式
|
||||
|
||||
### 兼容性测试
|
||||
|
||||
- Chrome、Firefox、Safari 浏览器测试
|
||||
- 响应式布局测试(桌面端、平板)
|
||||
- 标签页滚动测试(超过 10 个标签页)
|
||||
- 固定标签页与普通标签页混排测试
|
||||
Reference in New Issue
Block a user