feat: 新增效能分析看板(个人能力图谱 + 设备人员耗时对比)

- 前端:AnalyticsDashboard 页面 + BaseEChart 封装 + analyticsApi,路由与导航
- 后端:/analytics 系列接口(capability/flow/options/device-records)
- 能力图谱单机颗粒度、流转对比按人堆叠识别瓶颈、点击下钻备注、筛选动态联动
- 依赖:引入 echarts,移除 echarts-for-react
This commit is contained in:
2026-08-14 10:19:44 +08:00
parent f4fc23be42
commit 54b5ea00f1
10 changed files with 1390 additions and 1 deletions

View File

@ -0,0 +1,122 @@
import api from "./api";
// ============================================================
// 效能分析看板 — 前后端数据契约类型 + API
// 对应后端 app/services/analytics_service.py 的 Pydantic Schema
// ============================================================
// ─── 个人能力图谱(单机颗粒度分组柱状图) ──────────────────
export interface CapabilityDataPoint {
value: number | null; // 该人员在该设备上的总耗时(未触及为 null真实 0 为 0
spec_model: string; // 规格型号
status: string; // 该设备当前状态 WIP/PENDING/COMPLETED/—
first_received_at: string; // 最早接收时间 YYYY-MM-DD HH:mm
last_completed_at: string; // 最后完成时间 YYYY-MM-DD HH:mm无则空串
}
export interface CapabilityDevice {
product_sn: string; // 身份证
external_serial: string | null; // 产品序列号(业务序列号)
spec_model: string;
}
export interface CapabilitySeries {
name: string; // 人员姓名
assignee_id: string; // 人员ID
data: CapabilityDataPoint[]; // 与 categories 严格对齐,未触及设备补 0
}
export interface CapabilityResponse {
categories: string[]; // X 轴:设备身份证(按时间升序)
devices: CapabilityDevice[]; // 与 categories 对齐的设备元数据
series: CapabilitySeries[];
}
// ─── 设备流转对比(堆叠柱状) ──────────────────────────────
export interface FlowDevice {
product_sn: string;
external_serial: string | null;
material_name: string;
spec_model: string;
}
export interface FlowSeries {
name: string; // 工序节点名
data: (number | null)[]; // 每台设备在该工序的总耗时(未经过为 null
}
export interface FlowResponse {
devices: FlowDevice[];
series: FlowSeries[];
}
// ─── 设备备注记录(弹窗下钻) ──────────────────────────────
export interface DeviceRecord {
task_name: string; // 工序名
assignee_name: string; // 负责人姓名
status: string; // 任务状态
remark: string | null; // 备注
images: string[]; // 图片 URL 列表
created_at: string; // 记录时间 ISO
}
// ─── 顶部下拉选项 ──────────────────────────────────────────
export interface AssigneeOption {
id: string;
name: string;
}
export interface AnalyticsOptions {
assignees: AssigneeOption[];
spec_models: string[];
}
// ─── 查询参数 ──────────────────────────────────────────────
export interface CapabilityQuery {
assignee_ids?: string[];
spec_models?: string[];
since?: string;
until?: string;
}
export interface FlowQuery {
product_sns?: string[];
spec_models?: string[];
}
// ============================================================
// API 方法
// ============================================================
export async function fetchCapabilityProfile(query: CapabilityQuery): Promise<CapabilityResponse> {
const params: Record<string, string> = {};
if (query.assignee_ids?.length) params.assignee_ids = query.assignee_ids.join(",");
if (query.spec_models?.length) params.spec_models = query.spec_models.join(",");
if (query.since) params.since = query.since;
if (query.until) params.until = query.until;
const { data } = await api.get<CapabilityResponse>("/analytics/capability", { params });
return data;
}
export async function fetchFlowData(query: FlowQuery): Promise<FlowResponse> {
const params: Record<string, string> = {};
if (query.product_sns?.length) params.product_sns = query.product_sns.join(",");
if (query.spec_models?.length) params.spec_models = query.spec_models.join(",");
const { data } = await api.get<FlowResponse>("/analytics/flow", { params });
return data;
}
export async function fetchDeviceRecords(product_sn: string, assigneeIds?: string[]): Promise<DeviceRecord[]> {
const params: Record<string, string> = { product_sn };
if (assigneeIds?.length) params.assignee_ids = assigneeIds.join(",");
const { data } = await api.get<DeviceRecord[]>("/analytics/device-records", { params });
return data;
}
export async function fetchAnalyticsOptions(assigneeIds?: string[], specModels?: string[]): Promise<AnalyticsOptions> {
const params: Record<string, string> = {};
if (assigneeIds?.length) params.assignee_ids = assigneeIds.join(",");
if (specModels?.length) params.spec_models = specModels.join(",");
const { data } = await api.get<AnalyticsOptions>("/analytics/options", { params });
return data;
}