init: 数字员工平台初始代码

包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。
- 工作台画布:节点拖放、连线模式、右键菜单、AI 助手
- 后端:连接器 API、专员种子数据
- 导航:左侧导航、工坊、市场、控制台
This commit is contained in:
eaiadmin
2026-08-18 20:19:58 +08:00
commit 4e8817d768
239 changed files with 48631 additions and 0 deletions
+257
View File
@@ -0,0 +1,257 @@
# DR01 — 主题系统设计
> **版本:V1.1 | UI 框架:Element Plus**
> **参考:pj006-zhilianyuan2 frontend-orgadmin 的主题覆盖模式**
---
## 1. Element Plus 变量覆盖
```scss
// styles/element-variables.scss
$--color-primary: #1677ff; // 主色 — 博昇品牌蓝
$--color-success: #52c41a;
$--color-warning: #faad14;
$--color-danger: #ff4d4f;
$--color-info: #909399;
$--color-text-primary: #303133;
$--color-text-regular: #606266;
$--color-text-secondary: #909399;
$--color-text-placeholder: #c0c4cc;
$--border-color-base: #dcdfe6;
$--border-color-light: #e4e7ed;
$--border-color-lighter: #ebeef5;
$--border-color-extra-light: #f2f6fc;
$--background-color-base: #f5f7fa;
$--border-radius-base: 4px;
$--border-radius-small: 2px;
$--border-radius-round: 20px;
$--font-size-base: 14px;
$--font-size-medium: 16px;
$--font-size-small: 13px;
$--font-size-extra-small: 12px;
```
## 2. 全局 CSS 变量
```css
/* styles/variables.css */
:root {
/* ===== 左边导航栏 ===== */
--nav-width: 220px;
--nav-bg: #ffffff;
--nav-border: 1px solid #e4e7ed;
--nav-brand-height: 60px;
--nav-item-active-color: #1677ff;
--nav-item-active-bg: #ecf5ff;
--nav-item-hover-bg: #f5f7fa;
/* ===== 主内容区 ===== */
--content-padding: 24px;
--content-max-width: 1200px;
--content-bg: #f5f7fa;
/* ===== AI 侧栏 ===== */
--ai-panel-width: 360px;
--ai-panel-bg: #ffffff;
--ai-panel-border: 1px solid #e4e7ed;
/* ===== 消息气泡 ===== */
--bubble-user-bg: #1677ff;
--bubble-user-color: #ffffff;
--bubble-ai-bg: #f5f7fa;
--bubble-ai-color: #303133;
/* ===== 表格 ===== */
--table-stripe-bg: #fafafa;
--table-hover-bg: #f5f7fa;
/* ===== 阴影 ===== */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-base: 0 2px 8px rgba(0, 0, 0, 0.08);
--shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.12);
}
```
## 3. 统一页面容器样式
```css
/* 所有页面通用的容器样式 */
.page-container {
max-width: var(--content-max-width);
margin: 0 auto;
padding: 0 var(--content-padding);
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h2 {
font-size: 20px;
font-weight: 600;
color: #303133;
}
/* 筛选栏 */
.filter-bar {
display: flex;
gap: 12px;
align-items: center;
margin-bottom: 16px;
padding: 16px;
background: #fff;
border-radius: 4px;
border: 1px solid #e4e7ed;
}
/* 内容卡片 */
.content-card {
background: #fff;
border-radius: 4px;
border: 1px solid #e4e7ed;
padding: 20px;
margin-bottom: 16px;
}
```
## 4. 按钮状态规范
| 状态 | 样式 | 色值 |
|------|------|------|
| 可用(主按钮) | 蓝色底白字 | `#1677ff` / `#ffffff` |
| 可用(普通按钮) | 白底灰边框 | `#ffffff` / `#606266` / `#dcdfe6` |
| 可用(文字按钮) | 无底蓝字 | 透明 / `#1677ff` |
| 不可用(禁用) | 灰底灰字 + cursor not-allowed | `#cbd5e1` / `#64748b` |
| 危险(删除) | 红底白字 | `#ff4d4f` / `#ffffff` |
## 5. 主要组件样式
```css
/* ===== 左边导航栏 ===== */
.side-nav {
width: var(--nav-width);
min-width: var(--nav-width);
background: var(--nav-bg);
border-right: var(--nav-border);
display: flex;
flex-direction: column;
overflow-y: auto;
}
.side-nav__brand {
height: var(--nav-brand-height);
display: flex;
align-items: center;
padding: 0 20px;
border-bottom: var(--nav-border);
font-size: 18px;
font-weight: 700;
color: #1677ff;
}
.side-nav__menu {
flex: 1;
border-right: none !important;
}
.side-nav__menu .el-menu-item.is-active {
background-color: var(--nav-item-active-bg);
color: var(--nav-item-active-color);
border-right: 3px solid var(--nav-item-active-color);
}
.side-nav__footer {
padding: 12px 16px;
}
/* ===== AI 侧栏 ===== */
.ai-panel {
width: var(--ai-panel-width);
min-width: var(--ai-panel-width);
background: var(--ai-panel-bg);
border-left: var(--ai-panel-border);
display: flex;
flex-direction: column;
height: 100%;
}
.ai-header {
padding: 14px 16px;
border-bottom: var(--ai-panel-border);
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 600;
font-size: 15px;
}
.ai-messages {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.ai-messages .message {
margin-bottom: 12px;
}
.ai-messages .message.user {
text-align: right;
}
.ai-messages .message.user .bubble {
display: inline-block;
background: var(--bubble-user-bg);
color: var(--bubble-user-color);
border-radius: 8px 8px 0 8px;
padding: 8px 14px;
max-width: 80%;
text-align: left;
}
.ai-messages .message.assistant .bubble {
display: inline-block;
background: var(--bubble-ai-bg);
color: var(--bubble-ai-color);
border-radius: 8px 8px 8px 0;
padding: 8px 14px;
max-width: 80%;
}
.ai-input {
padding: 12px 16px;
border-top: var(--ai-panel-border);
display: flex;
gap: 8px;
}
.ai-quick-actions {
padding: 8px 16px 12px;
display: flex;
gap: 6px;
flex-wrap: wrap;
}
/* ===== 展开 AI 浮动按钮 ===== */
.ai-trigger {
position: fixed;
right: 0;
bottom: 80px;
z-index: 100;
padding: 8px 12px;
background: #1677ff;
color: #fff;
border-radius: 4px 0 0 4px;
cursor: pointer;
font-size: 13px;
}
```
@@ -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 → 真正安全边界
@@ -0,0 +1,113 @@
# DR03 — 交互规范
> **版本:V1.1 | 框架:Element Plus**
---
## 1. 按钮状态规范
| 场景 | 样式 | 色值 | 说明 |
|------|------|------|------|
| 主操作(提交/保存/开始) | `el-button type="primary"` | `#1677ff` | 页面中的主要行动点 |
| 次要操作(取消/返回) | `el-button`(默认) | `#ffffff` / `#606266` | 非主要行动点 |
| 危险操作(删除/驳回) | `el-button type="danger"` | `#ff4d4f` | 不可逆操作 |
| 文字操作(编辑/查看) | `el-button text` | `#1677ff` | 表格中操作列 |
| 禁用态 | `el-button disabled` | 灰底 `#cbd5e1` + 灰字 `#64748b` | 不可点击状态 |
| 快捷按钮(AI 侧栏) | `el-button size="small"` | 默认样式 | 3 个快捷入口 |
**关键规则(CODING_RULES 第 6 条):**
- 所有可点击的关键交互按钮必须使用蓝色底 `#1677ff`
- `disabled` 状态变化后,底色必须立即同步变化
- 不可用按钮保持 `cursor: not-allowed`
## 2. 加载与反馈
| 场景 | 组件 | 行为 |
|------|------|------|
| 页面加载 | `v-loading` | 全屏或区域加载遮罩 |
| 表单提交 | `el-button` loading 状态 | 按钮显示转圈,禁止重复点击 |
| 表格查询 | `el-table v-loading` | 表格区域加载 |
| 操作成功 | `ElMessage.success` | 顶部轻提示,2s 自动消失 |
| 操作失败 | `ElMessage.error` | 显示错误信息 |
| 确认操作 | `ElMessageBox.confirm` | 弹窗确认(删除/驳回等) |
## 3. 弹窗使用规范
| 类型 | 组件 | 场景 |
|------|------|------|
| 表单弹窗 | `el-dialog` | 新增/编辑 CRUD |
| 确认弹窗 | `el-message-box` | 删除/禁用确认 + 驳回理由输入 |
| 素材提交 | `el-dialog` | 员工提交素材建议(MaterialSuggestUpload) |
| 预览弹窗 | `el-dialog` + iframe | PDF 预览 / 视频播放 |
| 选择器弹窗 | `el-dialog` + `el-tree` | 绑定产品/课程选择 |
## 4. 表单交互规则
```javascript
// 通用规则
- 必填字段标红色 *
- 提交前校验(el-form :rules)
- 提交按钮 disable 直到表单合法
- 提交中按钮 loading,禁止重复点击
- 成功后关闭弹窗 + 刷新表格
- 失败后保持弹窗,显示错误信息
```
## 5. 表格交互规则
```javascript
// 通用规则
- 支持分页(el-pagination)
- 支持筛选栏(过滤条件)
- 操作列放最右侧,统一宽度
- 表格行 hover 高亮
- 长文本省略显示(show-overflow-tooltip)
- 空数据展示「暂无数据」
```
## 6. AI 聊天框交互规则
| 场景 | 行为 |
|------|------|
| 展开/收起 | 点击右上角 ✕ 收起,底部显示 [🤖 展开AI] 按钮 |
| 发送消息 | Enter 键或点击发送按钮 |
| 流式响应 | SSE 实时流式显示,打字机效果 |
| 上下文切换 | 切换页面时自动清理旧上下文,注入新页面上下文 |
| 快捷动作 | 点击快捷按钮触发对应的 API |
| 窗口切换 | 页面路由变化不清空对话历史(全局常驻) |
## 7. 错误处理交互
| HTTP 状态 | 用户看到 | 行为 |
|-----------|---------|------|
| 400 参数错误 | `ElMessage.warning` + 字段标红 | 表单校验错误 |
| 401 未认证 | 跳转到登录页 | 清除 token 并跳转 |
| 403 无权限 | `ElMessage.warning` + 跳转首页 | 前端+后端双重拦截 |
| 404 资源不存在 | `ElMessage.error` + 提示 | — |
| 500 服务器错误 | `ElMessage.error`「系统异常,请稍后重试」| — |
| 网络断开 | `ElMessage.error`「网络连接异常」| — |
## 8. 页面间跳转逻辑
| 来源页面 | 目标页面 | 传递参数 | 方式 |
|---------|---------|---------|------|
| 产品详情 | 对应课程 | `courseId` | `<router-link>` |
| 课程详情 | 对应产品 | `productId` | `<router-link>` |
| 产品详情 | 向 AI 提问 | `productId` + `page: "product_detail"` | 展开 AI 侧栏 + 自动注入 |
| 课程详情 | 向 AI 提问 | `courseId` + `page: "course_detail"` | 同上 |
| 考试列表 | 考试封面 | `paperId` | `router.push` |
| 考试封面 | 答题页面 | `paperId` | `router.push` |
| 考试结果 | 考试记录 | `recordId` | `router.push` |
## 9. 素材提交入口(弹窗而非独立页面)
```
素材提交仅在以下页面以弹窗形式出现:
- 公司介绍培训页面 → 点击「提交素材建议」
- 产品详情页面 → 点击「提交素材建议」
- 课程详情页面 → 点击「提交素材建议」
弹窗内容:
文件上传 + 备注说明
不提供独立页面访问
```
+15
View File
@@ -0,0 +1,15 @@
# 08_Design_Rules — UI/UX 设计规范
> **命名规则:** `DR{NN}_{描述}.md`
> **用途:** 前端 UI 规范、交互规则、主题系统
>
> **注:** 导航规则(DR02)需随平台一级导航演进(知识库/智能体/学习与考试/陪练与认证/内容运营/组织与系统)同步,见 SY03。
## 文件清单
| 文件 | 说明 |
|------|------|
| `README.md` | 本索引文件 |
| `DR01_Theme_System.md` | 主题系统(Element Plus 变量覆盖) |
| `DR02_Navigation_Rules.md` | 导航规则(菜单/权限显示/路由守卫) |
| `DR03_Interaction_Rules.md` | 交互规范(按钮状态/反馈/弹窗) |