- **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.
-`src/router/index.ts`: `beforeEach` guard — auth check, dynamic route registration from backend menus, tab management. Routes are dynamically added via `addRoute` with **flattened nested routes** (keep-alive limitation). View components resolved via `import.meta.glob("/src/views/**/*.vue")`.
-`src/store/index.ts` (`useAppStore`): monolithic store — all state nested in `state.state` (double nesting, e.g. `store.state.appIsLogin`). `initApp` fetches menus/permissions/user/dicts in 4 parallel requests.
-`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 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.
-`createdIsNeed: true` / `activatedIsNeed: false` by default. Pages needing refresh on tab activation must set `activatedIsNeed: true`.
- Includes workflow helpers (`handleFlowRoute`, `flowDetailRoute`) hardcoded to `/flow/task-form`.
## Cache utility
All cache keys prefixed with `v1@` to avoid collisions. Supports `localStorage` and `sessionStorage` (token uses sessionStorage). JSON serialization is automatic. `getCache` supports auto-delete-after-read (`isDelete` flag).
## Weather frontend module
The home dashboard (`src/views/home.vue`) uses a **composable-based architecture**:
| Composable | Responsibility |
|---|---|
| `useWeatherConstants.ts` | Rain levels, temperature thresholds, filter field definitions, `fmtVal()`, level/class helpers |
### 1. Null ≠ zero — missing data MUST be preserved as null
When mapping backend API responses to frontend models, **never** default missing numeric values to `0`. Rainfall of `0mm` means "no rain that day" (valid measurement); `null` means "no data available" (missing record). Use `: null` not `: 0` in data mapping, and display `"—"` for null values via `fmtVal()`.
// ❌ Wrong — confuses "no data" with "measured zero"
rainfall: row.rain2020!=null?+row.rain2020 : 0,
```
All helper functions must accept `number | null` and return `"—"` or `""` for null. Stats computations must skip null values.
### 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. 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`.
Always show `ElLoading.service` fullscreen and `ElMessage` success/failure. Disable the export button during rendering.
### 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.
| `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 |