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

22 KiB
Raw Blame History

工作日志服务平台 - 前端详细设计文档

版本号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 前端整体架构

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

  1. api 目录职责

    • 统一管理所有后端接口调用
    • 按业务模块拆分auth.ts、user.ts、log.ts、template.ts
    • 所有 API 函数从 @/api 统一导出
  2. 强制规范要求

    • 禁止硬编码:组件中禁止直接调用 request.get('/xxx/xxx')
    • 统一入口:所有 API 函数从 @/api 统一导出
    • 路径简化API 内部使用简化路径,由 baseURL 或拦截器统一添加前缀
    • 便于维护:后端接口变更时,只需修改 API 定义文件
  3. 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}`)
}
  1. 组件使用示例
// 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

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
  • 提供:编辑区 + 预览区
  • PropsmodelValuemaxLength
  • Emitsupdate:modelValue
  • 在日志编辑、模板编辑中复用

2.6.2 Pagination.vue

  • 封装 Element Plus 的分页组件
  • 接收:totalpageNumpageSize
  • Emitsupdate:pageNumupdate: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 管理规范:

  1. api 目录职责

    • 统一管理所有后端接口调用
    • 按业务模块拆分auth.ts、log.ts、template.ts
    • 所有 API 函数从 @/api 统一导出
  2. 强制规范要求(与管理后台一致):

    • 禁止硬编码:组件中禁止直接调用 request.get('/xxx/xxx')
    • 统一入口:所有 API 函数从 @/api 统一导出
    • 路径简化API 内部使用简化路径,由 baseURL 统一添加前缀
    • 便于维护:后端接口变更时,只需修改 API 定义文件
  3. 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}`)
}
  1. 组件使用示例
// 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 的 FormFieldButton
  • 登录成功后跳转 /home

3.3.2 首页Home.vue

  • 展示常用入口:
    • 写日志
    • 我的日志
    • 模板说明(可选)
  • 可展示近期日志摘要或提醒(例如“今日未写日志”提示)。

3.3.3 我的日志列表LogList.vue

  • 显示当前登录用户的日志列表
  • 支持按日期筛选、按关键字搜索
  • 列表项展示:日期、内容摘要、记录时间
  • 向下滚动分页加载(下拉刷新 + 上拉加载更多)

3.3.4 日志编辑页LogEdit.vue

  • 字段:日志日期、模板选择、内容编辑
  • 日志日期:默认当天,可修改为过去日期
  • 模板选择:使用 Vant 的 PickerActionSheet 展示模板列表
  • 内容编辑:采用简化版 Markdown 编辑器或富文本输入框
  • 底部悬浮提交按钮
3.3.4.1 新增/编辑日志表单设计LogEdit.vue
  • 表单字段定义
字段 显示名称 类型 是否必填 说明
logDate 日志日期 日期选择器 默认当前日期,允许选择历史日期
templateId 使用模板 下拉/弹窗选择 选填,选择后自动填充内容(用户仍可修改)
content 日志内容 多行文本/Markdown 编辑器 最多 2000 汉字,前后端双重校验
  • 提交规则
    1. logDatecontent必填字段,提交前需通过校验。
    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 = 400message 类似“账号已存在,请更换后再试”。
    • 前端处理策略
      • 新增用户或在编辑状态下修改 username 时,不做全量拉取检查,仅在提交后根据后台返回的 code/message 判断是否为重复账号;
      • 若检测到“账号已存在”类错误,将错误提示绑定到 username 字段下方,并保持表单数据不丢失。
  • 模板新增/修改Template

    • 唯一性依据templateName(模板名称)在系统内必须唯一。
    • 后端实现约定
      • 数据库层对 template_name 建立唯一索引;
      • Service 层在创建模板前校验名称是否已存在;
      • 在修改模板时,如变更了 templateName,也需进行相同的唯一性校验;
      • 若重复,返回 code = 400message 类似“模板名称已存在”。
    • 前端处理策略
      • 在 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:服务器内部错误