# 工作日志服务平台 - 前端详细设计文档 **版本号**: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` 的统一返回结构 - 鉴权方式:Token(Bearer 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>('/user/page', { params }), // 创建用户 create: (data: UserCreateDTO) => request.post('/user', data), // 更新用户 update: (id: string, data: UserUpdateDTO) => request.put(`/user/${id}`, data), // 删除用户 delete: (id: string) => request.delete(`/user/${id}`) } ``` 4. **组件使用示例**: ```typescript // views/user/UserList.vue ``` ### 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` 结构 - 当 `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>('/log/page', { params }), // 创建日志 create: (data: LogCreateDTO) => request.post('/log', data), // 查看日志详情 detail: (id: string) => request.get(`/log/${id}`), // 更新日志 update: (id: string, data: LogUpdateDTO) => request.put(`/log/${id}`, data), // 删除日志 delete: (id: string) => request.delete(`/log/${id}`) } ``` 4. **组件使用示例**: ```typescript // views/log/LogList.vue ``` ### 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` 结构:`{ code, message, data, success }` - 分页接口返回 `PageResult` 结构:`{ pageNum, pageSize, total, list }` - 认证接口约定:登录成功后,后端返回 Token 与用户信息,由前端存储并在后续请求中附加 `Authorization` 头 - 错误码约定: - `200`:成功 - `400`:参数错误(前端需校验并提示) - `401`:未授权/Token 无效(前端应跳转登录) - `403`:权限不足(前端提示“无权操作”) - `500`:服务器内部错误