From 9d08c50ef6df361dfd908cabef0fd35dd972a73e Mon Sep 17 00:00:00 2001 From: duxingchen Date: Fri, 28 Aug 2026 16:35:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=80=8F=E8=A7=86=E8=A1=A8):=20=E5=88=97?= =?UTF-8?q?=E6=95=B0=E8=B6=856=E6=97=B6=E5=88=86=E5=9D=97=E5=9E=82?= =?UTF-8?q?=E7=9B=B4=E6=8E=92=E5=88=97=20+=20=E8=A7=84=E6=A0=BC=E5=9E=8B?= =?UTF-8?q?=E5=8F=B7=E5=88=97=E5=8D=87=E9=99=8D=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 人员/工序列超过 CHUNK_MATRIX(6) 时按列分块,多张透视表垂直向下排列,告别一行十几个人员横向超长 - 规格型号列支持点击升降序排序(中文字典序) - 每块独立合计 --- frontend/src/pages/MatrixBoard.tsx | 131 ++++++++++++++++++----------- 1 file changed, 84 insertions(+), 47 deletions(-) diff --git a/frontend/src/pages/MatrixBoard.tsx b/frontend/src/pages/MatrixBoard.tsx index 6e5e7fb..b5a0692 100644 --- a/frontend/src/pages/MatrixBoard.tsx +++ b/frontend/src/pages/MatrixBoard.tsx @@ -4,11 +4,15 @@ */ import { useEffect, useMemo, useState } from "react"; import { Radio, Table, DatePicker, Button } from "antd"; +import { Loader2 } from "lucide-react"; import type { Dayjs } from "dayjs"; import { fetchWipMatrix, type WipMatrixRow } from "../services/dashboardApi"; const { RangePicker } = DatePicker; +// 每块最多显示的人员/工序列数,超过则分块垂直排列,避免横向超长 +const CHUNK_MATRIX = 6; + export default function MatrixBoard() { const [dimension, setDimension] = useState<"assignee" | "task_name">("assignee"); const [range, setRange] = useState<[Dayjs, Dayjs] | null>(null); @@ -28,46 +32,63 @@ export default function MatrixBoard() { }, [dimension, range]); // 🔧 核心:扁平数组 → 动态交叉表(规格型号为行、dimension_key 为列) - const { columns, dataSource } = useMemo(() => { + // 列数超过 CHUNK_MATRIX 时分块垂直排列,避免一行十几个人员横向超长 + const matrixChunks = useMemo(() => { const keys = Array.from(new Set(data.map((d) => d.dimension_key))); - const rowsMap = new Map(); - for (const item of data) { - if (!rowsMap.has(item.spec_model)) { - rowsMap.set(item.spec_model, { spec_model: item.spec_model, _assignees: {}, row_total: 0 }); + if (keys.length === 0) return []; + const chunks: { columns: any[]; dataSource: any[] }[] = []; + + for (let i = 0; i < keys.length; i += CHUNK_MATRIX) { + const chunkKeys = keys.slice(i, i + CHUNK_MATRIX); + const keySet = new Set(chunkKeys); + const rowsMap = new Map(); + for (const item of data) { + if (!keySet.has(item.dimension_key)) continue; + if (!rowsMap.has(item.spec_model)) { + rowsMap.set(item.spec_model, { spec_model: item.spec_model, _assignees: {}, row_total: 0 }); + } + const row = rowsMap.get(item.spec_model); + row[item.dimension_key] = (row[item.dimension_key] || 0) + item.count; + row.row_total += item.count; + // 记录每个交叉点的主负责人(中文名,去重) + row._assignees[item.dimension_key] = item.assignees || []; } - const row = rowsMap.get(item.spec_model); - row[item.dimension_key] = (row[item.dimension_key] || 0) + item.count; - row.row_total += item.count; - // 记录每个交叉点的主负责人(中文名,去重) - row._assignees[item.dimension_key] = item.assignees || []; - } - const ds = Array.from(rowsMap.values()); - const cols: any[] = [ - { title: "规格型号", dataIndex: "spec_model", fixed: "left", width: 190, render: (v: string) => {v} }, - ...keys.map((key) => ({ - title: key, - dataIndex: key, - align: "center" as const, - width: 120, - render: (val: number, record: any) => { - const assignees = record._assignees?.[key] || []; - return ( -
-
{val || 0}
- {assignees.length > 0 && ( -
- {assignees.join("、")} -
- )} -
- ); + const columns: any[] = [ + { + title: "规格型号", + dataIndex: "spec_model", + fixed: "left", + width: 170, + sorter: (a: any, b: any) => String(a.spec_model).localeCompare(String(b.spec_model), "zh"), + sortDirections: ["ascend", "descend"], + render: (v: string) => {v}, }, - })), - { title: "合计", dataIndex: "row_total", fixed: "right" as const, align: "center" as const, width: 90, render: (v: number) => {v} }, - ]; + ...chunkKeys.map((key) => ({ + title: key, + dataIndex: key, + align: "center" as const, + width: 120, + render: (val: number, record: any) => { + const assignees = record._assignees?.[key] || []; + return ( +
+
{val || 0}
+ {assignees.length > 0 && ( +
+ {assignees.join("、")} +
+ )} +
+ ); + }, + })), + { title: "合计", dataIndex: "row_total", fixed: "right" as const, align: "center" as const, width: 90, render: (v: number) => {v} }, + ]; - return { columns: cols, dataSource: ds }; + chunks.push({ columns, dataSource: Array.from(rowsMap.values()) }); + } + return chunks; }, [data]); return ( @@ -93,18 +114,34 @@ export default function MatrixBoard() { -
- - + {loading && data.length === 0 ? ( +
+ +
+ ) : matrixChunks.length === 0 ? ( +
暂无数据
+ ) : ( + /* 🔧 瀑布流:人员/工序列超 CHUNK_MATRIX 时,按列分块垂直向下排列 */ +
+ {matrixChunks.map((chunk, idx) => ( +
+ {idx > 0 &&
} +
+
+ + + ))} + + )} ); }