- 项目文档:PRD、SRS、架构设计文档、前后端详细设计文档、架构设计规范 - 数据库脚本:用户创建和数据库初始化脚本 - 后端框架:Spring Boot 3.2 + MyBatis-Plus 3.5 基础架构 - 公共组件:统一返回结果、分页封装、全局异常处理 - 基础功能:链路追踪、API日志、健康检查接口 - 配置文件:application.yml.example 配置模板 - 开发规范:遵循架构设计规范,data目录存放MyBatis-Plus文件
668 lines
22 KiB
Markdown
668 lines
22 KiB
Markdown
# 工作日志服务平台 - 前端详细设计文档
|
||
|
||
**版本号**: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 默认浅色主题,根据主色配置按钮、导航栏等组件颜色。
|
||
- **暗色模式**:当前版本不强制支持暗色模式,如后续有需要,将新增独立的小节进行说明。
|
||
|
||
### 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`:服务器内部错误
|