From 41dd25726056137532a99c481aef38a157bc6d48 Mon Sep 17 00:00:00 2001 From: duxingchen Date: Fri, 28 Aug 2026 16:32:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=80=8F=E8=A7=86=E8=A1=A8):=20=E5=B7=A5?= =?UTF-8?q?=E5=BA=8F=E5=88=86=E5=B8=83=E5=8C=85=E5=90=AB=E5=9C=A8=E5=BA=93?= =?UTF-8?q?/=E5=AE=8C=E6=88=90=20+=20=E5=A2=9E=E5=8A=A0=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - wip-matrix 去掉状态过滤,全量分布,工序包含「在库」(生产完成) - 后端加 since/until 按任务接手/创建时间过滤 - 前端 MatrixBoard 加 RangePicker 日期筛选 + 全部清除 --- backend/app/api/v1/endpoints/dashboard.py | 8 ++++-- backend/app/services/dashboard_service.py | 14 ++++++++-- frontend/src/pages/MatrixBoard.tsx | 34 +++++++++++++++++------ frontend/src/services/dashboardApi.ts | 9 ++++-- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/backend/app/api/v1/endpoints/dashboard.py b/backend/app/api/v1/endpoints/dashboard.py index 9fe7a1e..38fd52c 100644 --- a/backend/app/api/v1/endpoints/dashboard.py +++ b/backend/app/api/v1/endpoints/dashboard.py @@ -104,10 +104,14 @@ async def user_operations_detail( @router.get("/wip-matrix", response_model=list[WipMatrixRow]) async def wip_matrix( dimension: str = Query("assignee", description="聚合维度: assignee(人员) / task_name(工序)"), + since: str | None = Query(None, description="起始日期 ISO"), + until: str | None = Query(None, description="截止日期 ISO"), db: AsyncSession = Depends(get_db), ): - """在制品分布透视表 — 规格型号 × 人员/工序 的设备数量交叉聚合""" - return await get_wip_matrix(db, dimension=dimension) + """生产分布透视表 — 规格型号 × 人员/工序 的设备数量交叉聚合(含在库/完成)""" + since_dt = datetime.fromisoformat(since) if since else None + until_dt = datetime.fromisoformat(until) if until else None + return await get_wip_matrix(db, dimension=dimension, since=since_dt, until=until_dt) @router.get("/people-workload", response_model=list[PersonWorkload]) diff --git a/backend/app/services/dashboard_service.py b/backend/app/services/dashboard_service.py index 74b105e..9e16cb6 100644 --- a/backend/app/services/dashboard_service.py +++ b/backend/app/services/dashboard_service.py @@ -607,14 +607,19 @@ async def get_user_operations( async def get_wip_matrix( db: AsyncSession, dimension: str = "assignee", + since: datetime | None = None, + until: datetime | None = None, ) -> list[WipMatrixRow]: - """在制品交叉聚合:Y=规格型号,X=人员 或 工序,单元格=设备数量。 + """生产分布透视表:Y=规格型号,X=人员 或 工序,单元格=设备数量。 + + 覆盖全部任务状态(含已完成/在库),工序分布能看到「在库」(生产完成)。 + since/until 按任务接手/创建时间过滤。 dimension: - assignee: 按任务负责人聚合(dimension_key 为中文姓名) - task_name: 按工序名聚合(dimension_key 为工序名,附主负责人) """ - from app.models.task import Task, TASK_STATUS_WIP, TASK_STATUS_PENDING + from app.models.task import Task from app.models.product import Product dim_expr = Task.task_name if dimension == "task_name" else Task.assignee_id @@ -627,10 +632,13 @@ async def get_wip_matrix( func.array_agg(func.distinct(Task.assignee_id)), ) .join(Task, Task.product_id == Product.id) - .where(Task.status.in_([TASK_STATUS_WIP, TASK_STATUS_PENDING])) .group_by(Product.spec_model, dim_expr) .order_by(Product.spec_model, dim_expr) ) + if since: + stmt = stmt.where(func.coalesce(Task.received_at, Task.created_at) >= since) + if until: + stmt = stmt.where(func.coalesce(Task.received_at, Task.created_at) <= until) result = await db.execute(stmt) rows = result.all() diff --git a/frontend/src/pages/MatrixBoard.tsx b/frontend/src/pages/MatrixBoard.tsx index b47d8a3..6e5e7fb 100644 --- a/frontend/src/pages/MatrixBoard.tsx +++ b/frontend/src/pages/MatrixBoard.tsx @@ -3,21 +3,29 @@ * Y 轴 = 规格型号,X 轴 = 人员 或 工序,单元格 = 该交叉点的在制品设备数 */ import { useEffect, useMemo, useState } from "react"; -import { Radio, Table } from "antd"; +import { Radio, Table, DatePicker, Button } from "antd"; +import type { Dayjs } from "dayjs"; import { fetchWipMatrix, type WipMatrixRow } from "../services/dashboardApi"; +const { RangePicker } = DatePicker; + export default function MatrixBoard() { const [dimension, setDimension] = useState<"assignee" | "task_name">("assignee"); + const [range, setRange] = useState<[Dayjs, Dayjs] | null>(null); const [data, setData] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); - fetchWipMatrix(dimension) + fetchWipMatrix( + dimension, + range ? range[0].startOf("day").toISOString() : undefined, + range ? range[1].endOf("day").toISOString() : undefined, + ) .then(setData) .catch(() => setData([])) .finally(() => setLoading(false)); - }, [dimension]); + }, [dimension, range]); // 🔧 核心:扁平数组 → 动态交叉表(规格型号为行、dimension_key 为列) const { columns, dataSource } = useMemo(() => { @@ -67,12 +75,22 @@ export default function MatrixBoard() {

📊 WIP 分布矩阵

-

在制品分布透视表:规格型号 × {dimension === "assignee" ? "人员" : "工序"} · 单元格为设备数量

+

生产分布透视表:规格型号 × {dimension === "assignee" ? "人员" : "工序"} · 含在库/完成

+
+
+ setRange(dates as [Dayjs, Dayjs] | null)} + style={{ width: 240 }} + placeholder={["开始日期", "结束日期"]} + /> + {range && } + setDimension(e.target.value)} optionType="button" buttonStyle="solid"> + 按人员分布 + 按工序分布 +
- setDimension(e.target.value)} optionType="button" buttonStyle="solid"> - 按人员分布 - 按工序分布 -
diff --git a/frontend/src/services/dashboardApi.ts b/frontend/src/services/dashboardApi.ts index 0a58e70..0da6977 100644 --- a/frontend/src/services/dashboardApi.ts +++ b/frontend/src/services/dashboardApi.ts @@ -184,8 +184,13 @@ export async function fetchOperationDetail( return data; } -export async function fetchWipMatrix(dimension: "assignee" | "task_name"): Promise { - const { data } = await api.get("/dashboard/wip-matrix", { params: { dimension } }); +export async function fetchWipMatrix( + dimension: "assignee" | "task_name", since?: string, until?: string, +): Promise { + const params: Record = { dimension }; + if (since) params.since = since; + if (until) params.until = until; + const { data } = await api.get("/dashboard/wip-matrix", { params }); return data; }