优化通知栏,通知实时同步功能

This commit is contained in:
2026-06-27 19:15:06 +08:00
parent d38cbdbf07
commit 53b3b2c989
30 changed files with 1810 additions and 304 deletions
+70 -7
View File
@@ -4,6 +4,14 @@ Frontend module: Vue 3 / Vite 5 / TypeScript SPA.
---
## Code Style
- **No emoji in UI strings.** Plain Chinese text for labels, buttons, status.
- **No emoji in code comments or docstrings.** Plain text only.
- **Keep CLAUDE.md current** — whenever code is modified, added, deleted, or any file change affects the module structure, build, conventions, or component patterns, update this file (and the root `CLAUDE.md` if cross-cutting) in the same commit to reflect the new state. Stale documentation is a bug.
---
## Commands
```bash
@@ -55,7 +63,9 @@ Pre-commit: `lint-staged` runs `eslint --fix` on `*.ts`/`*.vue` via `yorkie` git
- `src/store/importTasks.ts`: separate store for import task tracking (computed getters: `activeTasks`, `hasActiveTasks`, `recentTasks`).
- `src/utils/router.ts`: converts backend menu records → Vue router records, supports iframe/external links with `openStyle` flags.
Layout is event-driven: `src/layout/` shell + `mitt` event bus (`src/utils/emits.ts`). The `EMitt` enum defines 13 events for sidebar, theme, tabs, layout changes. **Trace both the Pinia store and mitt events** when changing navigation/sidebar/tabs/theme.
Layout is event-driven: `src/layout/` shell + `mitt` event bus (`src/utils/emits.ts`). The `EMitt` enum defines events for sidebar, theme, tabs, layout changes. **Trace both the Pinia store and mitt events** when changing navigation/sidebar/tabs/theme.
Header right side: notification bell (combined badge) → `expand` (user menu). The old `import-task-indicator.vue` has been removed in favor of the notification center drawer.
## Common page pattern: `useView` hook
@@ -104,8 +114,8 @@ All helper functions must accept `number | null` and return `"—"` or `""` for
### 2. Heavy libraries must use dynamic imports
`html2canvas`, `jspdf`, and `echarts` are NOT imported at module level. Load them via `await import()` only when triggered by user action. This saves ~600KB from the initial bundle.
### 3. Google Fonts go in index.html, not scoped styles
Never use `@import url("https://fonts.googleapis.com/...")` inside Vue scoped styles. Use `<link rel="preconnect">` + `<link rel="stylesheet">` in `index.html`.
### 3. Fonts are self-hosted — no external network dependency
Fonts (Noto Sans SC, JetBrains Mono) are bundled via `@fontsource/*` packages, imported in `src/main.ts`. Do NOT add Google Fonts `<link>` tags or `@import` back — the system runs on intranet where external network may be unavailable. To add a new font weight, import the corresponding fontsource CSS file in `main.ts`.
### 4. Export must show user feedback
Always show `ElLoading.service` fullscreen and `ElMessage` success/failure. Disable the export button during rendering.
@@ -113,9 +123,62 @@ Always show `ElLoading.service` fullscreen and `ElMessage` success/failure. Disa
### 5. Deep watchers on filter objects are banned
Never use `watch(filters, callback, { deep: true })`. Derive a precise computed trigger key (e.g. `dataHash`, `filteredHash`, `extremesVersion`) and watch that instead.
## Import progress polling pattern
## Notification center
The global notification system (`src/components/alert-marquee/index.vue`) aggregates two feed types:
| Feed | Source | Clickable? |
|---|---|---|
| System alerts | `useAlertMarquee.ts` composable (polls `/sys/alert/active/since` every 30s) | Yes — opens detail dialog |
| Import tasks | `useImportTaskStore` Pinia store | No — shows real-time progress inline |
### Components & composables
| File | Role |
|---|---|
| `src/composables/useAlertMarquee.ts` | Module-level singleton: message queue, drawer toggle, detail dialog state, scrollbar position. Real-time delivery: SSE (`/sys/alert/stream`) primary + 10s polling fallback. Exports danger-specific computed: `dangerMessages`, `latestDangerMessage`, `dangerCount`, `hasDangerMessages` |
| `src/composables/useFloatingDrag.ts` | Pointer Events drag logic: `setPointerCapture`, viewport clamping, deferred `isDragging` (activates only on >3px move), `dragMoved` flag to distinguish drag vs click |
| `src/components/alert-marquee/index.vue` | Floating scrollbar (640px wide, centered top, draggable) + `el-drawer` notification center + `el-dialog` detail popup |
| `src/layout/header/base-header.vue` | Bell button with combined badge (alerts + active import tasks), toggles drawer |
| `src/store/importTasks.ts` | Import task CRUD: `addTask`, `updateTask`, `removeTask`, `clearCompleted`; getters: `activeTasks`, `recentTasks` |
### Level-based routing
- **danger (紧急)**: triggers the floating scrollbar + appears in notification center drawer + bell badge
- **warning / info (警告 / 提示)**: notification center drawer + bell badge only (no scrollbar)
- The bell badge in base-header always shows total count (all levels + active import tasks)
### Floating scrollbar behavior
- Visible only when logged in (`appStore.state.appIsLogin`) and has danger-level alerts (`hasDangerMessages`)
- Positioned centered at top (`y: 56` below header), draggable to reposition, re-centers on window resize
- Shows latest alert headline + count badge
- **Close button** hides the bar; **auto-reappears** when new danger alerts arrive (watch on `dangerCount`)
- **查看详情** button opens `el-dialog` with full alert text
- The old `import-task-indicator.vue` (bell icon with popover in header) has been **removed**
### Import progress integration
1. Upload via `baseService.upload()` (FormData, no explicit Content-Type).
2. On success, poll `GET .../import/progress/{backendTaskId}` every 3 seconds.
3. Update Pinia store (`useImportTaskStore`) with percentage/state.
4. Emit `refreshDataList` on completion. Header indicator (`import-task-indicator.vue`) shows active tasks with spinning badge + popover.
2. On upload start, toast: `"导入已开始,可在通知中心查看进度"`.
3. Poll `GET .../import/progress/{backendTaskId}` every 3 seconds.
4. Update Pinia store (`useImportTaskStore`) — progress bar + status tag render reactively in the drawer.
5. On completion: green checkmark; on failure: red cross + error message. Completed/failed tasks show a dismiss button.
### Alert management page
Manual alert CRUD at route `sys/system-alert`:
| File | Role |
|---|---|
| `src/views/sys/system-alert.vue` | List page: `useView`-based, search by level/title/sourceType, batch delete, per-row withdraw |
| `src/views/sys/system-alert-add-or-update.vue` | Add/edit dialog: level (info/warning/danger), title, content (textarea), sourceType, expireTime (datetime picker) |
Backend endpoints under `/sys/alert`:
- `GET /page` — paginated list (`sys:alert:page`)
- `GET /{id}` — detail (`sys:alert:info`)
- `POST /` — create (`sys:alert:save`)
- `PUT /` — update (`sys:alert:update`)
- `DELETE /` — batch delete (`sys:alert:delete`)
- `PUT /{id}/withdraw` — soft-withdraw (`sys:alert:update`)
- `GET /active`, `GET /active/since` — frontend polling (no permission required)