Files
hzhub/hzhub-portal-employee/docs/TABBAR_FEATURE.md
大壮 278e507e8a 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>
2026-04-13 03:47:33 +00:00

358 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 标签页功能说明
## 功能概述
已将 `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 个标签页)
- 固定标签页与普通标签页混排测试