feat: 仪表盘(租户卡片墙/平台总览/时间切换)+ 顺手:409契约锁与死代码

- 仪表盘视图:超管=平台总览四指标卡(/api/platform/stats 合并视图)+
  租户卡片墙(逐租户 ?tenant= 下钻,含停用租户);租户管理员=自己
  指标卡(/tenants/{slug}/stats,slug 取登录态);口径文案照
  stats.py docstring(线索=留资三选一/成功率分母0显—等)
- 时间范围快捷切换(今天/近7天/近30天)→ from/to 本地日期串;
  utils/statsRange.ts 纯函数 + epoch 防快速切换过期响应竞写
- 空数据态:无租户 ElEmpty / 单租户零数据自然显 0 与 —
- api/stats.ts 补 fetchPlatformStats/fetchTenantStats;tenants.ts 补
  fetchTenantList(卡片墙枚举,其余仍留 Task 6)
- vitest +11(statsRange 6 + stats API 5,总 36)
- 顺手1:test_auth must_change 409 测试补 detail 断言,锁死前后端
  契约文案(client.ts MUST_CHANGE_DETAIL)
- 顺手2:删除 beforeEach.ts 的 isUnauthorizedError 死分支与 ApiStatus
  import(模板 isHttpError 判自建 ApiError 恒 false,分支不可达)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-13 16:59:12 +08:00
parent 3973abea8b
commit f640889f22
9 changed files with 664 additions and 32 deletions
-16
View File
@@ -49,7 +49,6 @@ import { staticRoutes } from '../routes/staticRoutes'
import { loadingService } from '@/utils/ui'
import { useCommon } from '@/hooks/core/useCommon'
import { useWorktabStore } from '@/store/modules/worktab'
import { ApiStatus } from '@/utils/http/status'
import { isHttpError } from '@/utils/http/error'
import { RouteRegistry, MenuProcessor, IframeRouteManager, RoutePermissionValidator } from '../core'
@@ -389,14 +388,6 @@ async function handleDynamicRoutes(
// 关闭 loading
closeLoading()
// 401 错误:axios 拦截器已处理退出登录,取消当前导航
if (isUnauthorizedError(error)) {
// 重置状态,允许重新登录后再次初始化
routeInitInProgress = false
next(false)
return
}
// 标记初始化失败,防止死循环
routeInitFailed = true
routeInitInProgress = false
@@ -459,10 +450,3 @@ function handleRootPathRedirect(to: RouteLocationNormalized, next: NavigationGua
return false
}
/**
* 判断是否为未授权错误(401)
*/
function isUnauthorizedError(error: unknown): boolean {
return isHttpError(error) && error.code === ApiStatus.unauthorized
}