feat: 环线批量操作、任务筛选与模拟下发搬运任务
为 LoopMission 增加一键上线/结束任务(含二次确认),DeliveryViewer 增加起点/终点/车辆/状态筛选并修复 CycleGUI 控件 id 碰撞,TransportMission 增加 Shelf 站点间随机模拟下发能力。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -43,6 +43,14 @@ namespace StandardScene.Chained
|
||||
private readonly TimeSpan FlushInterval = TimeSpan.FromSeconds(1);
|
||||
private volatile string _status = "";
|
||||
|
||||
// 显示筛选(仅作用于已拉取的快照,渲染线程内即时过滤,不触发重新拉取)
|
||||
private string _filterSrc = "";
|
||||
private string _filterDst = "";
|
||||
private string _filterCar = "";
|
||||
private int _filterStatusIdx;
|
||||
private readonly string[] _statusFilterNames =
|
||||
new[] { "全部" }.Concat(Enum.GetNames(typeof(DeliveryStatus))).ToArray();
|
||||
|
||||
/// <summary>打开(或置前)任务管理面板。兼容原 <c>new DeliveryViewer().Show()</c> 调用方式。</summary>
|
||||
public void Show() => Open();
|
||||
|
||||
@@ -91,13 +99,27 @@ namespace StandardScene.Chained
|
||||
EnsureSnapshotFresh();
|
||||
|
||||
var items = _snapshot;
|
||||
pb.Label($"共 {items.Count} 个任务(超时任务高亮置顶)");
|
||||
|
||||
// 筛选栏:起点 / 终点 / 车辆 / 任务状态(仅过滤当前快照,不触发重新拉取)
|
||||
// ###id:控件 id 经 ASCII 哈希,同字数纯中文标签(起点筛选/终点筛选)会撞 id,用 ### 后缀给唯一 ASCII id(显示不变)。
|
||||
var (fs, _) = pb.TextInput("起点筛选(站点ID或名称)###flt-src", _filterSrc, alwaysReturnString: true); _filterSrc = fs;
|
||||
var (fd, _) = pb.TextInput("终点筛选(站点ID或名称)###flt-dst", _filterDst, alwaysReturnString: true); _filterDst = fd;
|
||||
var (fc, _) = pb.TextInput("车辆筛选(车辆名)###flt-car", _filterCar, alwaysReturnString: true); _filterCar = fc;
|
||||
pb.DropdownBox("状态筛选###flt-status", _statusFilterNames, ref _filterStatusIdx);
|
||||
if (pb.Button("清空筛选", distinct: "delivery-clear-filter"))
|
||||
{
|
||||
_filterSrc = _filterDst = _filterCar = "";
|
||||
_filterStatusIdx = 0;
|
||||
}
|
||||
|
||||
var filtered = ApplyFilters(items);
|
||||
pb.Label($"共 {filtered.Count}/{items.Count} 个任务(超时任务高亮置顶)");
|
||||
|
||||
pb.Table(TableId,
|
||||
new[] { "任务号", "小车", "取货点", "放货点", "任务状态", "下发时间", "执行时间", "结束时间", "优先级", "操作" },
|
||||
items.Count, (row, i) =>
|
||||
filtered.Count, (row, i) =>
|
||||
{
|
||||
var dd = items[i];
|
||||
var dd = filtered[i];
|
||||
if (IsOverdue(dd)) row.SetColor(OverdueRowColor);
|
||||
|
||||
row.Label($"{dd.Id}");
|
||||
@@ -134,6 +156,32 @@ namespace StandardScene.Chained
|
||||
private bool IsOverdue(Delivery dd) =>
|
||||
(DateTime.Now - dd.CreateTime).TotalMinutes > OverdueMinutesThreshold;
|
||||
|
||||
/// <summary>按 起点 / 终点 / 车辆 / 任务状态 对当前快照即时过滤(空条件不限制;起点终点匹配“ID-名称”,大小写不敏感)。</summary>
|
||||
private List<Delivery> ApplyFilters(List<Delivery> items)
|
||||
{
|
||||
IEnumerable<Delivery> q = items;
|
||||
|
||||
var fSrc = (_filterSrc ?? "").Trim();
|
||||
if (fSrc.Length > 0)
|
||||
q = q.Where(d => $"{d.Src}-{SafeSiteName(d.Src)}".Contains(fSrc, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var fDst = (_filterDst ?? "").Trim();
|
||||
if (fDst.Length > 0)
|
||||
q = q.Where(d => $"{d.Dst}-{SafeSiteName(d.Dst)}".Contains(fDst, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var fCar = (_filterCar ?? "").Trim();
|
||||
if (fCar.Length > 0)
|
||||
q = q.Where(d => (d.UsingCar?.name ?? "").Contains(fCar, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (_filterStatusIdx > 0 && _filterStatusIdx < _statusFilterNames.Length)
|
||||
{
|
||||
var st = _statusFilterNames[_filterStatusIdx];
|
||||
q = q.Where(d => d.GetStatus().ToString() == st);
|
||||
}
|
||||
|
||||
return q.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 渲染线程调用:到达刷新间隔且无在途刷新时,<b>在后台线程</b>重新拉取任务快照(超时任务置顶)。
|
||||
/// 业务侧的锁与文件 IO 一律放到后台,渲染线程只读 <see cref="_snapshot"/> 引用,避免界面卡死。
|
||||
|
||||
Reference in New Issue
Block a user