feat: 管理层数据大屏(后端聚合接口 + 前端全屏页面)

PC 管理端新增独立全屏数据大屏,供管理层查看直通率/产量趋势/不良分布,
替代原 AdminDashboard 上零散的手工下钻。

后端:
- endpoints/screen.py + services/screen_service.py: 大屏聚合接口
  (复用 lifecycle 的售后工序归一,保证统计口径与展示一致)
- router.py: 注册 screen_router

前端:
- pages/admin/ScreenDashboard.tsx: 全屏大屏页(自带鉴权守卫,无侧边栏)
- services/screenApi.ts: 大屏数据接口封装
- components/admin/UserOperationDetailDrawer.tsx: 人员操作明细抽屉,
  由 AdminDashboard 的原生 state 抽成独立组件(含命令式 handle)
- AdminDashboard.tsx: 改为使用该抽屉组件,移除内联的下钻状态
- MatrixBoard.tsx / App.tsx / AdminLayout.tsx: 挂载路由与导航入口
- BaseEChart.tsx: 注册 GaugeChart 与 GraphicComponent
  (graphic 需显式注册,否则饼图中心文字静默不渲染)
This commit is contained in:
2026-09-15 10:57:46 +08:00
parent 71e660b428
commit 0d1e45e3fb
11 changed files with 1054 additions and 76 deletions

View File

@ -0,0 +1,62 @@
/** 管理层数据大屏 — 前后端数据契约类型 + API
* 对应后端 app/services/screen_service.py 的 Pydantic Schema
*/
import api from "./api";
// ─── 当月吞吐(顶部数字卡) ────────────────────────────────
export interface MonthlyMetrics {
month: string; // "2026-09"
month_start: string; // "2026-09-01"(北京时间)
month_production: number; // 本月有流转记录或新建的生产态设备数
month_inbound: number; // 本月扫码入库数
month_outbound: number; // 本月扫码出库数
month_returned: number; // 本月进入售后/回流状态的设备数
}
// ─── 工序积压分布(柱状图) ────────────────────────────────
export interface WipStage {
stage: string;
phase: "PRODUCTION" | "AFTER_SALES";
count: number;
}
export interface WipDistributionResponse {
total: number;
items: WipStage[];
}
// ─── 系统使用活跃度 ────────────────────────────────────────
export interface ActiveUser {
user_id: string;
user_name: string;
receive_count: number;
transfer_count: number;
record_count: number;
total: number;
}
export interface ActiveUsersResponse {
month: string;
items: ActiveUser[];
}
// ============================================================
// API 方法
// ============================================================
export async function fetchMonthlyMetrics(): Promise<MonthlyMetrics> {
const { data } = await api.get<MonthlyMetrics>("/screen/monthly-metrics");
return data;
}
export async function fetchWipDistribution(): Promise<WipDistributionResponse> {
const { data } = await api.get<WipDistributionResponse>("/screen/wip-distribution");
return data;
}
export async function fetchActiveUsers(topN = 5): Promise<ActiveUsersResponse> {
const { data } = await api.get<ActiveUsersResponse>("/screen/active-users", {
params: { top_n: topN },
});
return data;
}