diff --git a/apps/web/src/viewport/VirtualLineReplay.tsx b/apps/web/src/viewport/VirtualLineReplay.tsx
index 1a44b7a..13d30d8 100644
--- a/apps/web/src/viewport/VirtualLineReplay.tsx
+++ b/apps/web/src/viewport/VirtualLineReplay.tsx
@@ -5,6 +5,8 @@ import './VirtualLineReplay.css';
const COLORS = ['#2563eb', '#f59e0b', '#0ea5e9', '#8b5cf6', '#ef4444', '#10b981', '#ec4899'];
const DEFAULT_OPERATION_ORDER = ['激光下料', '下料', '折弯', '焊接', '喷涂', '组装', '装配'];
const TARGET_REPLAY_MS = 45_000;
+const MAX_REPLAY_LOG_ROWS = 8; // 推演日志面板最多展示条数,更早的由滚动承接
+const MAX_ORDER_LANES = 8; // 订单泳道最多展示条数,避免窄屏溢出
type ReplayStatus = 'loading' | 'running' | 'warning' | 'passed' | 'blocked';
@@ -187,7 +189,7 @@ export default function VirtualLineReplay({ data }: { data: FlexGanttData }) {
kind: late || risk ? 'risk' : 'finish',
});
}
- return rows.filter(row => row.time <= virtualTime).sort((a, b) => b.time - a.time).slice(0, 8);
+ return rows.filter(row => row.time <= virtualTime).sort((a, b) => b.time - a.time).slice(0, MAX_REPLAY_LOG_ROWS);
}, [virtualTime, workOrders]);
const orderPosition = (order: OrderTrack) => {
@@ -283,7 +285,7 @@ export default function VirtualLineReplay({ data }: { data: FlexGanttData }) {
)}
订单入口
- {orders.slice(0, 8).map(order => {
+ {orders.slice(0, MAX_ORDER_LANES).map(order => {
const pos = orderPosition(order);
const left = stations.length <= 1 ? 50 : ((pos.stationIndex + 0.5) / stations.length) * 100;
const risk = pos.focus.conflict || pos.focus.overdue;
diff --git a/server/aps_domain/flex.py b/server/aps_domain/flex.py
index 7a37c01..1d40508 100644
--- a/server/aps_domain/flex.py
+++ b/server/aps_domain/flex.py
@@ -230,6 +230,9 @@ def flex_gantt_view(world: World) -> dict:
}
+MAX_BLOCKED_ORDER_REASONS = 8 # 单张阻断订单展示的缺料原因行数上限,超出由冲突中心承接
+
+
def _blocked_orders_view(world: World, wos_out: list[dict], orders_by_no: dict,
products: dict, latest_id) -> list[dict]:
"""阻断订单投影:本版本有冲突但没有任何工单的订单(如缺料急单)。
@@ -264,7 +267,7 @@ def _blocked_orders_view(world: World, wos_out: list[dict], orders_by_no: dict,
"priority": o.get("priority"),
"isRush": bool(o.get("isRush")),
"severity": severity_by_order.get(ono) or "",
- "reasons": reasons[:8],
+ "reasons": reasons[:MAX_BLOCKED_ORDER_REASONS],
})
blocked.sort(key=lambda b: (b.get("dueDate") or "", b["orderNo"]))
return blocked
diff --git a/server/aps_domain/orders.py b/server/aps_domain/orders.py
index fa83188..dc0ecf2 100644
--- a/server/aps_domain/orders.py
+++ b/server/aps_domain/orders.py
@@ -713,7 +713,7 @@ def apply_order_action(world: World, next_id, action: str, payload: dict[str, An
product = _product(world, p["productId"])
order_no = p.get("orderNo") or ""
if order_no:
- # 计划员指定单号(如急单 RUSH-0007):保留并查重,失败即拒绝
+ # 计划员指定单号:保留原号并全池查重,冲突即拒绝
folded = order_no.casefold()
clash = any(
str(o.get("orderNo") or "").casefold() == folded