feat: 顶部通知铃铛与站内通知中心
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m7s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m7s
This commit is contained in:
70
openspec/changes/add-notification-center/design.md
Normal file
70
openspec/changes/add-notification-center/design.md
Normal file
@@ -0,0 +1,70 @@
|
||||
## Context
|
||||
|
||||
系统已经存在 `ArtNotification` 顶部通知组件,但组件中的通知、消息和待办列表都是静态数据,顶部通知按钮处于注释状态。新的通知能力需要同时服务顶部快速查看和完整通知中心,并确保用户在任一入口执行已读操作后未读数与列表状态一致。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
- Goals: 提供余额预警、临期提醒、审批结果和系统告警统一的站内通知入口。
|
||||
- Goals: 在顶部快速查看最近 10 条通知,并提供进入通知中心的入口。
|
||||
- Goals: 支持通知分类、类型、严重级别和已读状态筛选。
|
||||
- Goals: 将通知点击导航限制在前端认可的业务目标内,禁止任意 URL 跳转。
|
||||
- Goals: 单条已读、全部已读、抽屉和通知中心共享同一份未读状态。
|
||||
- Non-Goals: 不实现 C 端通知页面。
|
||||
- Non-Goals: 不实现推送通道或服务端通知生成规则。
|
||||
- Non-Goals: 不让前端根据通知正文推断跳转地址。
|
||||
|
||||
## Decisions
|
||||
|
||||
- Decision: 通知铃铛放在顶部全局导航的设置按钮和用户头像菜单附近。
|
||||
- Rationale: 该区域已承载全局设置和用户级入口,适合放置跨页面可访问的通知入口,也符合产品指定位置。
|
||||
|
||||
- Decision: 顶部抽屉只加载最近 10 条,完整通知中心使用独立路由 `/notifications`。
|
||||
- Rationale: 顶部入口保持轻量,筛选和完整历史查询放在独立页面,避免挤占全局导航空间。
|
||||
|
||||
- Decision: 未读数使用独立接口获取,单条通知和全部已读成功后立即更新本地共享状态,并以接口返回值为最终结果。
|
||||
- Rationale: 未读数是全局状态,不能依赖当前抽屉或列表的局部数量推导。
|
||||
|
||||
- Decision: 点击通知先调用单条已读接口,再调用目标接口,根据返回的受控 route name/route params 跳转。
|
||||
- Rationale: 已读状态必须在导航前落库,目标由后端业务引用解析,前端不执行通知携带的任意 URL。
|
||||
|
||||
- Decision: 未知 `ref_type` 或目标接口无可用目标时只展示通知正文和状态,不跳转。
|
||||
- Rationale: 保证安全,同时让无法关联页面的系统通知仍然可读。
|
||||
|
||||
- Decision: `unread-summary` 为顶部抽屉分类提供数据,`notifications` 为通知中心筛选列表提供数据。
|
||||
- Rationale: 顶部快速查看与完整列表的数据量和筛选职责不同,避免顶部加载完整历史数据。
|
||||
|
||||
## Data Contract
|
||||
|
||||
- Notification item: id, title, content, category, type, severity, read status, created time and optional `ref_type`/reference ID.
|
||||
- Unread count: non-negative integer; frontend formats it as `0`, `1`-`99` or `99+`.
|
||||
- Unread summary: category counts and recent notification items, limited to the latest 10 items for the drawer.
|
||||
- Notification list: paginated items plus total count, with category, type, severity and read-state filters.
|
||||
- Target response: controlled internal route information, such as route name/path and route params; no arbitrary executable URL.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Risk: 后端通知字段或枚举名称与文档不一致。
|
||||
- Mitigation: 在任务阶段先确认字段契约,类型层保留稳定的可选字段和未知值占位展示。
|
||||
|
||||
- Risk: 用户在多个标签页同时读通知,单页本地未读数短暂不一致。
|
||||
- Mitigation: 操作成功后刷新未读数和当前列表;跨标签实时同步不作为本期强制目标。
|
||||
|
||||
- Risk: 目标记录已删除或用户权限发生变化。
|
||||
- Mitigation: 目标接口返回不可跳转时只展示正文,并处理权限/不存在状态,不回退到任意 URL。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 确认通知列表、摘要、未读数和目标接口字段及枚举。
|
||||
2. 新增通知 API service、类型和共享状态。
|
||||
3. 将顶部通知按钮放入设置/头像区域,接入未读数和最近通知抽屉。
|
||||
4. 新增 `/notifications` 路由及通知中心页面。
|
||||
5. 实现筛选、单条已读、全部已读和受控目标跳转。
|
||||
6. 删除或替换 `ArtNotification` 中的静态 mock 数据,并验证四类通知展示一致。
|
||||
|
||||
## Open Questions
|
||||
|
||||
- 通知中心是否需要分页参数名称 `page/page_size`,以及默认每页数量需要后端确认。
|
||||
- `category`、`type`、`severity` 和 `read_status` 的枚举值及展示名称需要后端确认。
|
||||
- 目标接口返回 route name、path 还是 route key + params,需要与路由菜单契约确认。
|
||||
- 未读数是否需要页面进入时定时刷新或仅在打开抽屉/完成操作时刷新,本期默认按接口调用时机刷新。
|
||||
- 通知中心菜单权限和按钮权限编码需要后端权限表确认。
|
||||
46
openspec/changes/add-notification-center/proposal.md
Normal file
46
openspec/changes/add-notification-center/proposal.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Change: 顶部通知铃铛与站内通知中心
|
||||
|
||||
## Why
|
||||
|
||||
当前顶部通知组件仍使用硬编码 mock 数据,通知按钮也未启用,运营人员无法统一查看余额预警、临期提醒、审批结果和系统告警。需要建立真实的站内通知数据链路,并在全局导航和通知中心之间保持未读状态同步。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 在顶部全局导航的设置按钮与用户头像入口区域增加通知铃铛。
|
||||
- 铃铛展示未读数量,统一格式为 `0`、`1`~`99` 或 `99+`。
|
||||
- 点击铃铛展示最近 10 条通知抽屉,支持全部、审批、临期、同步/系统分类。
|
||||
- 抽屉提供进入 `/notifications` 通知中心的入口。
|
||||
- 新增通知中心页面,支持分类、通知类型、严重级别、已读状态筛选和全部已读。
|
||||
- 点击通知时先调用标记已读接口,再根据受控 `ref_type` 获取目标并跳转;未知目标只展示通知正文,不执行任意 URL 跳转。
|
||||
- 接入未读数、未读摘要、通知列表、单条已读和全部已读接口。
|
||||
- 替换现有 `ArtNotification` 硬编码 mock 数据实现,并复用现有顶部布局和设置/头像区域的视觉位置。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs:
|
||||
- `notification-center`
|
||||
- Affected code:
|
||||
- `src/components/core/layouts/art-header-bar/index.vue`
|
||||
- `src/components/core/layouts/art-notification/index.vue`
|
||||
- `src/components/core/layouts/art-notification/style.scss`
|
||||
- `src/views/notifications/index.vue`
|
||||
- `src/api/modules/notification.ts`
|
||||
- `src/types/api/notification.ts`
|
||||
- 路由、菜单和通知相关权限配置
|
||||
- API contracts:
|
||||
- `GET /api/admin/notifications/unread-count`
|
||||
- `GET /api/admin/notifications/unread-summary`
|
||||
- `GET /api/admin/notifications`
|
||||
- `PUT /api/admin/notifications/{id}/read`
|
||||
- `PUT /api/admin/notifications/read-all`
|
||||
- `GET /api/admin/notifications/{id}/target`
|
||||
- Dependencies:
|
||||
- 后端按当前用户返回通知数据、未读统计和受控跳转目标。
|
||||
- 后端明确通知分类、类型、严重级别、已读状态和 `ref_type` 枚举。
|
||||
- 目标接口不得返回可被前端直接执行的任意外部 URL。
|
||||
- Out of scope:
|
||||
- C 端 `/api/c/v1/notifications` 接口和页面。
|
||||
- 前端创建、编辑或删除通知。
|
||||
- 浏览器推送、WebSocket 或轮询之外的实时推送机制。
|
||||
- Breaking changes:
|
||||
- 现有 `ArtNotification` mock 通知数据和无效的空操作按钮将被真实通知数据及接口行为替换。
|
||||
@@ -0,0 +1,81 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Global Notification Bell
|
||||
|
||||
The admin frontend SHALL provide a global notification bell in the top navigation near the settings and user avatar entries.
|
||||
|
||||
#### Scenario: Display unread count
|
||||
|
||||
- **GIVEN** 用户已登录后台
|
||||
- **WHEN** 顶部导航加载未读通知数量
|
||||
- **THEN** 铃铛 MUST call `GET /api/admin/notifications/unread-count`
|
||||
- **AND** 数量 MUST display as `0`, `1` through `99`, or `99+`
|
||||
|
||||
#### Scenario: Open recent notification drawer
|
||||
|
||||
- **WHEN** 用户点击顶部通知铃铛
|
||||
- **THEN** 页面 MUST display the latest 10 notifications from the unread summary contract
|
||||
- **AND** 抽屉 MUST provide 全部、审批、临期、同步/系统分类
|
||||
- **AND** 抽屉 MUST provide an entry to `/notifications`
|
||||
|
||||
### Requirement: Notification Center Filtering
|
||||
|
||||
The admin frontend SHALL provide a `/notifications` notification center with server-backed filtering.
|
||||
|
||||
#### Scenario: Filter notifications
|
||||
|
||||
- **WHEN** 用户打开通知中心或调整筛选条件
|
||||
- **THEN** 页面 MUST support category, notification type, severity and read-state filters
|
||||
- **AND** 页面 MUST load results from `GET /api/admin/notifications`
|
||||
- **AND** 页面 MUST display notification title, content, category, severity, read state and created time
|
||||
|
||||
#### Scenario: Mark all notifications read
|
||||
|
||||
- **WHEN** 用户点击全部已读
|
||||
- **THEN** 页面 MUST call `PUT /api/admin/notifications/read-all`
|
||||
- **AND** 页面 MUST refresh the unread count and visible notification read states after success
|
||||
|
||||
### Requirement: Notification Read State Synchronization
|
||||
|
||||
The notification drawer and notification center SHALL keep read state and unread count synchronized with the backend.
|
||||
|
||||
#### Scenario: Mark a notification read
|
||||
|
||||
- **WHEN** 用户点击一条未读通知
|
||||
- **THEN** 页面 MUST call `PUT /api/admin/notifications/{id}/read` before navigation
|
||||
- **AND** 页面 MUST refresh or update the shared unread count after the read request succeeds
|
||||
|
||||
#### Scenario: Preserve read state after refresh
|
||||
|
||||
- **GIVEN** 用户已完成单条已读或全部已读操作
|
||||
- **WHEN** 用户关闭抽屉、切换页面或刷新通知中心
|
||||
- **THEN** 页面 MUST use the latest backend response rather than restoring stale local unread state
|
||||
|
||||
### Requirement: Controlled Notification Navigation
|
||||
|
||||
The admin frontend SHALL navigate from notifications only through controlled internal targets.
|
||||
|
||||
#### Scenario: Navigate to a valid notification target
|
||||
|
||||
- **GIVEN** 通知包含受支持的 `ref_type` 或业务引用
|
||||
- **WHEN** 单条已读成功后页面请求 `GET /api/admin/notifications/{id}/target`
|
||||
- **THEN** 页面 MUST navigate only to a target recognized by the frontend route table
|
||||
- **AND** 页面 MUST preserve the target route parameters returned by the backend
|
||||
|
||||
#### Scenario: Handle an unknown target safely
|
||||
|
||||
- **GIVEN** 通知的 `ref_type` 未知、目标已删除或目标无权限
|
||||
- **WHEN** 页面处理通知点击
|
||||
- **THEN** 页面 MUST not navigate to an arbitrary URL
|
||||
- **AND** 页面 MUST still display the notification content and a stable unavailable-target state
|
||||
|
||||
### Requirement: Consistent Notification Categories
|
||||
|
||||
The notification drawer and notification center SHALL present balance warnings, expiry reminders, approval results and system alerts through the same notification data contract.
|
||||
|
||||
#### Scenario: Display supported notification types
|
||||
|
||||
- **GIVEN** 后端返回余额预警、临期提醒、审批结果或系统告警
|
||||
- **WHEN** 用户查看抽屉或通知中心
|
||||
- **THEN** 页面 MUST display each item using its backend category, type and severity
|
||||
- **AND** 页面 MUST not replace the item with hardcoded mock notification content
|
||||
39
openspec/changes/add-notification-center/tasks.md
Normal file
39
openspec/changes/add-notification-center/tasks.md
Normal file
@@ -0,0 +1,39 @@
|
||||
## 1. Contract And Types
|
||||
|
||||
- [x] 1.1 按产品文档落地通知项、未读统计、未读摘要、分页列表和受控目标响应字段。
|
||||
- [x] 1.2 按产品文档落地分类、通知类型、严重级别、已读状态和 `ref_type` 兼容类型。
|
||||
- [x] 1.3 新增通知中心菜单权限 `notifications:view`、单条已读权限 `notifications:read`、全部已读权限 `notifications:read_all`。
|
||||
- [x] 1.4 新增 `notification.ts` API service,覆盖未读数、摘要、列表、单条已读、全部已读和目标接口。
|
||||
- [x] 1.5 新增通知实体、筛选参数、分页响应和目标响应类型。
|
||||
|
||||
## 2. Global Header And Drawer
|
||||
|
||||
- [x] 2.1 在顶部设置按钮与用户头像入口区域增加通知铃铛。
|
||||
- [x] 2.2 实现未读数量格式化为 `0`、`1`~`99` 或 `99+`。
|
||||
- [x] 2.3 顶部抽屉加载最近 10 条通知,并按全部、审批、临期、同步/系统分类展示。
|
||||
- [x] 2.4 替换 `ArtNotification` 静态 mock 数据,接入真实摘要接口。
|
||||
- [x] 2.5 提供进入 `/notifications` 通知中心的入口,并保证桌面端和移动端布局可用。
|
||||
|
||||
## 3. Notification Center
|
||||
|
||||
- [x] 3.1 新增 `/notifications` 路由和通知中心页面。
|
||||
- [x] 3.2 支持分类、通知类型、严重级别和已读状态筛选。
|
||||
- [x] 3.3 展示通知标题、正文、严重级别、分类、时间和已读状态。
|
||||
- [x] 3.4 实现全部已读,并在成功后同步未读数和列表状态。
|
||||
- [x] 3.5 处理空列表、加载失败、权限失败和未知枚举值的稳定展示。
|
||||
|
||||
## 4. Read And Controlled Navigation
|
||||
|
||||
- [x] 4.1 点击通知时先调用 `PUT /api/admin/notifications/{id}/read`。
|
||||
- [x] 4.2 已读成功后调用 `GET /api/admin/notifications/{id}/target` 获取受控目标。
|
||||
- [x] 4.3 仅允许跳转到后端返回且前端路由表认可的内部目标。
|
||||
- [x] 4.4 未知 `ref_type`、目标不存在或无权限时只展示正文,不执行任意 URL 跳转。
|
||||
- [x] 4.5 抽屉和通知中心的已读操作后刷新共享未读数。
|
||||
|
||||
## 5. Verification
|
||||
|
||||
- [x] 5.1 验证余额、临期、审批和系统通知在抽屉及通知中心展示一致。
|
||||
- [x] 5.2 验证未读数在单条已读、全部已读、抽屉关闭和页面刷新后与接口结果一致。
|
||||
- [x] 5.3 验证通知点击不会跳转到任意外部或未注册 URL。
|
||||
- [x] 5.4 验证未知目标通知仍可查看正文且不会阻塞其他通知操作。
|
||||
- [x] 5.5 运行类型检查、lint、相关测试和构建。
|
||||
Reference in New Issue
Block a user