feat(效能分析): 人员视图设备过多时分页显示,解决超宽无滚动
- 每页显示20台设备,底部上一页/下一页翻页器 - 数据/图按页切片,筛选变化自动回第1页 - 图宽限制在当前页设备数,看板始终可见
This commit is contained in:
@ -59,6 +59,8 @@ function alignName(name: string) {
|
|||||||
return name.length === 2 ? name[0] + " " + name[1] : name;
|
return name.length === 2 ? name[0] + " " + name[1] : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CAP_PAGE_SIZE = 20; // 人员视图每页设备数
|
||||||
|
|
||||||
export default function AnalyticsDashboard() {
|
export default function AnalyticsDashboard() {
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
@ -90,6 +92,8 @@ export default function AnalyticsDashboard() {
|
|||||||
const [flowLoading, setFlowLoading] = useState(false);
|
const [flowLoading, setFlowLoading] = useState(false);
|
||||||
// 📅 设备生产总天数口径:自然天 / 工作日
|
// 📅 设备生产总天数口径:自然天 / 工作日
|
||||||
const [totalDaysMode, setTotalDaysMode] = useState<"natural" | "workdays">("natural");
|
const [totalDaysMode, setTotalDaysMode] = useState<"natural" | "workdays">("natural");
|
||||||
|
// 📄 人员视图分页
|
||||||
|
const [capPage, setCapPage] = useState(0);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
// ─── 弹窗下钻:设备备注(all=true 表示流转图点击,展示全部) ──
|
// ─── 弹窗下钻:设备备注(all=true 表示流转图点击,展示全部) ──
|
||||||
@ -226,11 +230,18 @@ export default function AnalyticsDashboard() {
|
|||||||
|
|
||||||
// ─── 柱状图:人员视图(X=设备身份证,系列=人员,居中紧凑,消除幽灵占位) ──
|
// ─── 柱状图:人员视图(X=设备身份证,系列=人员,居中紧凑,消除幽灵占位) ──
|
||||||
const capabilityOption = useMemo<EChartsCoreOption>(() => {
|
const capabilityOption = useMemo<EChartsCoreOption>(() => {
|
||||||
const categories = capability?.categories ?? [];
|
const allCategories = capability?.categories ?? [];
|
||||||
const devices = capability?.devices ?? [];
|
const allDevices = capability?.devices ?? [];
|
||||||
|
const allSeries = capability?.series ?? [];
|
||||||
|
// 🔧 分页切片:每页只显示 CAP_PAGE_SIZE 台设备,避免超宽无滚动
|
||||||
|
const start = capPage * CAP_PAGE_SIZE;
|
||||||
|
const end = Math.min(start + CAP_PAGE_SIZE, allCategories.length);
|
||||||
|
const categories = allCategories.slice(start, end);
|
||||||
|
const devices = allDevices.slice(start, end);
|
||||||
|
const series = allSeries.map((s) => ({ ...s, data: s.data.slice(start, end) }));
|
||||||
// 每台设备上真正产生耗时的人员 seriesIndex 数组
|
// 每台设备上真正产生耗时的人员 seriesIndex 数组
|
||||||
const activePerDevice = devices.map((_, devIdx) =>
|
const activePerDevice = devices.map((_, devIdx) =>
|
||||||
(capability?.series ?? [])
|
series
|
||||||
.map((s, sIdx) => (s.data[devIdx] && s.data[devIdx].value != null ? sIdx : -1))
|
.map((s, sIdx) => (s.data[devIdx] && s.data[devIdx].value != null ? sIdx : -1))
|
||||||
.filter((idx) => idx !== -1),
|
.filter((idx) => idx !== -1),
|
||||||
);
|
);
|
||||||
@ -276,7 +287,7 @@ export default function AnalyticsDashboard() {
|
|||||||
axisLabel: { fontSize: 10, interval: 0, lineHeight: 13 },
|
axisLabel: { fontSize: 10, interval: 0, lineHeight: 13 },
|
||||||
},
|
},
|
||||||
yAxis: { type: "value", name: "耗时(小时)" },
|
yAxis: { type: "value", name: "耗时(小时)" },
|
||||||
series: (capability?.series ?? []).map((s, sIdx) => ({
|
series: series.map((s, sIdx) => ({
|
||||||
name: s.name,
|
name: s.name,
|
||||||
type: "custom",
|
type: "custom",
|
||||||
encode: { x: 0, y: 1 },
|
encode: { x: 0, y: 1 },
|
||||||
@ -324,7 +335,15 @@ export default function AnalyticsDashboard() {
|
|||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}, [capability]);
|
}, [capability, capPage]);
|
||||||
|
|
||||||
|
// 分页信息
|
||||||
|
const capTotalPages = capability ? Math.max(1, Math.ceil(capability.devices.length / CAP_PAGE_SIZE)) : 0;
|
||||||
|
const capViewCount = capability
|
||||||
|
? Math.min(CAP_PAGE_SIZE, Math.max(0, capability.devices.length - capPage * CAP_PAGE_SIZE))
|
||||||
|
: 0;
|
||||||
|
// 查询/筛选变化后回到第 1 页
|
||||||
|
useEffect(() => { setCapPage(0); }, [capability]);
|
||||||
|
|
||||||
// ─── 有数据的日期集合(用于日历蓝点) ──
|
// ─── 有数据的日期集合(用于日历蓝点) ──
|
||||||
const activityDates = useMemo(() => {
|
const activityDates = useMemo(() => {
|
||||||
@ -764,7 +783,7 @@ export default function AnalyticsDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full overflow-x-auto pb-2 custom-scrollbar">
|
<div className="w-full overflow-x-auto pb-2 custom-scrollbar">
|
||||||
<div style={{ minWidth: (capability?.devices?.length ?? 0) * 140 }}>
|
<div style={{ minWidth: Math.max(1, capViewCount) * 140 }}>
|
||||||
<BaseEChart
|
<BaseEChart
|
||||||
option={capabilityOption}
|
option={capabilityOption}
|
||||||
height={420}
|
height={420}
|
||||||
@ -773,6 +792,14 @@ export default function AnalyticsDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{/* 分页器:设备多时分页查看,避免超宽无滚动 */}
|
||||||
|
{capTotalPages > 1 && (
|
||||||
|
<div className="mt-3 flex items-center justify-center gap-2">
|
||||||
|
<Button size="small" disabled={capPage === 0} onClick={() => setCapPage(p => Math.max(0, p - 1))}>上一页</Button>
|
||||||
|
<span className="text-xs text-gray-400">{capPage + 1} / {capTotalPages} 页 · 每页 {CAP_PAGE_SIZE} 台</span>
|
||||||
|
<Button size="small" disabled={capPage >= capTotalPages - 1} onClick={() => setCapPage(p => Math.min(capTotalPages - 1, p + 1))}>下一页</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user