feat(前端): 产品卡片双生产总天数 + 效能分析设备总天数按钮切换

- 产品管理卡片同时显示「生产总天数(自然天)」和「生产总天数(工作日)」
- 效能分析页新增「设备生产总天数」统计卡片(设备数/平均/最长),顶部按钮切换 自然天/工作日
- 顺手修复 AnalyticsDashboard Tabs onChange 类型错误
This commit is contained in:
2026-08-28 15:29:01 +08:00
parent b09187ac6e
commit 7f1858387c
4 changed files with 58 additions and 4 deletions

View File

@ -250,7 +250,8 @@ export default function AdminProductsPage() {
return <span className={`rounded-md px-1.5 py-0.5 font-bold ${cls}`}>{formatDuration(h)}</span>;
})()}
</div>
<InfoRow icon={CalendarDays} label="生产总天数" value={formatProductionDays(p.created_at)} />
<InfoRow icon={CalendarDays} label="生产总天数" value={`${p.production_days ?? formatProductionDays(p.created_at)}`} />
<InfoRow icon={CalendarDays} label="生产总天数(工作日)" value={p.production_days_workdays != null ? `${p.production_days_workdays}` : "—"} />
<InfoRow icon={Clock} label="创建时间" value={new Date(p.created_at).toLocaleDateString("zh-CN")} />
</div>
<div className="border-t border-gray-50 px-4 py-3">

View File

@ -2,8 +2,8 @@
* 个人能力图谱X 轴 = 设备身份证,单机耗时对比,支持点击柱子下钻备注弹窗(含照片)。 */
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { DatePicker, Tabs, Select, Button, Empty, Modal, Timeline, Image } from "antd";
import { Users, GitBranch, RefreshCw, Loader2, AlertCircle } from "lucide-react";
import { DatePicker, Tabs, Select, Button, Empty, Modal, Timeline, Image, Radio } from "antd";
import { Users, GitBranch, RefreshCw, Loader2, AlertCircle, CalendarDays } from "lucide-react";
import dayjs, { type Dayjs } from "dayjs";
import type { EChartsCoreOption } from "echarts/core";
import "dayjs/locale/zh-cn";
@ -88,6 +88,8 @@ export default function AnalyticsDashboard() {
const [capabilityLoading, setCapabilityLoading] = useState(false);
const [flow, setFlow] = useState<FlowResponse | null>(null);
const [flowLoading, setFlowLoading] = useState(false);
// 📅 设备生产总天数口径:自然天 / 工作日
const [totalDaysMode, setTotalDaysMode] = useState<"natural" | "workdays">("natural");
const [error, setError] = useState<string | null>(null);
// ─── 弹窗下钻设备备注all=true 表示流转图点击,展示全部) ──
@ -673,10 +675,57 @@ export default function AnalyticsDashboard() {
</div>
)}
{/* 📅 设备生产总天数(自然天 / 工作日切换) */}
<div className="rounded-xl bg-white p-4 shadow-sm">
<div className="mb-3 flex items-center justify-between">
<h3 className="flex items-center gap-2 text-sm font-semibold text-gray-700">
<CalendarDays className="h-4 w-4 text-emerald-500" /> 📅
</h3>
<Radio.Group
value={totalDaysMode}
onChange={e => setTotalDaysMode(e.target.value)}
size="small"
optionType="button"
buttonStyle="solid"
>
<Radio.Button value="natural"></Radio.Button>
<Radio.Button value="workdays"></Radio.Button>
</Radio.Group>
</div>
{flowLoading && !flow ? (
<div className="flex items-center justify-center py-8">
<Loader2 className="h-5 w-5 animate-spin text-emerald-500" />
</div>
) : !flow || flow.devices.length === 0 ? (
<div className="py-8 text-center text-sm text-gray-400"></div>
) : (
<div className="grid gap-3 sm:grid-cols-3">
<div>
<p className="text-xs text-gray-400"></p>
<p className="text-2xl font-bold text-gray-800">{flow.devices.length}<span className="text-sm font-normal text-gray-400"> </span></p>
</div>
<div>
<p className="text-xs text-gray-400">{totalDaysMode === "natural" ? "平均生产总天数(自然天)" : "平均生产总天数(工作日)"}</p>
<p className="text-2xl font-bold text-emerald-600">
{Math.round(flow.devices.reduce((s, d) => s + (totalDaysMode === "natural" ? d.total_days : d.total_workdays), 0) / flow.devices.length)}
<span className="text-sm font-normal text-gray-400"> </span>
</p>
</div>
<div>
<p className="text-xs text-gray-400">{totalDaysMode === "natural" ? "自然天" : "工作日"}</p>
<p className="text-2xl font-bold text-blue-600">
{Math.max(...flow.devices.map(d => totalDaysMode === "natural" ? d.total_days : d.total_workdays))}
<span className="text-sm font-normal text-gray-400"> </span>
</p>
</div>
</div>
)}
</div>
{/* 主体:两个可视化区块 */}
<Tabs
activeKey={activeTab}
onChange={setActiveTab}
onChange={(k) => setActiveTab(k as "capability" | "flow")}
items={[
{
key: "capability",

View File

@ -41,6 +41,8 @@ export interface FlowDevice {
spec_model: string;
lead_time: number; // 设备生命周期总时长(小时)
started_at: string; // 设备最早介入时间T0MM-DD HH:mm
total_days: number; // 设备生产总天数(自然天)
total_workdays: number; // 设备生产总天数(工作日)
}
// 时间区间deviceIndex, startOffset, endOffset, taskName, duration, isMain

View File

@ -24,6 +24,8 @@ export interface ProductResponse {
latest_record_assignee_id: string | null;
latest_record_assignee_name: string | null;
active_duration_hours: number | null;
production_days: number;
production_days_workdays: number;
}
/** MOM 物料选项 */