worklog/doc/前端详细设计文档.md
zhangjf ae33bd4d6a feat: 初始化工作日志服务平台项目
- 项目文档:PRD、SRS、架构设计文档、前后端详细设计文档、架构设计规范
- 数据库脚本:用户创建和数据库初始化脚本
- 后端框架:Spring Boot 3.2 + MyBatis-Plus 3.5 基础架构
- 公共组件:统一返回结果、分页封装、全局异常处理
- 基础功能:链路追踪、API日志、健康检查接口
- 配置文件:application.yml.example 配置模板
- 开发规范:遵循架构设计规范,data目录存放MyBatis-Plus文件
2026-02-24 14:47:26 +08:00

668 lines
22 KiB
Markdown
Raw Permalink 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.

# 工作日志服务平台 - 前端详细设计文档
**版本号**V1.0
**编写日期**2026-02-24
**文档状态**:初稿
**适用范围**管理后台Web与移动端 H5 前端实现
---
## 1. 前端总体设计
### 1.1 技术栈
- **管理后台Web**
- 框架Vue 3 + TypeScript
- 构建工具Vite 5
- UI 库Element Plus
- 状态管理Pinia
- 路由Vue Router 4
- **移动端 H5**
- 框架Vue 3 + TypeScript
- 构建工具Vite 5
- UI 库Vant 4
- 状态管理Pinia
- 路由Vue Router 4
- **公共约定**
- 接口协议:基于 `Result<T>` 的统一返回结构
- 鉴权方式TokenBearer Token存储于内存 + LocalStorage
- 接口前缀:`/api/v1`(通过 Vite 代理转发到后端)
### 1.3 UI 主题与配色
- **整体色调**以浅色主题为主Light Theme背景以白色或近白色为主色辅以浅灰分隔线。
- **主色 Primary**:建议使用偏蓝或偏青的浅色系(例如 `#409EFF` 或同级亮度色),用于主按钮、选中态、链接等高亮元素。
- **辅色 Secondary**:用于标签、次级按钮和提示信息,可选用与主色相近的浅色系或中性灰。
- **文字颜色**
- 标题文字:深灰或近黑色(例如 `#303133`),保证可读性。
- 正文文字:中等深度灰(例如 `#606266`)。
- 辅助文字(说明、占位):浅灰(例如 `#909399`)。
- **组件风格**
- 管理后台沿用 Element Plus 默认浅色主题,根据主色配置 `primary-color`
- 移动端 H5 沿用 Vant 默认浅色主题,根据主色配置按钮、导航栏等组件颜色。
- **暗色模式**:当前版本不强制支持暗色模式,如后续有需要,将新增独立的小节进行说明。
### 1.2 前端整体架构
```mermaid
graph TB
subgraph Admin[管理后台]
A1[路由 Router]
A2[状态 Store]
A3[视图 Views]
A4[组件 Components]
A5[API 封装]
end
subgraph Mobile[移动端 H5]
M1[路由 Router]
M2[状态 Store]
M3[视图 Views]
M4[组件 Components]
M5[API 封装]
end
A3 --> A5
A4 --> A5
M3 --> M5
M4 --> M5
```
- 前后端通过统一的 API 封装层交互,前端不直接拼接 URL 字符串。
- 管理后台与移动端的页面路由、状态管理相互独立,但可复用类型定义和部分工具函数。
---
## 2. 管理后台详细设计Admin Web
### 2.1 目录结构设计
```text
worklog-admin/
├── src/
│ ├── main.ts
│ ├── App.vue
│ ├── router/
│ │ └── index.ts # 路由定义
│ ├── store/
│ │ ├── index.ts # Pinia 创建
│ │ ├── user.ts # 用户与权限状态
│ │ └── layout.ts # 布局相关状态
│ ├── api/
│ │ ├── index.ts # 统一导出
│ │ ├── request.ts # Axios 实例与拦截器
│ │ ├── auth.ts # 认证相关 API
│ │ ├── user.ts # 人员管理 API
│ │ ├── log.ts # 日志管理 API
│ │ └── template.ts # 模板管理 API
│ ├── views/
│ │ ├── login/
│ │ │ └── Login.vue
│ │ ├── layout/
│ │ │ └── Layout.vue
│ │ ├── user/
│ │ │ ├── UserList.vue
│ │ │ └── UserForm.vue
│ │ ├── log/
│ │ │ ├── LogList.vue
│ │ │ ├── LogDetail.vue
│ │ │ └── LogEdit.vue
│ │ └── template/
│ │ ├── TemplateList.vue
│ │ └── TemplateForm.vue
│ ├── components/
│ │ ├── SearchBar.vue
│ │ ├── Pagination.vue
│ │ └── MarkdownEditor.vue
│ ├── types/
│ │ ├── api.d.ts # 接口类型定义
│ │ └── models.d.ts # 领域模型类型定义
│ └── utils/
│ ├── auth.ts # Token 工具
│ ├── request-helpers.ts # 请求工具
│ └── validators.ts # 校验工具
└── vite.config.ts
```
**目录结构说明**
按照架构设计规范,前端项目应采用独立目录或文件集中管理与后端交互的 API避免在页面组件中硬编码 URL
1. **api 目录职责**
- 统一管理所有后端接口调用
- 按业务模块拆分auth.ts、user.ts、log.ts、template.ts
- 所有 API 函数从 `@/api` 统一导出
2. **强制规范要求**
-**禁止硬编码**:组件中禁止直接调用 `request.get('/xxx/xxx')`
-**统一入口**:所有 API 函数从 `@/api` 统一导出
-**路径简化**API 内部使用简化路径,由 `baseURL` 或拦截器统一添加前缀
-**便于维护**:后端接口变更时,只需修改 API 定义文件
3. **API 定义示例**
```typescript
// api/user.ts
import request from './request'
/**
* 用户管理相关 API
*/
export const userApi = {
// 分页查询用户列表
page: (params: UserPageParams) =>
request.get<PageResult<UserVO>>('/user/page', { params }),
// 创建用户
create: (data: UserCreateDTO) =>
request.post<string>('/user', data),
// 更新用户
update: (id: string, data: UserUpdateDTO) =>
request.put<void>(`/user/${id}`, data),
// 删除用户
delete: (id: string) =>
request.delete<void>(`/user/${id}`)
}
```
4. **组件使用示例**
```typescript
// views/user/UserList.vue
<script setup lang="ts">
import { userApi } from '@/api'
// ✅ 正确:使用 API 封装
const loadData = async () => {
const res = await userApi.page({ pageNum: 1, pageSize: 10 })
// ...
}
// ❌ 错误:硬编码 URL
// const loadData = async () => {
// const res = await request.get('/api/v1/user/page')
// }
</script>
```
### 2.2 路由设计
#### 2.2.1 路由结构
```ts
// router/index.ts简化示意
const routes: RouteRecordRaw[] = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/login/Login.vue'),
meta: { public: true }
},
{
path: '/',
component: () => import('@/views/layout/Layout.vue'),
children: [
{ path: '', redirect: '/dashboard' },
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/Dashboard.vue'),
meta: { title: '首页' }
},
{
path: '/user',
name: 'UserList',
component: () => import('@/views/user/UserList.vue'),
meta: { title: '人员管理', requiresAdmin: true }
},
{
path: '/template',
name: 'TemplateList',
component: () => import('@/views/template/TemplateList.vue'),
meta: { title: '模板管理', requiresAdmin: true }
},
{
path: '/log',
name: 'LogList',
component: () => import('@/views/log/LogList.vue'),
meta: { title: '日志管理' }
},
{
path: '/log/:id',
name: 'LogDetail',
component: () => import('@/views/log/LogDetail.vue'),
meta: { title: '日志详情' }
}
]
}
]
```
#### 2.2.2 路由守卫
- 检查 `meta.public`:公共路由(如登录页)不需 Token
- 对其他路由:
- 若无 Token则重定向到 `/login`
-`requiresAdmin: true`,则检查用户角色是否为 `ADMIN`
### 2.3 状态管理设计Pinia
#### 2.3.1 用户状态 store/user.ts
- 状态字段:
- `token`:当前用户 Token
- `userInfo``{ id, username, name, role }`
- 动作:
- `login(payload)`:调用 `authApi.login`,保存 token 与 userInfo
- `logout()`:清空本地状态和 LocalStorage
- `fetchUserInfo()`:根据 token 获取最新用户信息(可选)
#### 2.3.2 布局状态 store/layout.ts
- 记录侧边栏折叠状态、当前菜单 key 等 UI 状态
### 2.4 API 封装设计
#### 2.4.1 Axios 实例api/request.ts
- 统一设置 `baseURL`(通过 Vite 代理到 `/api`
- 请求拦截器:
- 自动在 Header 注入 `Authorization: Bearer {token}`
- 响应拦截器:
- 统一处理后端 `Result<T>` 结构
-`code == 401` 时,触发登出并跳转登录页
#### 2.4.2 API 模块
- `auth.ts`:登录、登出
- `user.ts`
- `pageUsers(params)`
- `createUser(data)`
- `updateUser(id, data)`
- `updateUserStatus(id, status)`
- `deleteUser(id)`
- `template.ts`
- `listTemplates(params)`
- `createTemplate(data)`
- `updateTemplate(id, data)`
- `deleteTemplate(id)`
- `log.ts`
- `pageLogs(params)`
- `getLogDetail(id)`
- `createLog(data)`
- `updateLog(id, data)`
- `deleteLog(id)`
### 2.5 页面详细设计
#### 2.5.1 登录页Login.vue
- **功能**:用户登录系统,获取 Token
- **主要元素**
- 账号输入框username
- 密码输入框password
- 登录按钮
- **交互逻辑**
1. 用户输入账号和密码
2. 点击登录时进行前端必填校验
3. 调用 `authApi.login` 接口
4. 成功后保存 token 与用户信息,跳转到首页
#### 2.5.2 人员管理列表页UserList.vue
- **功能**:分页展示人员列表,支持搜索、启用/禁用、编辑、删除
- **筛选条件**
- 姓名(模糊)
- 职位(下拉/文本)
- 状态(启用/禁用)
- **表格字段**:姓名、账号、职位、状态、创建时间、操作
- **操作按钮**
- 新增人员(打开 `UserForm` 弹窗)
- 编辑(弹出编辑表单)
- 启用/禁用(确认弹窗)
- 删除(确认弹窗,逻辑删除)
##### 2.5.2.1 新增人员表单设计UserForm.vue
- **表单字段定义**
| 字段 | 显示名称 | 类型 | 是否必填 | 说明 |
|------|----------|------|----------|------|
| username | 登录账号 | 输入框 | 是 | 全局唯一,只能包含字母、数字、部分符号(具体正则可配置) |
| password | 初始密码 | 密码框 | 是 | 前端不做复杂度校验,后端做统一校验;仅在新增时显示 |
| name | 姓名 | 输入框 | 是 | 方便在列表和日志中展示 |
| role | 角色 | 下拉框 | 是 | 可选值:`USER`(普通用户)、`ADMIN`(管理员) |
| phone | 联系方式 | 输入框 | 否 | 选填,手机号或座机号,用于联系 |
| email | 电子邮箱 | 输入框 | 否 | 需通过基本邮箱格式校验 |
| position | 职位 | 输入框/下拉 | 否 | 如“研发工程师”、“产品经理”等 |
| description | 描述 | 多行文本 | 否 | 该用户的备注信息 |
- **提交规则**
1. 点击“保存”时触发表单校验,所有**必填字段**必须通过校验。
2. 若是新增模式:调用 `createUser` API编辑模式调用 `updateUser` API。
3. 保存成功后关闭弹窗并刷新人员列表。
#### 2.5.3 日志管理列表页LogList.vue
- **功能**
- 管理员:查看全员日志
- 普通用户:仅查看本人日志
- **筛选条件**
- 日期范围(起止)
- 记录人(管理员可选)
- 关键字(按内容模糊搜索)
- **表格字段**:日期、记录人、内容摘要(前 50 字)、使用模板、记录时间、操作
- **操作**:查看详情、删除
#### 2.5.4 日志详情页LogDetail.vue
- 展示完整日志内容Markdown 渲染为 HTML
- 展示模板名称、记录人、记录时间
#### 2.5.5 模板列表与编辑页
- 模板列表:展示模板名称、状态、更新时间
- 新建/编辑模板:
- 使用 `MarkdownEditor` 组件
- 填写模板名称、内容、使用说明
##### 2.5.5.1 新增/编辑模板表单设计TemplateForm.vue
- **表单字段定义**
| 字段 | 显示名称 | 类型 | 是否必填 | 说明 |
|------|----------|------|----------|------|
| templateName | 模板名称 | 输入框 | 是 | 在系统内唯一,用于列表展示和选择 |
| templateContent | 模板内容 | Markdown 编辑器 | 是 | 预设日志内容结构,支持 Markdown 语法 |
| instruction | 使用说明 | 多行文本 | 否 | 说明该模板适用场景及填写注意事项 |
| status | 状态 | 开关/单选 | 否(编辑时可选) | 0-禁用1-启用,新增时默认启用 |
- **提交规则**
1. 模板名称与模板内容为**必填字段**,前端和后端均需校验非空。
2. 模板名称建议在前端做简单重复校验(可选),后端做最终唯一性校验。
3. 新增成功后返回模板列表;编辑成功后保持在当前页或返回列表(可配置)。
### 2.6 组件设计
#### 2.6.1 MarkdownEditor.vue
- 基于开源 Markdown 编辑器封装(如 `@kangc/v-md-editor`
- 提供:编辑区 + 预览区
- Props`modelValue``maxLength`
- Emits`update:modelValue`
- 在日志编辑、模板编辑中复用
#### 2.6.2 Pagination.vue
- 封装 Element Plus 的分页组件
- 接收:`total``pageNum``pageSize`
- Emits`update:pageNum``update:pageSize`
---
## 3. 移动端 H5 详细设计Mobile H5
### 3.1 目录结构设计
```text
worklog-mobile/
├── src/
│ ├── main.ts
│ ├── App.vue
│ ├── router/
│ │ └── index.ts
│ ├── store/
│ │ ├── index.ts
│ │ └── user.ts
│ ├── api/
│ │ ├── index.ts
│ │ ├── request.ts
│ │ ├── auth.ts
│ │ ├── log.ts
│ │ └── template.ts
│ ├── views/
│ │ ├── login/Login.vue
│ │ ├── home/Home.vue
│ │ ├── log/LogList.vue
│ │ ├── log/LogEdit.vue
│ │ └── log/LogDetail.vue
│ ├── components/
│ │ ├── MobileNavBar.vue
│ │ └── MobileMarkdownEditor.vue
│ ├── types/
│ └── utils/
└── vite.config.ts
```
**目录结构说明**
移动端 H5 项目同样遵循架构设计规范的 API 管理规范:
1. **api 目录职责**
- 统一管理所有后端接口调用
- 按业务模块拆分auth.ts、log.ts、template.ts
- 所有 API 函数从 `@/api` 统一导出
2. **强制规范要求**(与管理后台一致):
-**禁止硬编码**:组件中禁止直接调用 `request.get('/xxx/xxx')`
-**统一入口**:所有 API 函数从 `@/api` 统一导出
-**路径简化**API 内部使用简化路径,由 `baseURL` 统一添加前缀
-**便于维护**:后端接口变更时,只需修改 API 定义文件
3. **API 定义示例**
```typescript
// api/log.ts
import request from './request'
/**
* 日志管理相关 API
*/
export const logApi = {
// 分页查询日志列表
page: (params: LogPageParams) =>
request.get<PageResult<LogVO>>('/log/page', { params }),
// 创建日志
create: (data: LogCreateDTO) =>
request.post<string>('/log', data),
// 查看日志详情
detail: (id: string) =>
request.get<LogDetailVO>(`/log/${id}`),
// 更新日志
update: (id: string, data: LogUpdateDTO) =>
request.put<void>(`/log/${id}`, data),
// 删除日志
delete: (id: string) =>
request.delete<void>(`/log/${id}`)
}
```
4. **组件使用示例**
```typescript
// views/log/LogList.vue
<script setup lang="ts">
import { logApi } from '@/api'
// ✅ 正确:使用 API 封装
const loadData = async () => {
const res = await logApi.page({
pageNum: pageNum.value,
pageSize: 10
})
// ...
}
// ❌ 错误:硬编码 URL
// const loadData = async () => {
// const res = await request.get('/api/v1/log/page')
// }
</script>
```
### 3.2 路由设计
- `/login`:登录页
- `/home`:首页(入口导航)
- `/log/list`:我的日志列表
- `/log/edit`:新建日志
- `/log/edit/:id`:编辑已有日志
- `/log/detail/:id`:日志详情
路由守卫逻辑与管理后台类似:无 Token 时跳转 `/login`
### 3.3 页面详细设计
#### 3.3.1 移动端登录页
- UI 使用 Vant 的 `Form``Field``Button`
- 登录成功后跳转 `/home`
#### 3.3.2 首页Home.vue
- 展示常用入口:
- 写日志
- 我的日志
- 模板说明(可选)
- 可展示近期日志摘要或提醒(例如“今日未写日志”提示)。
#### 3.3.3 我的日志列表LogList.vue
- 显示当前登录用户的日志列表
- 支持按日期筛选、按关键字搜索
- 列表项展示:日期、内容摘要、记录时间
- 向下滚动分页加载(下拉刷新 + 上拉加载更多)
#### 3.3.4 日志编辑页LogEdit.vue
- 字段:日志日期、模板选择、内容编辑
- **日志日期**:默认当天,可修改为过去日期
- **模板选择**:使用 Vant 的 `Picker``ActionSheet` 展示模板列表
- **内容编辑**:采用简化版 Markdown 编辑器或富文本输入框
- 底部悬浮提交按钮
##### 3.3.4.1 新增/编辑日志表单设计LogEdit.vue
- **表单字段定义**
| 字段 | 显示名称 | 类型 | 是否必填 | 说明 |
|------|----------|------|----------|------|
| logDate | 日志日期 | 日期选择器 | 是 | 默认当前日期,允许选择历史日期 |
| templateId | 使用模板 | 下拉/弹窗选择 | 否 | 选填,选择后自动填充内容(用户仍可修改) |
| content | 日志内容 | 多行文本/Markdown 编辑器 | 是 | 最多 2000 汉字,前后端双重校验 |
- **提交规则**
1. `logDate``content` 为**必填字段**,提交前需通过校验。
2. 内容长度校验:前端统计字符数,超过 2000 字给出提示并阻止提交;后端再做最终校验。
3. 新增:调用 `createLog` 接口;编辑:调用 `updateLog` 接口。
4. 保存成功后返回日志列表或详情页,并展示“保存成功”提示。
#### 3.3.5 日志详情页LogDetail.vue
- 展示完整日志内容Markdown 渲染)
- 显示日期、记录时间、模板名称
- 提供“编辑”按钮(仅本人日志)
### 3.4 交互与体验设计
- 登录成功后缓存 Token 于 LocalStorage并同步到 Pinia
- 在网络不佳时,日志编辑页可支持“本地草稿”能力(可后续迭代):
- 提交失败时将内容缓存到 LocalStorage
- 下次进入编辑页时提示恢复草稿
- 列表场景下,优先展示最近 30 天日志,并提供更早数据的加载入口
### 3.5 API 与数据复用策略
- 移动端与管理后台共享同一套后端 API 定义
- 移动端的 `api` 目录在类型定义上可与管理后台共享(通过独立的 `@worklog/api-types` 内部包或复制类型文件)
---
## 4. 前端校验与错误处理
### 4.1 表单校验
- 使用 Element Plus / Vant 的表单校验能力结合自定义规则:
- 登录:账号、密码必填
- 人员新增:账号、姓名、初始角色必填
- 日志:日期必填,内容必填且不超过 2000 字
- 模板:模板名称和内容必填
### 4.2 错误提示与反馈
- 业务错误(如参数错误、权限不足)
- 统一使用后端返回的 `message` 字段,在前端展示 Message/Toast
- 系统异常(网络错误、服务器错误)
- 显示通用错误提示,例如“系统异常,请稍后重试”
- 表单提交成功
- Message/Toast 提示“保存成功”,并按场景返回列表或停留当前页
### 4.3 新增/修改重复数据检测逻辑
> 本小节说明“新增/修改”场景下,前端识别和处理**重复数据**的依据,所有最终校验以后端为准。
- **人员新增/修改User**
- **唯一性依据**`username`(登录账号)字段在系统内必须唯一。
- **后端实现约定**
- 数据库层对 `username` 建立唯一索引;
- Service 层在创建用户前先查询是否已存在相同 `username`
- 在修改用户时,如修改了 `username`,也需进行相同的唯一性校验;
- 若重复,返回 `code = 400``message` 类似“账号已存在,请更换后再试”。
- **前端处理策略**
- 新增用户或在编辑状态下修改 `username` 时,不做全量拉取检查,仅在提交后根据后台返回的 `code`/`message` 判断是否为重复账号;
- 若检测到“账号已存在”类错误,将错误提示绑定到 `username` 字段下方,并保持表单数据不丢失。
- **模板新增/修改Template**
- **唯一性依据**`templateName`(模板名称)在系统内必须唯一。
- **后端实现约定**
- 数据库层对 `template_name` 建立唯一索引;
- Service 层在创建模板前校验名称是否已存在;
- 在修改模板时,如变更了 `templateName`,也需进行相同的唯一性校验;
- 若重复,返回 `code = 400``message` 类似“模板名称已存在”。
- **前端处理策略**
- 在 TemplateForm 中,模板名称为必填项,新增或编辑(名称变更)提交后根据后端返回的错误信息判断是否名称重复;
- 若为重复名称错误,则在 `templateName` 字段下显示明确提示,不清空用户已输入的其他字段;
- 可选增强:在失去焦点时调用后端“名称可用性检查”接口做即时反馈(如后续迭代增加此类接口)。
- **其他新增场景(如日志新增)**
- 当前版本不对日志内容或日期做“重复日志”判定;
- 允许同一用户在同一天写多条日志;
- 如后续需要增加“同一天仅一条日志”或“内容完全相同视为重复”的规则,将在 PRD/SRS 中补充后,再同步更新本设计。
---
## 5. 安全与日志
### 5.1 安全考虑
- 不在前端长时间持久化敏感信息(只保存 Token 和必要的用户基础信息)
- 对日志内容进行 XSS 过滤后再渲染(后端或前端统一处理)
- 对管理后台的关键操作(删除用户、删除日志等)增加二次确认
### 5.2 前端日志
- 重要前端错误(如接口异常、未捕获异常)可上报到后端监控接口(可选)
- 在开发环境启用详细日志,在生产环境关闭 console 输出
---
## 6. 与后端接口的协作约定
- 所有接口均返回 `Result<T>` 结构:`{ code, message, data, success }`
- 分页接口返回 `PageResult<T>` 结构:`{ pageNum, pageSize, total, list }`
- 认证接口约定:登录成功后,后端返回 Token 与用户信息,由前端存储并在后续请求中附加 `Authorization`
- 错误码约定:
- `200`:成功
- `400`:参数错误(前端需校验并提示)
- `401`:未授权/Token 无效(前端应跳转登录)
- `403`:权限不足(前端提示“无权操作”)
- `500`:服务器内部错误