diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 7635d3a..be750ac 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -21,7 +21,22 @@
"Bash(dotnet new *)",
"Bash(dotnet add *)",
"Bash(python3 *)",
- "Bash(git restore *)"
+ "Bash(git restore *)",
+ "Bash(sort -t'\"' -k2 -n)",
+ "Bash(git checkout *)",
+ "Bash(sed -n '41,65p' Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml)",
+ "Bash(sed -n '58,75p' Sln.Wcs.UI/Views/Task/TaskQueueListView.axaml)",
+ "Bash(sed -n '11,28p' Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml)",
+ "Bash(sed -n '42,50p' Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml)",
+ "Bash(sed -n '64,68p' Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml)",
+ "Bash(sed -n '82,86p' Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml)",
+ "Bash(sed -n '62,67p' Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml)",
+ "Bash(sed -n '80,84p' Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml)",
+ "Bash(sed -i '' 's|Grid\\\\.Column=\"1\" Text=\"{Binding SlidePanelTitle}\"|Grid.Column=\"0\" Text=\"{Binding SlidePanelTitle}\"|g' Sln.Wcs.UI/Views/Device/DeviceInfoListView.axaml)",
+ "Bash(sed -i '' 's|Grid\\\\.Column=\"2\" Content=\"新增\" Command=\"{Binding AddParamCommand}\"|Grid.Column=\"1\" Content=\"新增\" Command=\"{Binding AddParamCommand}\"|g' Sln.Wcs.UI/Views/Device/DeviceInfoListView.axaml)",
+ "Bash(sed -i '' 's|Grid\\\\.Column=\"3\" Content=\"✕\" Click=\"ClosePanel_Click\"|Grid.Column=\"2\" Content=\"✕\" Click=\"ClosePanel_Click\"|g' Sln.Wcs.UI/Views/Device/DeviceInfoListView.axaml)",
+ "Bash(sed -i '' 's|Grid\\\\.Column=\"1\" Text=\"{Binding SlidePanelTitle}\"|Grid.Column=\"0\" Text=\"{Binding SlidePanelTitle}\"|g' Sln.Wcs.UI/Views/Path/PathInfoListView.axaml)",
+ "Bash(sed -i '' 's|Grid\\\\.Column=\"2\" Content=\"新增\" Command=\"{Binding AddDetailCommand}\"|Grid.Column=\"1\" Content=\"新增\" Command=\"{Binding AddDetailCommand}\"|g' Sln.Wcs.UI/Views/Path/PathInfoListView.axaml)"
]
}
}
diff --git a/Sln.Wcs.HikRoBotServer/Program.cs b/Sln.Wcs.HikRoBotServer/Program.cs
index f3cded7..45a3b79 100644
--- a/Sln.Wcs.HikRoBotServer/Program.cs
+++ b/Sln.Wcs.HikRoBotServer/Program.cs
@@ -103,9 +103,23 @@ api.MapGet("/task/status", (string taskCode) =>
return Results.Ok(new { time = DateTime.Now, status = "ok" , taskStatus = taskStatus });
});
+//0524779AA0550094
+// AGV 等待点
+// api.MapPost("/robot/wait", (HikRoBotWaitRequest req) =>
+// {
+// return Results.Ok(new { time = DateTime.Now, status = "ok" , agvPosition = "0524779AA0550094" });
+// });
+
api.MapGet("/health", () => Results.Ok(new { time = DateTime.Now, status = "ok" }));
log.Info("HikRoBotServer 就绪: http://localhost:5200/swagger");
app.Run();
record ReceiveTaskRequest(string TaskCode, string StartPoint, string EndPoint);
+
+///
+/// 请求参数
+///
+/// WCS 下发的任务编号:同 submit 接口
+/// 提升机编号:15 栋入库-1#Hoist ;15栋出库-2#Hoist;14 栋提升机-3#Host;13 栋提升机-4#Hoist;
+record HikRoBotWaitRequest(string TaskCode, string HoistCode);
diff --git a/Sln.Wcs.HoistDispatcher/HoistDispatchHub.cs b/Sln.Wcs.HoistDispatcher/HoistDispatchHub.cs
index 98d3cfb..e28a526 100644
--- a/Sln.Wcs.HoistDispatcher/HoistDispatchHub.cs
+++ b/Sln.Wcs.HoistDispatcher/HoistDispatchHub.cs
@@ -112,8 +112,32 @@ public class HoistDispatchHub
out string startLocation);
TryParsePointCode(liveTaskDetail.endPoint, out string endBuilding, out string endFloor,
out string endLocation);
- int endPoint = Convert.ToInt32(deviceInfo.deviceSerialNo + startFloor + endFloor);
+
+ if (startBuilding.Contains("15") && endBuilding.Contains("15"))
+ {
+ if (startFloor == "1")
+ {
+ startFloor = deviceInfo.deviceSerialNo == 1 ? "13" : "12";
+ }
+ else
+ {
+ startFloor = $"0{startFloor}";
+ }
+ if (endFloor == "1")
+ {
+ endFloor = deviceInfo.deviceSerialNo == 1 ? "13" : "12";
+ }
+ else
+ {
+ endFloor = $"0{endFloor}";
+ }
+ }
+
+ int endPoint = Convert.ToInt32(deviceInfo.deviceSerialNo + startFloor + endFloor);
+
+
+
//调用适配层下发 提升机调度任务
SetHoistTaskResultDto res = _hoistAdapter.SetHoistTask(new SetHoistTaskDto()
{
@@ -164,7 +188,7 @@ public class HoistDispatchHub
}
}
}
-
+
///
/// 刷新设备参数:根据设备参数地址通过 PLC 获取参数值
/// hostCode为空时获取配置的所有参数值
diff --git a/Sln.Wcs.Model/Domain/BaseDeviceHost.cs b/Sln.Wcs.Model/Domain/BaseDeviceHost.cs
index cf7e280..5a07098 100644
--- a/Sln.Wcs.Model/Domain/BaseDeviceHost.cs
+++ b/Sln.Wcs.Model/Domain/BaseDeviceHost.cs
@@ -103,4 +103,7 @@ public class BaseDeviceHost
///
[SugarColumn(ColumnName = "remark")]
public string remark { get; set; }
+
+ [SugarColumn(IsIgnore = true)]
+ public int RowIndex { get; set; }
}
\ No newline at end of file
diff --git a/Sln.Wcs.Model/Domain/BaseDeviceInfo.cs b/Sln.Wcs.Model/Domain/BaseDeviceInfo.cs
index f6b50a2..145caa6 100644
--- a/Sln.Wcs.Model/Domain/BaseDeviceInfo.cs
+++ b/Sln.Wcs.Model/Domain/BaseDeviceInfo.cs
@@ -123,5 +123,16 @@ namespace Sln.Wcs.Model.Domain
[SugarColumn(IsIgnore = true)]
[Navigate(NavigateType.OneToMany, nameof(BaseDeviceParam.deviceCode), nameof(deviceCode))]
public List< BaseDeviceParam> deviceParams { get; set; }
+
+ [SugarColumn(IsIgnore = true)]
+ public string deviceTypeText
+ {
+ get { return deviceType switch { 0 => "输送线", 1 => "AGV", 2 => "提升机", _ => "--" }; }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string deviceStatusText
+ {
+ get { return deviceStatus switch { 0 => "正常", 1 => "在忙", 2 => "异常", _ => "--" }; }
+ }
}
}
diff --git a/Sln.Wcs.Model/Domain/BaseDeviceParam.cs b/Sln.Wcs.Model/Domain/BaseDeviceParam.cs
index 22037d4..ea8eed9 100644
--- a/Sln.Wcs.Model/Domain/BaseDeviceParam.cs
+++ b/Sln.Wcs.Model/Domain/BaseDeviceParam.cs
@@ -118,4 +118,7 @@ public class BaseDeviceParam
[SugarColumn(ColumnName = "remark")]
public string remark { get; set; }
+
+ [SugarColumn(IsIgnore = true)]
+ public int RowIndex { get; set; }
}
\ No newline at end of file
diff --git a/Sln.Wcs.Model/Domain/BasePathDetails.cs b/Sln.Wcs.Model/Domain/BasePathDetails.cs
index 624f00b..39edaee 100644
--- a/Sln.Wcs.Model/Domain/BasePathDetails.cs
+++ b/Sln.Wcs.Model/Domain/BasePathDetails.cs
@@ -98,5 +98,14 @@ namespace Sln.Wcs.Model.Domain
///
[SugarColumn(ColumnName = "remark")]
public string remark { get; set; }
+
+ [SugarColumn(IsIgnore = true)]
+ public string deviceTypeText
+ {
+ get { return deviceType switch { 0 => "输送线", 1 => "AGV", 2 => "提升机", _ => "--" }; }
+ }
+
+ [SugarColumn(IsIgnore = true)]
+ public int RowIndex { get; set; }
}
}
diff --git a/Sln.Wcs.Model/Domain/BasePathInfo.cs b/Sln.Wcs.Model/Domain/BasePathInfo.cs
index a5eae50..0bea36e 100644
--- a/Sln.Wcs.Model/Domain/BasePathInfo.cs
+++ b/Sln.Wcs.Model/Domain/BasePathInfo.cs
@@ -113,5 +113,8 @@ namespace Sln.Wcs.Model.Domain
[SugarColumn(IsIgnore = true)]
[Navigate(NavigateType.OneToMany, nameof(BasePathDetails.pathCode), nameof(pathCode))]
public List pathDetails { get; set; }
+
+ [SugarColumn(IsIgnore = true)]
+ public int RowIndex { get; set; }
}
}
diff --git a/Sln.Wcs.Model/Domain/LiveTaskDetail.cs b/Sln.Wcs.Model/Domain/LiveTaskDetail.cs
index 1570882..4d9891e 100644
--- a/Sln.Wcs.Model/Domain/LiveTaskDetail.cs
+++ b/Sln.Wcs.Model/Domain/LiveTaskDetail.cs
@@ -161,6 +161,14 @@ public class LiveTaskDetail
[SugarColumn(ColumnName = "execution_mode")]
public int? executionMode { get; set; }
+ ///
+ /// Desc:执行设备编号
+ /// Default:
+ /// Nullable:True
+ ///
+ [SugarColumn(ColumnName = "exec_device")]
+ public string execDevice { get; set; }
+
///
/// Desc:备注
/// Default:
@@ -168,4 +176,33 @@ public class LiveTaskDetail
///
[SugarColumn(ColumnName = "remark")]
public string remark { get; set; }
+
+ [SugarColumn(IsIgnore = true)]
+ public string taskTypeText
+ {
+ get { return taskType switch { 1 => "入库", 2 => "出库", _ => "--" }; }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string taskCategoryText
+ {
+ get { return taskCategory switch { 1 => "包材", 2 => "成品", 3 => "托盘", _ => "--" }; }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string taskStatusText
+ {
+ get { return taskStatus switch { 1 => "待执行", 2 => "执行中", 3 => "已完成", _ => "--" }; }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string executionModeText
+ {
+ get { return executionMode switch { 0 => "自动", 1 => "手动", _ => "--" }; }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string deviceTypeText
+ {
+ get { return deviceType switch { 0 => "输送线", 1 => "AGV", 2 => "提升机", _ => "--" }; }
+ }
+
+ [SugarColumn(IsIgnore = true)]
+ public int RowIndex { get; set; }
}
\ No newline at end of file
diff --git a/Sln.Wcs.Model/Domain/LiveTaskQueue.cs b/Sln.Wcs.Model/Domain/LiveTaskQueue.cs
index 20d3f87..d7138bf 100644
--- a/Sln.Wcs.Model/Domain/LiveTaskQueue.cs
+++ b/Sln.Wcs.Model/Domain/LiveTaskQueue.cs
@@ -167,4 +167,40 @@ public class LiveTaskQueue
[SugarColumn(IsIgnore = true)]
[Navigate(NavigateType.OneToMany, nameof(LiveTaskDetail.taskCode), nameof(taskCode))]
public List taskDetails { get; set; }
+
+ [SugarColumn(IsIgnore = true)]
+ public string taskTypeText
+ {
+ get
+ {
+ return taskType switch { 1 => "入库", 2 => "出库", _ => "--" };
+ }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string taskCategoryText
+ {
+ get
+ {
+ return taskCategory switch { 1 => "包材", 2 => "成品", 3 => "托盘", _ => "--" };
+ }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string taskStatusText
+ {
+ get
+ {
+ return taskStatus switch { 1 => "待执行", 2 => "执行中", 3 => "已完成", _ => "--" };
+ }
+ }
+ [SugarColumn(IsIgnore = true)]
+ public string executionModeText
+ {
+ get
+ {
+ return executionMode switch { 0 => "自动", 1 => "手动", _ => "--" };
+ }
+ }
+
+ [SugarColumn(IsIgnore = true)]
+ public int RowIndex { get; set; }
}
\ No newline at end of file
diff --git a/Sln.Wcs.UI/App.axaml b/Sln.Wcs.UI/App.axaml
index ce86296..31b7d96 100644
--- a/Sln.Wcs.UI/App.axaml
+++ b/Sln.Wcs.UI/App.axaml
@@ -1,6 +1,7 @@
@@ -9,6 +10,7 @@
+
diff --git a/Sln.Wcs.UI/ViewModels/Base/CrudPageViewModel.cs b/Sln.Wcs.UI/ViewModels/Base/CrudPageViewModel.cs
index f35f9c0..2a7d6d5 100644
--- a/Sln.Wcs.UI/ViewModels/Base/CrudPageViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Base/CrudPageViewModel.cs
@@ -69,6 +69,11 @@ public abstract partial class CrudPageViewModel : ObservableObject, ICrudPage
var exp = BuildSearchExpression(SearchText);
list = exp != null ? _service.Query(exp) : _service.Query();
}
+ for (int i = 0; i < list.Count; i++)
+ {
+ var rowProp = typeof(T).GetProperty("RowIndex");
+ rowProp?.SetValue(list[i], i + 1);
+ }
Items = new ObservableCollection(list);
StatusText = $"共 {list.Count} 条记录";
}
diff --git a/Sln.Wcs.UI/ViewModels/Base/FieldConfig.cs b/Sln.Wcs.UI/ViewModels/Base/FieldConfig.cs
index 226c7dc..6277aa1 100644
--- a/Sln.Wcs.UI/ViewModels/Base/FieldConfig.cs
+++ b/Sln.Wcs.UI/ViewModels/Base/FieldConfig.cs
@@ -7,6 +7,13 @@ public class FieldConfig
public FieldType FieldType { get; set; } = FieldType.Text;
public bool IsReadOnly { get; set; }
public bool IsRequired { get; set; }
+ public List Options { get; set; } = new();
+}
+
+public class ComboOption
+{
+ public object? Value { get; set; }
+ public string Display { get; set; } = string.Empty;
}
public enum FieldType
@@ -16,3 +23,61 @@ public enum FieldType
Combo,
CheckBox
}
+
+public static class StandardOptions
+{
+ public static readonly List TaskType = new()
+ {
+ new() { Value = 1, Display = "1 - 入库" },
+ new() { Value = 2, Display = "2 - 出库" },
+ };
+
+ public static readonly List TaskCategory = new()
+ {
+ new() { Value = 1, Display = "1 - 包材" },
+ new() { Value = 2, Display = "2 - 成品" },
+ new() { Value = 3, Display = "3 - 托盘" },
+ };
+
+ public static readonly List TaskStatus = new()
+ {
+ new() { Value = 1, Display = "1 - 待执行" },
+ new() { Value = 2, Display = "2 - 执行中" },
+ new() { Value = 3, Display = "3 - 已完成" },
+ };
+
+ public static readonly List ExecutionMode = new()
+ {
+ new() { Value = 0, Display = "0 - 自动" },
+ new() { Value = 1, Display = "1 - 手动" },
+ };
+
+ public static readonly List DeviceType = new()
+ {
+ new() { Value = 0, Display = "0 - 输送线" },
+ new() { Value = 1, Display = "1 - AGV" },
+ new() { Value = 2, Display = "2 - 提升机" },
+ };
+
+ public static readonly List DeviceStatus = new()
+ {
+ new() { Value = 0, Display = "0 - 正常" },
+ new() { Value = 1, Display = "1 - 在忙" },
+ new() { Value = 2, Display = "2 - 异常" },
+ };
+
+ public static readonly List LocationStatus = new()
+ {
+ new() { Value = 0, Display = "0 - 未使用" },
+ new() { Value = 1, Display = "1 - 已使用" },
+ new() { Value = 2, Display = "2 - 锁库" },
+ new() { Value = 3, Display = "3 - 异常" },
+ };
+
+ public static readonly List OperationType = new()
+ {
+ new() { Value = "0", Display = "0 - 默认读写" },
+ new() { Value = "1", Display = "1 - 只读" },
+ new() { Value = "2", Display = "2 - 只写" },
+ };
+}
diff --git a/Sln.Wcs.UI/ViewModels/Base/LocationInfoViewModel.cs b/Sln.Wcs.UI/ViewModels/Base/LocationInfoViewModel.cs
index 85f2ccb..50abd81 100644
--- a/Sln.Wcs.UI/ViewModels/Base/LocationInfoViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Base/LocationInfoViewModel.cs
@@ -26,7 +26,7 @@ public class LocationInfoViewModel : CrudPageViewModel
new() { PropertyName = "materialCode", DisplayName = "物料编号" },
new() { PropertyName = "palletBarcode", DisplayName = "托盘条码" },
new() { PropertyName = "stackCount", DisplayName = "库存数量" },
- new() { PropertyName = "locationStatus", DisplayName = "库位状态", FieldType = FieldType.Number },
+ new() { PropertyName = "locationStatus", DisplayName = "库位状态", FieldType = FieldType.Combo, Options = StandardOptions.LocationStatus },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
diff --git a/Sln.Wcs.UI/ViewModels/Base/StoreInfoViewModel.cs b/Sln.Wcs.UI/ViewModels/Base/StoreInfoViewModel.cs
index 7b8f37a..f99e1ca 100644
--- a/Sln.Wcs.UI/ViewModels/Base/StoreInfoViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Base/StoreInfoViewModel.cs
@@ -1,15 +1,32 @@
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq.Expressions;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
+using Sln.Wcs.UI.Views.Base;
namespace Sln.Wcs.UI.ViewModels.Base;
-public class StoreInfoViewModel : CrudPageViewModel
+public partial class StoreInfoViewModel : CrudPageViewModel
{
- public StoreInfoViewModel(IBaseStoreInfoService service) : base(service)
+ private readonly IBaseLocationInfoService _locationService;
+ private BaseStoreInfo? _currentStore;
+
+ [ObservableProperty]
+ private bool _isPanelOpen;
+
+ [ObservableProperty]
+ private ObservableCollection _storeLocations = new();
+
+ [ObservableProperty]
+ private string _slidePanelTitle = string.Empty;
+
+ public StoreInfoViewModel(IBaseStoreInfoService service, IBaseLocationInfoService locationService) : base(service)
{
+ _locationService = locationService;
PageTitle = "仓库信息管理";
}
@@ -26,4 +43,84 @@ public class StoreInfoViewModel : CrudPageViewModel
=> x => x.storeCode.Contains(search) || (x.storeName != null && x.storeName.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Base.StoreInfoListView();
+
+ public void LoadLocationsData(BaseStoreInfo store)
+ {
+ _currentStore = store;
+ LoadLocations();
+ SlidePanelTitle = $"库位列表 - {store.storeCode}";
+ }
+
+ [RelayCommand]
+ public void ClosePanel()
+ {
+ IsPanelOpen = false;
+ }
+
+ [RelayCommand]
+ private async System.Threading.Tasks.Task AddLocation()
+ {
+ if (_currentStore is null) return;
+ var entity = new BaseLocationInfo { storeCode = _currentStore.storeCode };
+ var editor = new EntityEditWindow();
+ var result = await editor.ShowDialog(entity, LocationFieldConfigs, false, GetMainWindow());
+ if (result)
+ {
+ _locationService.Insert(entity);
+ LoadLocations();
+ }
+ }
+
+ public async System.Threading.Tasks.Task EditLocationAsync(BaseLocationInfo location)
+ {
+ var editor = new EntityEditWindow();
+ var result = await editor.ShowDialog(location, LocationFieldConfigs, true, GetMainWindow());
+ if (result)
+ {
+ _locationService.Update(location);
+ LoadLocations();
+ }
+ }
+
+ public async System.Threading.Tasks.Task DeleteLocationAsync(BaseLocationInfo location)
+ {
+ var dlg = new Sln.Wcs.UI.Views.Base.ConfirmDialog();
+ if (!await dlg.ShowDialog("确定要删除该库位吗?", GetMainWindow())) return;
+ _locationService.DeleteById(location.objId);
+ LoadLocations();
+ }
+
+ private void LoadLocations()
+ {
+ if (_currentStore is null) return;
+ var list = _locationService.Query(x => x.storeCode == _currentStore.storeCode);
+ for (int i = 0; i < list.Count; i++) list[i].RowIndex = i + 1;
+ StoreLocations = new ObservableCollection(list);
+ }
+
+ public List LocationFieldConfigs => new()
+ {
+ new() { PropertyName = "locationCode", DisplayName = "库位编号", IsRequired = true },
+ new() { PropertyName = "storeCode", DisplayName = "仓库编号", IsRequired = true, IsReadOnly = true },
+ new() { PropertyName = "locationName", DisplayName = "库位名称" },
+ new() { PropertyName = "locationArea", DisplayName = "库位区域" },
+ new() { PropertyName = "locationRows", DisplayName = "排", FieldType = FieldType.Number },
+ new() { PropertyName = "locationColumns", DisplayName = "列", FieldType = FieldType.Number },
+ new() { PropertyName = "locationLayers", DisplayName = "层", FieldType = FieldType.Number },
+ new() { PropertyName = "locationStatus", DisplayName = "库位状态", FieldType = FieldType.Combo, Options = StandardOptions.LocationStatus },
+ new() { PropertyName = "agvPosition", DisplayName = "AGV定位" },
+ new() { PropertyName = "materialCode", DisplayName = "物料编号" },
+ new() { PropertyName = "palletBarcode", DisplayName = "托盘条码" },
+ new() { PropertyName = "stackCount", DisplayName = "库存数量" },
+ new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
+ new() { PropertyName = "remark", DisplayName = "备注" },
+ };
+
+ private Avalonia.Controls.Window GetMainWindow()
+ {
+ return (Avalonia.Controls.Window)Avalonia.Application.Current!
+ .ApplicationLifetime!.GetType()
+ .GetProperty("MainWindow")!
+ .GetValue(Avalonia.Application.Current.ApplicationLifetime)!;
+ }
}
diff --git a/Sln.Wcs.UI/ViewModels/Device/DeviceInfoViewModel.cs b/Sln.Wcs.UI/ViewModels/Device/DeviceInfoViewModel.cs
index 962cee9..12f8ca5 100644
--- a/Sln.Wcs.UI/ViewModels/Device/DeviceInfoViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Device/DeviceInfoViewModel.cs
@@ -36,8 +36,8 @@ public partial class DeviceInfoViewModel : CrudPageViewModel
new() { PropertyName = "deviceName", DisplayName = "设备名称" },
new() { PropertyName = "deviceAlias", DisplayName = "设备别名" },
new() { PropertyName = "deviceSerialNo", DisplayName = "设备序号", FieldType = FieldType.Number },
- new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
- new() { PropertyName = "deviceStatus", DisplayName = "设备状态", FieldType = FieldType.Number },
+ new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Combo, Options = StandardOptions.DeviceType },
+ new() { PropertyName = "deviceStatus", DisplayName = "设备状态", FieldType = FieldType.Combo, Options = StandardOptions.DeviceStatus },
new() { PropertyName = "hostCode", DisplayName = "主机编号", IsRequired = true },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
@@ -98,6 +98,7 @@ public partial class DeviceInfoViewModel : CrudPageViewModel
{
if (_currentDevice is null) return;
var list = _paramService.Query(x => x.deviceCode == _currentDevice.deviceCode);
+ for (int i = 0; i < list.Count; i++) list[i].RowIndex = i + 1;
DeviceParams = new ObservableCollection(list);
}
@@ -109,7 +110,7 @@ public partial class DeviceInfoViewModel : CrudPageViewModel
new() { PropertyName = "paramAddress", DisplayName = "参数地址", IsRequired = true },
new() { PropertyName = "paramType", DisplayName = "参数类型" },
new() { PropertyName = "paramValue", DisplayName = "参数值", FieldType = FieldType.Number },
- new() { PropertyName = "operationType", DisplayName = "操作类型" },
+ new() { PropertyName = "operationType", DisplayName = "操作类型", FieldType = FieldType.Combo, Options = StandardOptions.OperationType },
new() { PropertyName = "operationFrequency", DisplayName = "操作频率(ms)" },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
diff --git a/Sln.Wcs.UI/ViewModels/Device/DeviceParamViewModel.cs b/Sln.Wcs.UI/ViewModels/Device/DeviceParamViewModel.cs
index 95be9d2..d5e656b 100644
--- a/Sln.Wcs.UI/ViewModels/Device/DeviceParamViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Device/DeviceParamViewModel.cs
@@ -21,7 +21,7 @@ public class DeviceParamViewModel : CrudPageViewModel
new() { PropertyName = "paramAddress", DisplayName = "参数地址" },
new() { PropertyName = "paramType", DisplayName = "参数类型" },
new() { PropertyName = "paramValue", DisplayName = "参数值", FieldType = FieldType.Number },
- new() { PropertyName = "operationType", DisplayName = "操作类型" },
+ new() { PropertyName = "operationType", DisplayName = "操作类型", FieldType = FieldType.Combo, Options = StandardOptions.OperationType },
new() { PropertyName = "operationFrequency", DisplayName = "操作频率(ms)" },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
diff --git a/Sln.Wcs.UI/ViewModels/Path/PathDetailsViewModel.cs b/Sln.Wcs.UI/ViewModels/Path/PathDetailsViewModel.cs
index 194fc6a..49bd9e1 100644
--- a/Sln.Wcs.UI/ViewModels/Path/PathDetailsViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Path/PathDetailsViewModel.cs
@@ -19,7 +19,7 @@ public class PathDetailsViewModel : CrudPageViewModel
new() { PropertyName = "pathName", DisplayName = "路径名称" },
new() { PropertyName = "startPoint", DisplayName = "起点" },
new() { PropertyName = "endPoint", DisplayName = "终点" },
- new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
+ new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Combo, Options = StandardOptions.DeviceType },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
diff --git a/Sln.Wcs.UI/ViewModels/Path/PathInfoViewModel.cs b/Sln.Wcs.UI/ViewModels/Path/PathInfoViewModel.cs
index 386297c..ed1cfcd 100644
--- a/Sln.Wcs.UI/ViewModels/Path/PathInfoViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Path/PathInfoViewModel.cs
@@ -34,8 +34,8 @@ public partial class PathInfoViewModel : CrudPageViewModel
{
new() { PropertyName = "pathCode", DisplayName = "路径编号" },
new() { PropertyName = "pathName", DisplayName = "路径名称" },
- new() { PropertyName = "pathType", DisplayName = "路径类型", FieldType = FieldType.Number },
- new() { PropertyName = "pathCategory", DisplayName = "路径类别", FieldType = FieldType.Number },
+ new() { PropertyName = "pathType", DisplayName = "路径类型", FieldType = FieldType.Combo, Options = StandardOptions.TaskType },
+ new() { PropertyName = "pathCategory", DisplayName = "路径类别", FieldType = FieldType.Combo, Options = StandardOptions.TaskCategory },
new() { PropertyName = "startPoint", DisplayName = "起点" },
new() { PropertyName = "endPoint", DisplayName = "终点" },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
@@ -98,6 +98,7 @@ public partial class PathInfoViewModel : CrudPageViewModel
{
if (_currentPath is null) return;
var list = _detailService.Query(x => x.pathCode == _currentPath.pathCode);
+ for (int i = 0; i < list.Count; i++) list[i].RowIndex = i + 1;
DetailItems = new ObservableCollection(list);
}
@@ -107,7 +108,7 @@ public partial class PathInfoViewModel : CrudPageViewModel
new() { PropertyName = "pathName", DisplayName = "路径名称" },
new() { PropertyName = "startPoint", DisplayName = "起点", IsRequired = true },
new() { PropertyName = "endPoint", DisplayName = "终点", IsRequired = true },
- new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
+ new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Combo, Options = StandardOptions.DeviceType },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
diff --git a/Sln.Wcs.UI/ViewModels/Task/CreateTaskViewModel.cs b/Sln.Wcs.UI/ViewModels/Task/CreateTaskViewModel.cs
index 940e91f..691207e 100644
--- a/Sln.Wcs.UI/ViewModels/Task/CreateTaskViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Task/CreateTaskViewModel.cs
@@ -1,3 +1,4 @@
+using System.Collections.ObjectModel;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
@@ -24,11 +25,41 @@ public partial class CreateTaskViewModel : ObservableObject
[ObservableProperty] private string _endLocation = "01";
// ---- 任务属性 ----
- [ObservableProperty] private int _taskType = 1; // 1-入库 2-出库
- [ObservableProperty] private int _taskCategory = 1; // 1-包材 2-成品 3-托盘
+ [ObservableProperty] private int _taskType = 1;
+ [ObservableProperty] private int _taskCategory = 1;
[ObservableProperty] private string _palletBarcode = string.Empty;
[ObservableProperty] private string _materialCode = string.Empty;
+ // ---- 下拉选项 ----
+ public ObservableCollection TaskTypeOptions { get; } = new()
+ {
+ new() { Value = 1, Display = "1 - 入库" },
+ new() { Value = 2, Display = "2 - 出库" },
+ };
+
+ public ObservableCollection TaskCategoryOptions { get; } = new()
+ {
+ new() { Value = 1, Display = "1 - 包材" },
+ new() { Value = 2, Display = "2 - 成品" },
+ new() { Value = 3, Display = "3 - 托盘" },
+ };
+
+ [ObservableProperty]
+ private TaskAttributeOption _selectedTaskType;
+
+ [ObservableProperty]
+ private TaskAttributeOption _selectedTaskCategory;
+
+ partial void OnSelectedTaskTypeChanged(TaskAttributeOption? value)
+ {
+ if (value is not null) TaskType = value.Value;
+ }
+
+ partial void OnSelectedTaskCategoryChanged(TaskAttributeOption? value)
+ {
+ if (value is not null) TaskCategory = value.Value;
+ }
+
// ---- 手动操作选项 ----
[ObservableProperty] private bool _manualPutIn;
[ObservableProperty] private bool _manualTakeOut;
@@ -41,6 +72,8 @@ public partial class CreateTaskViewModel : ObservableObject
{
_taskCreateService = taskCreateService;
_taskQueueService = taskQueueService;
+ _selectedTaskType = TaskTypeOptions[0];
+ _selectedTaskCategory = TaskCategoryOptions[0];
}
public Avalonia.Controls.Control CreateView() => new Views.Task.CreateTaskView();
@@ -85,3 +118,9 @@ public partial class CreateTaskViewModel : ObservableObject
}
}
}
+
+public class TaskAttributeOption
+{
+ public int Value { get; set; }
+ public string Display { get; set; } = string.Empty;
+}
diff --git a/Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs b/Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs
index d918995..b2836b7 100644
--- a/Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Task/ManualTaskViewModel.cs
@@ -19,11 +19,6 @@ public partial class ManualTaskViewModel : ObservableObject
private readonly ILiveTaskDetailService _taskDetailService;
private readonly HttpClient _http;
private readonly SerilogHelper _logger;
-
- private FreeHoistResponse? _allocatedHoist;
-
- private Dictionary hoistDic = new Dictionary();
-
public string PageTitle => "手动执行任务";
[ObservableProperty]
@@ -35,6 +30,9 @@ public partial class ManualTaskViewModel : ObservableObject
[ObservableProperty]
private ObservableCollection _details = new();
+ [ObservableProperty]
+ private LiveTaskDetail? _selectedDetail;
+
[ObservableProperty]
private string _statusText = string.Empty;
@@ -53,6 +51,17 @@ public partial class ManualTaskViewModel : ObservableObject
[ObservableProperty]
private string _dockingPoint = "--";
+ public ObservableCollection HostOptions { get; } = new()
+ {
+ new() { Name = "15栋入库提升机", Code = "1#Host" },
+ new() { Name = "15栋出库提升机", Code = "2#Host" },
+ new() { Name = "14栋提升机", Code = "3#Host" },
+ new() { Name = "13栋提升机", Code = "4#Host" }
+ };
+
+ [ObservableProperty]
+ private HostOption _selectedHostOption;
+
public ManualTaskViewModel(
ILiveTaskQueueService taskQueueService,
ILiveTaskDetailService taskDetailService,
@@ -62,6 +71,7 @@ public partial class ManualTaskViewModel : ObservableObject
_taskDetailService = taskDetailService;
_http = new HttpClient { Timeout = System.TimeSpan.FromSeconds(5) };
_logger = logger;
+ _selectedHostOption = HostOptions[0];
}
public Avalonia.Controls.Control CreateView() => new Views.Task.ManualTaskView();
@@ -75,6 +85,7 @@ public partial class ManualTaskViewModel : ObservableObject
private void RefreshTaskList()
{
List list = _taskQueueService.getLiveTaskQueues(x=>x.executionMode == 1 && x.isFlag == 1);
+ for (int i = 0; i < list.Count; i++) list[i].RowIndex = i + 1;
Tasks = new ObservableCollection(list);
StatusText = list.Count > 0
? $"查询到 {list.Count} 条待执行手动任务"
@@ -84,18 +95,35 @@ public partial class ManualTaskViewModel : ObservableObject
partial void OnSelectedTaskChanged(LiveTaskQueue? value)
{
TaskExecuted = false;
- _allocatedHoist = null;
+ SelectedDetail = null;
HoistInfo = "--";
DockingPoint = "--";
if (value is not null)
LoadDetails(value.taskCode);
}
+ partial void OnSelectedDetailChanged(LiveTaskDetail? value)
+ {
+ if (value is not null && !string.IsNullOrWhiteSpace(value.execDevice))
+ {
+ var parts = value.execDevice.Split('_');
+ if (parts.Length == 2)
+ {
+ HoistInfo = parts[0];
+ DockingPoint = parts[1];
+ return;
+ }
+ }
+ HoistInfo = "--";
+ DockingPoint = "--";
+ }
+
private void LoadDetails(string taskCode)
{
var detailList = _taskDetailService.Query(x => x.taskCode == taskCode)
.OrderBy(d => d.objId)
.ToList();
+ for (int i = 0; i < detailList.Count; i++) detailList[i].RowIndex = i + 1;
Details = new ObservableCollection(detailList);
//StatusText = $"任务 {taskCode},共 {detailList.Count} 条明细";
}
@@ -118,10 +146,16 @@ public partial class ManualTaskViewModel : ObservableObject
return;
}
- var hoistDetail = Details.FirstOrDefault(d => d.deviceType == 2);
- if (hoistDetail is null)
+ if (SelectedDetail is null)
{
- StatusText = "当前任务未包含提升机步骤,无需手动执行";
+ StatusText = "请先在明细列表中选择一条提升机明细";
+ return;
+ }
+
+ var hoistDetail = SelectedDetail;
+ if (hoistDetail.deviceType != 2)
+ {
+ StatusText = "所选明细不是提升机步骤,请选择设备类型为提升机的明细";
return;
}
@@ -130,8 +164,26 @@ public partial class ManualTaskViewModel : ObservableObject
try
{
+
// 1. 获取空闲提升机
- string hostCode = "1#Host";
+ string hostCode = SelectedHostOption?.Code ?? "1#Host";
+ if (hoistDetail.taskType == 2 && hoistDetail.taskCategory == 2 && hoistDetail.startPoint.Contains("15#"))
+ {
+ hostCode = "2#Host";
+ }
+ else
+ {
+ if (hoistDetail.startPoint.Contains("13#"))
+ {
+ hostCode = "4#Host";
+ }else if (hoistDetail.startPoint.Contains("14#"))
+ {
+ hostCode = "3#Host";
+ }else if (hoistDetail.startPoint.Contains("15#"))
+ {
+ hostCode = "1#Host";
+ }
+ }
var freeUrl = $"{HoistBaseUrl}/api/hoist/free?hostCode={Uri.EscapeDataString(hostCode)}";
var freeRes = await _http.GetFromJsonAsync(freeUrl);
@@ -143,12 +195,8 @@ public partial class ManualTaskViewModel : ObservableObject
return;
}
- _allocatedHoist = freeRes;
- HoistInfo = $"{freeRes.deviceName ?? freeRes.deviceCode} (空闲)";
- DockingPoint = $"{freeRes.deviceSerialNo}" ?? "--";
-
StatusText = $"已获取空闲提升机 {freeRes.deviceName ?? freeRes.deviceCode},正在下发任务...";
- hoistDic.Add(SelectedTask.taskCode,freeRes);
+
// 2. 下发提升机调度任务
var dispatchBody = new
{
@@ -176,7 +224,11 @@ public partial class ManualTaskViewModel : ObservableObject
_taskQueueService.Update(SelectedTask);
hoistDetail.taskStatus = 2;
+ hoistDetail.execDevice = $"{freeRes.hostCode}_{freeRes.deviceSerialNo}";
_taskDetailService.Update(hoistDetail);
+
+ HoistInfo = freeRes.hostCode.ToString(); // HoistInfo = freeRes.hostCode;
+ DockingPoint = freeRes.deviceSerialNo.ToString();
});
TaskExecuted = true;
@@ -188,7 +240,6 @@ public partial class ManualTaskViewModel : ObservableObject
catch (HttpRequestException ex)
{
TaskExecuted = false;
- _allocatedHoist = null;
HoistInfo = "--";
StatusText = $"与提升机调度中心通信失败: {ex.Message}";
_logger.Error($"HoistServer 通信失败: {ex.Message}");
@@ -196,7 +247,6 @@ public partial class ManualTaskViewModel : ObservableObject
catch (Exception ex)
{
TaskExecuted = false;
- _allocatedHoist = null;
HoistInfo = "--";
StatusText = $"任务执行失败: {ex.Message}";
_logger.Error($"手动任务执行失败: {ex.Message}");
@@ -219,18 +269,29 @@ public partial class ManualTaskViewModel : ObservableObject
return;
}
- hoistDic.TryGetValue(SelectedTask.taskCode, out _allocatedHoist);
-
- if (_allocatedHoist is null)
+ if (SelectedDetail is null)
{
- StatusText = "请先执行任务获取空闲提升机,再确认物料到位";
+ StatusText = "请先在明细列表中选择一条明细";
return;
}
- var hoistDetail = Details.FirstOrDefault(d => d.deviceType == 2);
- if (hoistDetail is null)
+ var hoistDetail = SelectedDetail;
+
+ if (hoistDetail.deviceType != 2)
{
- StatusText = "未找到提升机任务明细";
+ StatusText = "所选明细不是提升机步骤,请选择设备类型为提升机的明细";
+ return;
+ }
+
+ if (SelectedTask.taskStatus != 2)
+ {
+ StatusText = "当前任务未在执行中,请先执行任务";
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(hoistDetail.execDevice))
+ {
+ StatusText = "当前任务未分配执行设备,请先执行任务";
return;
}
@@ -239,10 +300,14 @@ public partial class ManualTaskViewModel : ObservableObject
try
{
+ var execParts = hoistDetail.execDevice!.Split('_');
+ var execHostCode = execParts[0];
+ var execSerialNo = int.Parse(execParts[1]);
+
var body = new
{
- hostCode = _allocatedHoist.hostCode,
- serialNo = _allocatedHoist.deviceSerialNo,
+ hostCode = execHostCode,
+ serialNo = execSerialNo,
taskCode = hoistDetail.taskCode,
palletBarcode = hoistDetail.palletBarcode ?? "",
startPoint = hoistDetail.startPoint,
@@ -302,4 +367,10 @@ public partial class ManualTaskViewModel : ObservableObject
public bool success { get; set; }
public string? msg { get; set; }
}
+
+ public class HostOption
+ {
+ public string Name { get; set; } = string.Empty;
+ public string Code { get; set; } = string.Empty;
+ }
}
diff --git a/Sln.Wcs.UI/ViewModels/Task/TaskDetailViewModel.cs b/Sln.Wcs.UI/ViewModels/Task/TaskDetailViewModel.cs
index 9e018d0..75cfe0f 100644
--- a/Sln.Wcs.UI/ViewModels/Task/TaskDetailViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Task/TaskDetailViewModel.cs
@@ -21,13 +21,14 @@ public class TaskDetailViewModel : CrudPageViewModel
new() { PropertyName = "palletBarcode", DisplayName = "托盘条码" },
new() { PropertyName = "materialBarcode", DisplayName = "物料条码" },
new() { PropertyName = "materialCount", DisplayName = "物料数量", FieldType = FieldType.Number },
- new() { PropertyName = "taskType", DisplayName = "任务类型", FieldType = FieldType.Number },
- new() { PropertyName = "taskCategory", DisplayName = "任务类别", FieldType = FieldType.Number },
+ new() { PropertyName = "taskType", DisplayName = "任务类型", FieldType = FieldType.Combo, Options = StandardOptions.TaskType },
+ new() { PropertyName = "taskCategory", DisplayName = "任务类别", FieldType = FieldType.Combo, Options = StandardOptions.TaskCategory },
new() { PropertyName = "startPoint", DisplayName = "起始位置" },
new() { PropertyName = "endPoint", DisplayName = "结束位置" },
- new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
+ new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Combo, Options = StandardOptions.DeviceType },
+ new() { PropertyName = "execDevice", DisplayName = "执行设备" },
new() { PropertyName = "isValidate", DisplayName = "校验物料", FieldType = FieldType.CheckBox },
- new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Number },
+ new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Combo, Options = StandardOptions.TaskStatus },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
diff --git a/Sln.Wcs.UI/ViewModels/Task/TaskQueueViewModel.cs b/Sln.Wcs.UI/ViewModels/Task/TaskQueueViewModel.cs
index 496c0a0..d25bb36 100644
--- a/Sln.Wcs.UI/ViewModels/Task/TaskQueueViewModel.cs
+++ b/Sln.Wcs.UI/ViewModels/Task/TaskQueueViewModel.cs
@@ -37,12 +37,12 @@ public partial class TaskQueueViewModel : CrudPageViewModel
new() { PropertyName = "palletBarcode", DisplayName = "托盘条码" },
new() { PropertyName = "materialBarcode", DisplayName = "物料条码" },
new() { PropertyName = "materialCount", DisplayName = "物料数量", FieldType = FieldType.Number },
- new() { PropertyName = "taskType", DisplayName = "任务类型", FieldType = FieldType.Number },
- new() { PropertyName = "taskCategory", DisplayName = "任务类别", FieldType = FieldType.Number },
+ new() { PropertyName = "taskType", DisplayName = "任务类型", FieldType = FieldType.Combo, Options = StandardOptions.TaskType },
+ new() { PropertyName = "taskCategory", DisplayName = "任务类别", FieldType = FieldType.Combo, Options = StandardOptions.TaskCategory },
new() { PropertyName = "startPoint", DisplayName = "起始位置" },
new() { PropertyName = "endPoint", DisplayName = "结束位置" },
new() { PropertyName = "pathCode", DisplayName = "路径编号" },
- new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Number },
+ new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Combo, Options = StandardOptions.TaskStatus },
new() { PropertyName = "taskSteps", DisplayName = "任务步骤", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
@@ -104,6 +104,7 @@ public partial class TaskQueueViewModel : CrudPageViewModel
{
if (_currentTask is null) return;
var list = _detailService.Query(x => x.taskCode == _currentTask.taskCode);
+ for (int i = 0; i < list.Count; i++) list[i].RowIndex = i + 1;
DetailItems = new ObservableCollection(list);
}
@@ -114,9 +115,10 @@ public partial class TaskQueueViewModel : CrudPageViewModel
new() { PropertyName = "materialCode", DisplayName = "物料编号" },
new() { PropertyName = "startPoint", DisplayName = "起始位置" },
new() { PropertyName = "endPoint", DisplayName = "结束位置" },
- new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
+ new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Combo, Options = StandardOptions.DeviceType },
+ new() { PropertyName = "execDevice", DisplayName = "执行设备" },
new() { PropertyName = "isValidate", DisplayName = "校验物料", FieldType = FieldType.CheckBox },
- new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Number },
+ new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Combo, Options = StandardOptions.TaskStatus },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
diff --git a/Sln.Wcs.UI/Views/Base/EntityEditWindow.axaml.cs b/Sln.Wcs.UI/Views/Base/EntityEditWindow.axaml.cs
index 4495595..35d1fb0 100644
--- a/Sln.Wcs.UI/Views/Base/EntityEditWindow.axaml.cs
+++ b/Sln.Wcs.UI/Views/Base/EntityEditWindow.axaml.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Data;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Styling;
@@ -88,6 +89,42 @@ public partial class EntityEditWindow : Window
prop.SetValue(_entity, cb.IsChecked == true ? 1 : 0);
input = cb;
}
+ else if (field.FieldType == FieldType.Combo)
+ {
+ var combo = new ComboBox
+ {
+ ItemsSource = field.Options,
+ DisplayMemberBinding = new Binding("Display"),
+ IsEnabled = !field.IsReadOnly,
+ Background = tbBg,
+ Foreground = tbFg,
+ BorderBrush = tbBorder,
+ BorderThickness = new Thickness(1),
+ CornerRadius = new CornerRadius(3),
+ Padding = new Thickness(8, 5),
+ FontSize = 12,
+ HorizontalAlignment = HorizontalAlignment.Stretch,
+ };
+
+ // 选中当前值对应的选项
+ ComboOption? selected = null;
+ foreach (ComboOption opt in field.Options)
+ {
+ if (Equals(opt.Value, value))
+ {
+ selected = opt;
+ break;
+ }
+ }
+ combo.SelectedItem = selected;
+
+ combo.SelectionChanged += (_, _) =>
+ {
+ if (combo.SelectedItem is ComboOption opt)
+ prop.SetValue(_entity, opt.Value);
+ };
+ input = combo;
+ }
else
{
var tb = new TextBox
diff --git a/Sln.Wcs.UI/Views/Base/LocationInfoListView.axaml b/Sln.Wcs.UI/Views/Base/LocationInfoListView.axaml
index 91fa5ec..1246081 100644
--- a/Sln.Wcs.UI/Views/Base/LocationInfoListView.axaml
+++ b/Sln.Wcs.UI/Views/Base/LocationInfoListView.axaml
@@ -8,38 +8,40 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sln.Wcs.UI/Views/Base/MaterialInfoListView.axaml b/Sln.Wcs.UI/Views/Base/MaterialInfoListView.axaml
index 8634a97..8744938 100644
--- a/Sln.Wcs.UI/Views/Base/MaterialInfoListView.axaml
+++ b/Sln.Wcs.UI/Views/Base/MaterialInfoListView.axaml
@@ -8,30 +8,32 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml b/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml
index 027aae3..a42f8d3 100644
--- a/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml
+++ b/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml
@@ -1,38 +1,108 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml.cs b/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml.cs
index 56670c2..6c646d5 100644
--- a/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml.cs
+++ b/Sln.Wcs.UI/Views/Base/StoreInfoListView.axaml.cs
@@ -1,5 +1,7 @@
using Avalonia.Controls;
+using Avalonia.Input;
using Avalonia.Interactivity;
+using Avalonia.Media;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Base;
@@ -28,4 +30,99 @@ public partial class StoreInfoListView : UserControl
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
+
+ private async void Locations_Click(object? sender, RoutedEventArgs e)
+ {
+ if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BaseStoreInfo store && DataContext is StoreInfoViewModel vm)
+ {
+ vm.LoadLocationsData(store);
+ Backdrop.Opacity = 0;
+ if (SlideContent.RenderTransform is TranslateTransform transform)
+ transform.X = 3000;
+ vm.IsPanelOpen = true;
+ await AnimateSlideIn();
+ }
+ }
+
+ private async void ClosePanel_Click(object? sender, RoutedEventArgs e)
+ {
+ await AnimateSlideOut();
+ if (DataContext is StoreInfoViewModel vm)
+ vm.ClosePanel();
+ }
+
+ private async void Backdrop_Pressed(object? sender, PointerPressedEventArgs e)
+ {
+ await AnimateSlideOut();
+ if (DataContext is StoreInfoViewModel vm)
+ vm.ClosePanel();
+ }
+
+ private async void LocationEdit_Click(object? sender, RoutedEventArgs e)
+ {
+ if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BaseLocationInfo location && DataContext is StoreInfoViewModel vm)
+ await vm.EditLocationAsync(location);
+ }
+
+ private async void LocationDelete_Click(object? sender, RoutedEventArgs e)
+ {
+ if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BaseLocationInfo location && DataContext is StoreInfoViewModel vm)
+ await vm.DeleteLocationAsync(location);
+ }
+
+ private async System.Threading.Tasks.Task AnimateSlideIn()
+ {
+ var transform = SlideContent.RenderTransform as TranslateTransform;
+ if (transform is null) return;
+
+ double slideFrom = transform.X;
+ int steps = 16;
+ int delay = 16;
+
+ for (int i = 1; i <= steps; i++)
+ {
+ double t = (double)i / steps;
+ double eased = 1.0 - System.Math.Pow(1.0 - t, 3.0);
+ transform.X = slideFrom + (0 - slideFrom) * eased;
+ await System.Threading.Tasks.Task.Delay(delay);
+ }
+ transform.X = 0;
+
+ int fadeSteps = 10;
+ int fadeDelay = 15;
+ for (int i = 1; i <= fadeSteps; i++)
+ {
+ Backdrop.Opacity = (double)i / fadeSteps;
+ await System.Threading.Tasks.Task.Delay(fadeDelay);
+ }
+ Backdrop.Opacity = 1;
+ }
+
+ private async System.Threading.Tasks.Task AnimateSlideOut()
+ {
+ var transform = SlideContent.RenderTransform as TranslateTransform;
+ if (transform is null) return;
+
+ int fadeSteps = 8;
+ int fadeDelay = 12;
+ for (int i = fadeSteps; i >= 0; i--)
+ {
+ Backdrop.Opacity = (double)i / fadeSteps;
+ await System.Threading.Tasks.Task.Delay(fadeDelay);
+ }
+ Backdrop.Opacity = 0;
+
+ double slideFrom = transform.X;
+ int slideSteps = 16;
+ int slideDelay = 14;
+
+ for (int i = 1; i <= slideSteps; i++)
+ {
+ double t = (double)i / slideSteps;
+ double eased = t * t * t;
+ transform.X = slideFrom + (3000 - slideFrom) * eased;
+ await System.Threading.Tasks.Task.Delay(slideDelay);
+ }
+ transform.X = 3000;
+ }
}
diff --git a/Sln.Wcs.UI/Views/Device/DeviceHostListView.axaml b/Sln.Wcs.UI/Views/Device/DeviceHostListView.axaml
index f29d67c..d4007c6 100644
--- a/Sln.Wcs.UI/Views/Device/DeviceHostListView.axaml
+++ b/Sln.Wcs.UI/Views/Device/DeviceHostListView.axaml
@@ -9,15 +9,16 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+