init: 数字员工平台初始代码
包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。 - 工作台画布:节点拖放、连线模式、右键菜单、AI 助手 - 后端:连接器 API、专员种子数据 - 导航:左侧导航、工坊、市场、控制台
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
# DR02 — 导航与路由访问规则
|
||||
|
||||
> **版本:V1.1 | 左导航布局 | Vue3 + Element Plus + Vue Router**
|
||||
> **参考:pj006-zhilianyuan2 frontend-orgadmin 的路由守卫模式**
|
||||
>
|
||||
> **⚠️ 本文为 V1 导航基线。** 管理员导航已拆为「6 组织管理 + 7 系统设置」(见 `00_AI_Context/forai02`);一级导航将按 SY03 演进为「知识库 / 智能体 / 学习与考试 / 陪练与认证 / 内容运营 / 组织与系统」。本文第 1 节的菜单树为 V1 历史结构。
|
||||
|
||||
---
|
||||
|
||||
## 1. 导航菜单可见性规则
|
||||
|
||||
| 菜单 | 可见角色 | 类型 | 说明 |
|
||||
|------|---------|------|------|
|
||||
| 首页 | employee + admin | 一级菜单 | 所有人可见 |
|
||||
| 公司介绍培训 | employee + admin | 一级菜单 | 所有人可见 |
|
||||
| 产品知识 | employee + admin | 一级菜单 | 所有人可见 |
|
||||
| 产品销售培训 | employee + admin | 一级菜单 | 所有人可见 |
|
||||
| 考试 | employee + admin | 一级展开 | 所有人可见 |
|
||||
| ├ 自测练习 | employee + admin | 子菜单 | — |
|
||||
| ├ 正式结业考试 | employee + admin | 子菜单 | — |
|
||||
| ├ 我的考试记录 | employee + admin | 子菜单 | — |
|
||||
| └ 题库管理 | **admin only** | 子菜单 | **管理员专属,员工不可见** |
|
||||
| 知识管理 | **admin only** | 一级展开 | **管理员专属,员工不可见** |
|
||||
| ├ 课件素材管理 | admin only | 子菜单 | — |
|
||||
| └ 素材审批列表 | admin only | 子菜单 | — |
|
||||
| 系统管理 | **admin only** | 一级展开 | **管理员专属,员工不可见** |
|
||||
| ├ 用户账号管理 | admin only | 子菜单 | — |
|
||||
| ├ 全部考试成绩 | admin only | 子菜单 | — |
|
||||
| └ 系统参数配置 | admin only | 子菜单 | — |
|
||||
|
||||
## 2. 路由守卫实现
|
||||
|
||||
```javascript
|
||||
// router/index.js
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('@/views/Login.vue'),
|
||||
meta: { requiresAuth: false },
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: MainLayout,
|
||||
children: [
|
||||
// 员工 + 管理员都能访问
|
||||
{ path: '', component: () => import('@/views/home/HomeView.vue') },
|
||||
{ path: 'company-train', component: () => import('@/views/companyTrain/CompanyTrainView.vue') },
|
||||
{ path: 'products', component: () => import('@/views/product/ProductList.vue') },
|
||||
{ path: 'products/:id', component: () => import('@/views/product/ProductDetail.vue') },
|
||||
{ path: 'courses', component: () => import('@/views/salesTrain/CourseList.vue') },
|
||||
{ path: 'courses/:id', component: () => import('@/views/salesTrain/CourseDetail.vue') },
|
||||
{ path: 'exam/self-test', component: () => import('@/views/exam/ExamSelfTest.vue') },
|
||||
{ path: 'exam/formal', component: () => import('@/views/exam/ExamFormal.vue') },
|
||||
{ path: 'exam/my-records', component: () => import('@/views/exam/ExamMyRecord.vue') },
|
||||
|
||||
// 管理员专属路由
|
||||
{
|
||||
path: 'exam/questions',
|
||||
component: () => import('@/views/exam/ExamQuestionBank.vue'),
|
||||
meta: { requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: 'knowledge/materials',
|
||||
component: () => import('@/views/knowledge/MaterialManage.vue'),
|
||||
meta: { requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: 'knowledge/audit',
|
||||
component: () => import('@/views/knowledge/MaterialAuditList.vue'),
|
||||
meta: { requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: 'system/users',
|
||||
component: () => import('@/views/system/UserManage.vue'),
|
||||
meta: { requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: 'system/exam-records',
|
||||
component: () => import('@/views/system/ExamRecordManage.vue'),
|
||||
meta: { requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: 'system/config',
|
||||
component: () => import('@/views/system/SystemConfig.vue'),
|
||||
meta: { requiresAdmin: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const router = createRouter({ history: createWebHistory(), routes })
|
||||
|
||||
// 路由守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.path === '/login') {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
// 未登录 → 跳转登录页
|
||||
if (!authStore.isLoggedIn) {
|
||||
next('/login')
|
||||
return
|
||||
}
|
||||
|
||||
// 管理员路由校验(⚠️ 前端仅作 UX 隐藏,非安全边界)
|
||||
if (to.meta.requiresAdmin && authStore.user?.role !== 'admin') {
|
||||
next('/')
|
||||
return
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
```
|
||||
|
||||
## 3. 后端鉴权对应规则
|
||||
|
||||
| 路由 | 后端校验 | 说明 |
|
||||
|------|---------|------|
|
||||
| `/api/auth/*` | 仅 login 无需认证 | — |
|
||||
| `/api/company-train/*` | 需有效 JWT | 员工/管理员均可 |
|
||||
| `/api/products/*` | 需有效 JWT | 员工/管理员均可 |
|
||||
| `/api/courses/*` | 需有效 JWT | 员工/管理员均可 |
|
||||
| `/api/exam/list\|cover\|start\|submit\|record` | 需有效 JWT | 学员端考试 |
|
||||
| `/api/exam/questions\|papers` | 需 admin 角色 | 题库/考试配置管理 |
|
||||
| `/api/media/upload\|preview\|status` | 需有效 JWT | 文件操作 |
|
||||
| `/api/media/audit*` | 需 admin 角色 | 素材审批 |
|
||||
| `/api/system/*` | 需 admin 角色 | 系统管理全部 |
|
||||
| `/api/ai-chat/*` | 需有效 JWT | 员工/管理员均可 |
|
||||
| `/api/health` | 无需认证 | 健康检查 |
|
||||
|
||||
## 4. 安全边界说明
|
||||
|
||||
> **前端路由守卫仅提供用户体验层面的菜单隐藏和路径拦截,不能作为安全边界。**
|
||||
> **所有权限校验必须在后端 API 层强制执行。**
|
||||
>
|
||||
> 即使员工通过直接输入 URL 访问 `/knowledge/materials`:
|
||||
> 1. 前端路由守卫会拦截并重定向 → UX 保护
|
||||
> 2. 如果前端拦截被绕过,后端 API 返回 403 → 真正安全边界
|
||||
Reference in New Issue
Block a user