- 项目文档:PRD、SRS、架构设计文档、前后端详细设计文档、架构设计规范 - 数据库脚本:用户创建和数据库初始化脚本 - 后端框架:Spring Boot 3.2 + MyBatis-Plus 3.5 基础架构 - 公共组件:统一返回结果、分页封装、全局异常处理 - 基础功能:链路追踪、API日志、健康检查接口 - 配置文件:application.yml.example 配置模板 - 开发规范:遵循架构设计规范,data目录存放MyBatis-Plus文件
22 KiB
22 KiB
工作日志服务平台 - 前端详细设计文档
版本号: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>的统一返回结构 - 鉴权方式:Token(Bearer Token),存储于内存 + LocalStorage
- 接口前缀:
/api/v1(通过 Vite 代理转发到后端)
- 接口协议:基于
1.3 UI 主题与配色
- 整体色调:以浅色主题为主(Light Theme),背景以白色或近白色为主色,辅以浅灰分隔线。
- 主色 Primary:建议使用偏蓝或偏青的浅色系(例如
#409EFF或同级亮度色),用于主按钮、选中态、链接等高亮元素。 - 辅色 Secondary:用于标签、次级按钮和提示信息,可选用与主色相近的浅色系或中性灰。
- 文字颜色:
- 标题文字:深灰或近黑色(例如
#303133),保证可读性。 - 正文文字:中等深度灰(例如
#606266)。 - 辅助文字(说明、占位):浅灰(例如
#909399)。
- 标题文字:深灰或近黑色(例如
- 组件风格:
- 管理后台沿用 Element Plus 默认浅色主题,根据主色配置
primary-color。 - 移动端 H5 沿用 Vant 默认浅色主题,根据主色配置按钮、导航栏等组件颜色。
- 管理后台沿用 Element Plus 默认浅色主题,根据主色配置
- 暗色模式:当前版本不强制支持暗色模式,如后续有需要,将新增独立的小节进行说明。
1.2 前端整体架构
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 目录结构设计
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:
-
api 目录职责:
- 统一管理所有后端接口调用
- 按业务模块拆分(auth.ts、user.ts、log.ts、template.ts)
- 所有 API 函数从
@/api统一导出
-
强制规范要求:
- ❌ 禁止硬编码:组件中禁止直接调用
request.get('/xxx/xxx') - ✅ 统一入口:所有 API 函数从
@/api统一导出 - ✅ 路径简化:API 内部使用简化路径,由
baseURL或拦截器统一添加前缀 - ✅ 便于维护:后端接口变更时,只需修改 API 定义文件
- ❌ 禁止硬编码:组件中禁止直接调用
-
API 定义示例:
// 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}`)
}
- 组件使用示例:
// 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 路由结构
// 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
- 若无 Token,则重定向到
2.3 状态管理设计(Pinia)
2.3.1 用户状态 store/user.ts
- 状态字段:
token:当前用户 TokenuserInfo:{ id, username, name, role }
- 动作:
login(payload):调用authApi.login,保存 token 与 userInfologout():清空本地状态和 LocalStoragefetchUserInfo():根据 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}
- 自动在 Header 注入
- 响应拦截器:
- 统一处理后端
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)
- 登录按钮
- 交互逻辑:
- 用户输入账号和密码
- 点击登录时进行前端必填校验
- 调用
authApi.login接口 - 成功后保存 token 与用户信息,跳转到首页
2.5.2 人员管理列表页(UserList.vue)
- 功能:分页展示人员列表,支持搜索、启用/禁用、编辑、删除
- 筛选条件:
- 姓名(模糊)
- 职位(下拉/文本)
- 状态(启用/禁用)
- 表格字段:姓名、账号、职位、状态、创建时间、操作
- 操作按钮:
- 新增人员(打开
UserForm弹窗) - 编辑(弹出编辑表单)
- 启用/禁用(确认弹窗)
- 删除(确认弹窗,逻辑删除)
- 新增人员(打开
2.5.2.1 新增人员表单设计(UserForm.vue)
- 表单字段定义:
| 字段 | 显示名称 | 类型 | 是否必填 | 说明 |
|---|---|---|---|---|
| username | 登录账号 | 输入框 | 是 | 全局唯一,只能包含字母、数字、部分符号(具体正则可配置) |
| password | 初始密码 | 密码框 | 是 | 前端不做复杂度校验,后端做统一校验;仅在新增时显示 |
| name | 姓名 | 输入框 | 是 | 方便在列表和日志中展示 |
| role | 角色 | 下拉框 | 是 | 可选值:USER(普通用户)、ADMIN(管理员) |
| phone | 联系方式 | 输入框 | 否 | 选填,手机号或座机号,用于联系 |
| 电子邮箱 | 输入框 | 否 | 需通过基本邮箱格式校验 | |
| position | 职位 | 输入框/下拉 | 否 | 如“研发工程师”、“产品经理”等 |
| description | 描述 | 多行文本 | 否 | 该用户的备注信息 |
- 提交规则:
- 点击“保存”时触发表单校验,所有必填字段必须通过校验。
- 若是新增模式:调用
createUserAPI;编辑模式:调用updateUserAPI。 - 保存成功后关闭弹窗并刷新人员列表。
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-启用,新增时默认启用 |
- 提交规则:
- 模板名称与模板内容为必填字段,前端和后端均需校验非空。
- 模板名称建议在前端做简单重复校验(可选),后端做最终唯一性校验。
- 新增成功后返回模板列表;编辑成功后保持在当前页或返回列表(可配置)。
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 目录结构设计
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 管理规范:
-
api 目录职责:
- 统一管理所有后端接口调用
- 按业务模块拆分(auth.ts、log.ts、template.ts)
- 所有 API 函数从
@/api统一导出
-
强制规范要求(与管理后台一致):
- ❌ 禁止硬编码:组件中禁止直接调用
request.get('/xxx/xxx') - ✅ 统一入口:所有 API 函数从
@/api统一导出 - ✅ 路径简化:API 内部使用简化路径,由
baseURL统一添加前缀 - ✅ 便于维护:后端接口变更时,只需修改 API 定义文件
- ❌ 禁止硬编码:组件中禁止直接调用
-
API 定义示例:
// 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}`)
}
- 组件使用示例:
// 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 汉字,前后端双重校验 |
- 提交规则:
logDate与content为必填字段,提交前需通过校验。- 内容长度校验:前端统计字符数,超过 2000 字给出提示并阻止提交;后端再做最终校验。
- 新增:调用
createLog接口;编辑:调用updateLog接口。 - 保存成功后返回日志列表或详情页,并展示“保存成功”提示。
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:服务器内部错误