add - 添加 UI 界面

dev
WenJY 6 days ago
parent 23b8c8b3a2
commit a97aca845e

@ -4,7 +4,11 @@
"Bash(dotnet build *)",
"Bash(git -C \"/Users/wenxiansheng/Public/WorkSpace/Mesnac/项目资料/研发项目/基于多场景应用的 WCS 通用平台研发/程序设计/wcs_core\" log --all --oneline --grep=\"HikRobot\" --grep=\"HikRoBot\")",
"Bash(git -C \"/Users/wenxiansheng/Public/WorkSpace/Mesnac/项目资料/研发项目/基于多场景应用的 WCS 通用平台研发/程序设计/wcs_core\" log --all --oneline -S \"HikRobot\")",
"Bash(git -C \"/Users/wenxiansheng/Public/WorkSpace/Mesnac/项目资料/研发项目/基于多场景应用的 WCS 通用平台研发/程序设计/wcs_core\" log --all --oneline -S \"HikRoBot\")"
"Bash(git -C \"/Users/wenxiansheng/Public/WorkSpace/Mesnac/项目资料/研发项目/基于多场景应用的 WCS 通用平台研发/程序设计/wcs_core\" log --all --oneline -S \"HikRoBot\")",
"Bash(dotnet --version)",
"Bash(dotnet sln *)",
"Bash(dotnet clean *)",
"Bash(cat)"
]
}
}

@ -0,0 +1,13 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Sln.Wcs.UI"
x:Class="Sln.Wcs.UI.App"
RequestedThemeVariant="Dark">
<Application.DataTemplates>
<local:ViewLocator />
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

@ -0,0 +1,110 @@
using System;
using System.Reflection;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Com.Ctrip.Framework.Apollo;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NeoSmart.Caching.Sqlite;
using Sln.Wcs.Repository;
using Sln.Wcs.Serilog;
using Sln.Wcs.UI.ViewModels;
using Sln.Wcs.UI.Views;
using ZiggyCreatures.Caching.Fusion;
using ZiggyCreatures.Caching.Fusion.Serialization.NewtonsoftJson;
namespace Sln.Wcs.UI;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// ---- 独立初始化 WCS 后端服务 ----
var services = new ServiceCollection();
// 1. Apollo 配置中心
var basePath = AppContext.BaseDirectory;
var localConfig = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
services.AddSingleton<IConfiguration>(localConfig);
var apolloConfigSection = localConfig.GetSection("apollo");
var apolloConfig = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddApollo(apolloConfigSection)
.AddDefault()
.Build();
// 替换为 Apollo 增强配置
services.Remove(new ServiceDescriptor(typeof(IConfiguration), localConfig));
services.AddSingleton<IConfiguration>(apolloConfig);
// 2. 加载程序集并扫描注册 DI
var assemblies = new[]
{
Assembly.LoadFrom(Path.Combine(basePath, "Sln.Wcs.Common.dll")),
Assembly.LoadFrom(Path.Combine(basePath, "Sln.Wcs.Cache.dll")),
Assembly.LoadFrom(Path.Combine(basePath, "Sln.Wcs.Repository.dll")),
};
services.Scan(scan => scan.FromAssemblies(assemblies)
.AddClasses()
.AsImplementedInterfaces()
.AsSelf()
.WithTransientLifetime());
// 3. 注册日志
services.AddSingleton(typeof(SerilogHelper));
// 4. SqlSugar + FusionCache
services.AddSqlSugarSetup();
services.AddFusionCache()
.WithSerializer(new FusionCacheNewtonsoftJsonSerializer())
.WithDistributedCache(new SqliteCache(new SqliteCacheOptions
{
CachePath = apolloConfig["cachePath"]!
}));
// 5. 注册 ViewModel
services.AddSingleton<ViewModels.NavigationViewModel>();
services.AddSingleton<ViewModels.HomePageViewModel>();
services.AddTransient<ViewModels.Device.DeviceHostViewModel>();
services.AddTransient<ViewModels.Device.DeviceInfoViewModel>();
services.AddTransient<ViewModels.Device.DeviceParamViewModel>();
services.AddTransient<ViewModels.Base.LocationInfoViewModel>();
services.AddTransient<ViewModels.Base.MaterialInfoViewModel>();
services.AddTransient<ViewModels.Base.StoreInfoViewModel>();
services.AddTransient<ViewModels.Path.PathInfoViewModel>();
services.AddTransient<ViewModels.Path.PathDetailsViewModel>();
services.AddTransient<ViewModels.Task.TaskQueueViewModel>();
services.AddTransient<ViewModels.Task.TaskDetailViewModel>();
var serviceProvider = services.BuildServiceProvider();
// ---- 启动后初始化 ----
// 启用 Serilog
serviceProvider.UseSerilogExtensions();
var config = serviceProvider.GetRequiredService<IConfiguration>();
var log = serviceProvider.GetRequiredService<SerilogHelper>();
log.Info($"系统启动成功,日志存放位置:{config["logPath"]}");
// 创建主窗口
desktop.MainWindow = new MainWindow(serviceProvider.GetRequiredService<ViewModels.NavigationViewModel>());
}
base.OnFrameworkInitializationCompleted();
}
}

@ -0,0 +1,17 @@
using Avalonia;
using System;
namespace Sln.Wcs.UI;
sealed class Program
{
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}

@ -0,0 +1,48 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.1.5" />
<PackageReference Include="Avalonia.Desktop" Version="11.1.5" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.5" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.1.5" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.5" />
<PackageReference Include="Avalonia.Diagnostics" Version="11.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Com.Ctrip.Framework.Apollo" Version="2.11.0" />
<PackageReference Include="Com.Ctrip.Framework.Apollo.Configuration" Version="2.11.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Scrutor" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sln.Wcs.Cache\Sln.Wcs.Cache.csproj" />
<ProjectReference Include="..\Sln.Wcs.Common\Sln.Wcs.Common.csproj" />
<ProjectReference Include="..\Sln.Wcs.Model\Sln.Wcs.Model.csproj" />
<ProjectReference Include="..\Sln.Wcs.Repository\Sln.Wcs.Repository.csproj" />
<ProjectReference Include="..\Sln.Wcs.Serilog\Sln.Wcs.Serilog.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

@ -0,0 +1,30 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
namespace Sln.Wcs.UI;
public class ViewLocator : IDataTemplate
{
public Control? Build(object? param)
{
if (param is null)
return new TextBlock { Text = "DataContext is null" };
var viewName = param.GetType().FullName!
.Replace("ViewModels", "Views")
.Replace("ViewModel", "Window");
var type = Type.GetType(viewName);
if (type is null)
return new TextBlock { Text = $"View not found: {viewName}" };
return (Control)Activator.CreateInstance(type)!;
}
public bool Match(object? data)
{
return data is ObservableObject;
}
}

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Sln.Wcs.Repository.service.@base;
using Sln.Wcs.UI.Views.Base;
namespace Sln.Wcs.UI.ViewModels.Base;
public interface ICrudPageViewModel
{
void Load();
Avalonia.Controls.Control CreateView();
}
public abstract partial class CrudPageViewModel<T> : ObservableObject, ICrudPageViewModel where T : class, new()
{
protected readonly IBaseService<T> _service;
[ObservableProperty]
private ObservableCollection<T> _items = new();
[ObservableProperty]
private T? _selectedItem;
[ObservableProperty]
private string _searchText = string.Empty;
[ObservableProperty]
private string _pageTitle = string.Empty;
[ObservableProperty]
private string _statusText = string.Empty;
public abstract List<FieldConfig> FieldConfigs { get; }
public abstract Avalonia.Controls.Control CreateView();
/// <summary>
/// Expression to search by name/code field. Override to customize.
/// Returns null to skip filtering (load all).
/// </summary>
protected abstract Expression<Func<T, bool>>? BuildSearchExpression(string search);
protected CrudPageViewModel(IBaseService<T> service)
{
_service = service;
AddCommand = new AsyncRelayCommand(Add);
EditCommand = new AsyncRelayCommand(Edit);
}
[RelayCommand]
public void Load()
{
Console.WriteLine($"[CRUD] Load 调用, T={typeof(T).Name}, SearchText='{SearchText}'");
try
{
List<T> list;
if (string.IsNullOrWhiteSpace(SearchText))
{
Console.WriteLine($"[CRUD] 调用 _service.Query() 无过滤");
list = _service.Query();
}
else
{
Console.WriteLine($"[CRUD] 调用 _service.Query(expression)");
var exp = BuildSearchExpression(SearchText);
list = exp != null ? _service.Query(exp) : _service.Query();
}
Console.WriteLine($"[CRUD] _service.Query() 返回 {list.Count} 条记录");
Items = new ObservableCollection<T>(list);
StatusText = $"共 {list.Count} 条记录";
Console.WriteLine($"[CRUD] Items 已设置, Count={Items.Count}");
}
catch (Exception ex)
{
Console.WriteLine($"[CRUD] Load 异常: {ex}");
StatusText = $"加载失败: {ex.Message}";
}
}
public AsyncRelayCommand AddCommand { get; }
public AsyncRelayCommand EditCommand { get; }
private async System.Threading.Tasks.Task Add()
{
var entity = new T();
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(entity, FieldConfigs, false, GetMainWindow());
if (result)
{
_service.Insert(entity);
Load();
StatusText = "新增成功";
}
}
private async System.Threading.Tasks.Task Edit()
{
if (SelectedItem is null) return;
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(SelectedItem, FieldConfigs, true, GetMainWindow());
if (result)
{
_service.Update(SelectedItem);
Load();
StatusText = "编辑成功";
}
}
[RelayCommand]
private void Delete()
{
if (SelectedItem is null) return;
var prop = typeof(T).GetProperty("objId") ?? typeof(T).GetProperty("ObjId");
if (prop is null) return;
var id = prop.GetValue(SelectedItem);
_service.DeleteById(id!);
Load();
StatusText = "删除成功";
}
[RelayCommand]
private void Search()
{
Load();
}
private Avalonia.Controls.Window GetMainWindow()
{
return (Avalonia.Controls.Window)Avalonia.Application.Current!
.ApplicationLifetime!.GetType()
.GetProperty("MainWindow")!
.GetValue(Avalonia.Application.Current.ApplicationLifetime)!;
}
}

@ -0,0 +1,18 @@
namespace Sln.Wcs.UI.ViewModels.Base;
public class FieldConfig
{
public string PropertyName { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
public FieldType FieldType { get; set; } = FieldType.Text;
public bool IsReadOnly { get; set; }
public bool IsRequired { get; set; }
}
public enum FieldType
{
Text,
Number,
Combo,
CheckBox
}

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Base;
public class LocationInfoViewModel : CrudPageViewModel<BaseLocationInfo>
{
public LocationInfoViewModel(IBaseLocationInfoService service) : base(service)
{
PageTitle = "库位信息管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "locationCode", DisplayName = "库位编号" },
new() { PropertyName = "locationName", DisplayName = "库位名称" },
new() { PropertyName = "locationArea", DisplayName = "库位区域" },
new() { PropertyName = "storeCode", DisplayName = "所属仓库" },
new() { PropertyName = "locationRows", DisplayName = "排", FieldType = FieldType.Number },
new() { PropertyName = "locationColumns", DisplayName = "列", FieldType = FieldType.Number },
new() { PropertyName = "locationLayers", DisplayName = "层", FieldType = FieldType.Number },
new() { PropertyName = "agvPosition", DisplayName = "AGV定位" },
new() { PropertyName = "materialCode", DisplayName = "物料编号" },
new() { PropertyName = "palletBarcode", DisplayName = "托盘条码" },
new() { PropertyName = "stackCount", DisplayName = "库存数量" },
new() { PropertyName = "locationStatus", DisplayName = "库位状态", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BaseLocationInfo, bool>>? BuildSearchExpression(string search)
=> x => (x.locationCode != null && x.locationCode.Contains(search))
|| (x.locationName != null && x.locationName.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Base.LocationInfoListView();
}

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Base;
public class MaterialInfoViewModel : CrudPageViewModel<BaseMaterialInfo>
{
public MaterialInfoViewModel(IBaseMaterialInfoService service) : base(service)
{
PageTitle = "物料信息管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "materialCode", DisplayName = "物料编号", IsRequired = true },
new() { PropertyName = "materialName", DisplayName = "物料名称" },
new() { PropertyName = "materialType", DisplayName = "物料类型" },
new() { PropertyName = "materialBarcode", DisplayName = "物料条码" },
new() { PropertyName = "minStorageCycle", DisplayName = "最短存放周期", FieldType = FieldType.Number },
new() { PropertyName = "maxStorageCycle", DisplayName = "最长存放周期", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BaseMaterialInfo, bool>>? BuildSearchExpression(string search)
=> x => x.materialCode.Contains(search) || (x.materialName != null && x.materialName.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Base.MaterialInfoListView();
}

@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Base;
public class StoreInfoViewModel : CrudPageViewModel<BaseStoreInfo>
{
public StoreInfoViewModel(IBaseStoreInfoService service) : base(service)
{
PageTitle = "仓库信息管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "storeCode", DisplayName = "仓库编号", IsRequired = true },
new() { PropertyName = "storeName", DisplayName = "仓库名称" },
new() { PropertyName = "storeType", DisplayName = "仓库类型", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BaseStoreInfo, bool>>? BuildSearchExpression(string search)
=> 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();
}

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Device;
public class DeviceHostViewModel : CrudPageViewModel<BaseDeviceHost>
{
public DeviceHostViewModel(IBaseDeviceHostService service) : base(service)
{
PageTitle = "设备主机管理";
System.Console.WriteLine("[VM] DeviceHostViewModel 构造, service 类型: " + service.GetType().FullName);
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "hostCode", DisplayName = "主机编号", IsRequired = true },
new() { PropertyName = "hostName", DisplayName = "主机名称" },
new() { PropertyName = "hostType", DisplayName = "主机类型", FieldType = FieldType.Number },
new() { PropertyName = "hostIP", DisplayName = "主机IP" },
new() { PropertyName = "hostPort", DisplayName = "主机端口", FieldType = FieldType.Number },
new() { PropertyName = "hostPath", DisplayName = "主机路径" },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BaseDeviceHost, bool>>? BuildSearchExpression(string search)
=> x => x.hostCode.Contains(search) || x.hostName.Contains(search);
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Device.DeviceHostListView();
}

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Device;
public class DeviceInfoViewModel : CrudPageViewModel<BaseDeviceInfo>
{
public DeviceInfoViewModel(IBaseDeviceInfoService service) : base(service)
{
PageTitle = "设备信息管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "deviceCode", DisplayName = "设备编号", IsRequired = true },
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 = "hostCode", DisplayName = "主机编号", IsRequired = true },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BaseDeviceInfo, bool>>? BuildSearchExpression(string search)
=> x => x.deviceCode.Contains(search) || (x.deviceName != null && x.deviceName.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Device.DeviceInfoListView();
}

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Device;
public class DeviceParamViewModel : CrudPageViewModel<BaseDeviceParam>
{
public DeviceParamViewModel(IBaseDeviceParamService service) : base(service)
{
PageTitle = "设备参数管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "paramCode", DisplayName = "参数编号", IsRequired = true },
new() { PropertyName = "deviceCode", DisplayName = "设备编号", IsRequired = true },
new() { PropertyName = "paramName", DisplayName = "参数名称" },
new() { PropertyName = "paramAddress", DisplayName = "参数地址" },
new() { PropertyName = "paramType", DisplayName = "参数类型" },
new() { PropertyName = "paramValue", DisplayName = "参数值", FieldType = FieldType.Number },
new() { PropertyName = "operationType", DisplayName = "操作类型" },
new() { PropertyName = "operationFrequency", DisplayName = "操作频率(ms)" },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BaseDeviceParam, bool>>? BuildSearchExpression(string search)
=> x => x.paramCode.Contains(search) || x.paramName.Contains(search);
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Device.DeviceParamListView();
}

@ -0,0 +1,15 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Sln.Wcs.UI.ViewModels;
public partial class HomePageViewModel : ObservableObject
{
[ObservableProperty]
private string _title = "基于多场景应用的 WCS 通用平台";
[ObservableProperty]
private string _version = "V1.0.0";
[ObservableProperty]
private string _description = "基于 .NET 8.0 + Avalonia UI 构建的跨平台仓库控制系统,支持多种硬件设备协议(海康 AGV、提升机、西门子/汇川 PLC提供统一的调度、监控和管理能力。";
}

@ -0,0 +1,109 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Configuration;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.Serilog;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace Sln.Wcs.UI.ViewModels;
public partial class MainViewModel : ObservableObject
{
private readonly SerilogHelper _log;
private readonly IConfiguration _config;
private readonly IBaseDeviceInfoService _deviceInfoService;
private readonly ISqlSugarClient _db;
[ObservableProperty]
private string _appTitle = "基于多场景应用的 WCS 通用平台";
[ObservableProperty]
private string _appVersion = "V1.0.0";
[ObservableProperty]
private string _statusMessage = "系统就绪,点击'加载设备'获取设备列表";
[ObservableProperty]
private int _deviceCount;
[ObservableProperty]
private ObservableCollection<DeviceSummary> _devices = new();
public MainViewModel(
SerilogHelper log,
IConfiguration config,
IBaseDeviceInfoService deviceInfoService,
ISqlSugarClient db)
{
_log = log;
_config = config;
_deviceInfoService = deviceInfoService;
_db = db;
LoadDevicesCommand = new AsyncRelayCommand(LoadDevicesAsync);
}
public AsyncRelayCommand LoadDevicesCommand { get; }
private async System.Threading.Tasks.Task LoadDevicesAsync()
{
StatusMessage = "正在加载设备列表...";
try
{
var devices = await System.Threading.Tasks.Task.Run(() =>
_deviceInfoService.GetDeviceInfos(x => x.isFlag == 1).ToList());
Devices.Clear();
foreach (var d in devices)
{
Devices.Add(new DeviceSummary
{
DeviceCode = d.deviceCode,
DeviceName = d.deviceName ?? "",
DeviceType = ParseDeviceType(d.deviceType),
HostCode = d.hostCode,
IsFlag = d.isFlag == 1
});
}
DeviceCount = Devices.Count;
StatusMessage = $"已加载 {DeviceCount} 台设备";
_log.Info($"UI: 设备列表加载完成,共{DeviceCount}台");
}
catch (Exception ex)
{
StatusMessage = $"加载设备失败: {ex.Message}";
_log.Error($"UI: 设备列表加载失败 - {ex.Message}");
}
}
[RelayCommand]
private void RefreshStatus()
{
var apolloEnv = _config["apollo:Env"] ?? "未知";
var logPath = _config["logPath"] ?? "未配置";
StatusMessage = $"Apollo环境: {apolloEnv} | 日志路径: {logPath} | 已连接设备: {DeviceCount}";
}
private static string ParseDeviceType(int? deviceType) => deviceType switch
{
0 => "输送线",
1 => "AGV",
2 => "提升机",
_ => "未知"
};
}
public class DeviceSummary
{
public string DeviceCode { get; set; } = string.Empty;
public string DeviceName { get; set; } = string.Empty;
public string DeviceType { get; set; } = string.Empty;
public string HostCode { get; set; } = string.Empty;
public bool IsFlag { get; set; }
}

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Sln.Wcs.UI.ViewModels.Base;
using Sln.Wcs.UI.ViewModels.Device;
using Sln.Wcs.UI.ViewModels.Path;
using Sln.Wcs.UI.ViewModels.Task;
using Sln.Wcs.UI.Views;
namespace Sln.Wcs.UI.ViewModels;
public partial class NavigationViewModel : ObservableObject
{
private readonly IServiceProvider _sp;
private readonly Dictionary<Type, ICrudPageViewModel> _vmCache = new();
private readonly Dictionary<Type, Control> _viewCache = new();
private Control? _homeView;
public ObservableCollection<TopMenuItem> TopMenuItems { get; } = new();
public event Action<Control?>? PageChanged;
public NavigationViewModel(IServiceProvider sp)
{
_sp = sp;
BuildMenu();
}
public void LoadDefaultPage() => ShowHome();
private void BuildMenu()
{
TopMenuItems.Clear();
TopMenuItems.Add(new TopMenuItem("首页", ShowHome));
TopMenuItems.Add(new TopMenuItem("基础数据", new List<SubMenuItem>
{
new("库位信息", () => NavigateTo<LocationInfoViewModel>()),
new("物料信息", () => NavigateTo<MaterialInfoViewModel>()),
new("仓库信息", () => NavigateTo<StoreInfoViewModel>()),
}));
TopMenuItems.Add(new TopMenuItem("设备管理", new List<SubMenuItem>
{
new("设备主机", () => NavigateTo<DeviceHostViewModel>()),
new("设备信息", () => NavigateTo<DeviceInfoViewModel>()),
new("设备参数", () => NavigateTo<DeviceParamViewModel>()),
}));
TopMenuItems.Add(new TopMenuItem("路径管理", new List<SubMenuItem>
{
new("路径信息", () => NavigateTo<PathInfoViewModel>()),
new("路径明细", () => NavigateTo<PathDetailsViewModel>()),
}));
TopMenuItems.Add(new TopMenuItem("任务管理", new List<SubMenuItem>
{
new("任务队列", () => NavigateTo<TaskQueueViewModel>()),
new("任务明细", () => NavigateTo<TaskDetailViewModel>()),
}));
}
private void ShowHome()
{
if (_homeView == null)
{
var vm = _sp.GetRequiredService<HomePageViewModel>();
_homeView = new HomePageView { DataContext = vm };
}
PageChanged?.Invoke(_homeView);
}
private void NavigateTo<T>() where T : ICrudPageViewModel
{
var type = typeof(T);
if (!_vmCache.TryGetValue(type, out var vm))
{
vm = _sp.GetRequiredService<T>();
_vmCache[type] = vm;
}
if (!_viewCache.TryGetValue(type, out var view))
{
view = vm.CreateView();
view.DataContext = vm;
_viewCache[type] = view;
}
PageChanged?.Invoke(view);
vm.Load();
}
}
public class TopMenuItem
{
public string Label { get; set; }
public List<SubMenuItem>? Children { get; set; }
public Action? Action { get; set; }
public TopMenuItem(string label, Action action)
{
Label = label; Action = action;
}
public TopMenuItem(string label, List<SubMenuItem> children)
{
Label = label; Children = children;
}
}
public class SubMenuItem
{
public string Label { get; set; }
public Action Action { get; set; }
public SubMenuItem(string label, Action action)
{
Label = label; Action = action;
}
}

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Path;
public class PathDetailsViewModel : CrudPageViewModel<BasePathDetails>
{
public PathDetailsViewModel(IBasePathDetailsService service) : base(service)
{
PageTitle = "路径明细管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "pathCode", DisplayName = "路径编号" },
new() { PropertyName = "pathName", DisplayName = "路径名称" },
new() { PropertyName = "startPoint", DisplayName = "起点" },
new() { PropertyName = "endPoint", DisplayName = "终点" },
new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BasePathDetails, bool>>? BuildSearchExpression(string search)
=> x => (x.pathCode != null && x.pathCode.Contains(search))
|| (x.pathName != null && x.pathName.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Path.PathDetailsListView();
}

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Path;
public class PathInfoViewModel : CrudPageViewModel<BasePathInfo>
{
public PathInfoViewModel(IBasePathInfoService service) : base(service)
{
PageTitle = "路径信息管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "pathCode", DisplayName = "路径编号" },
new() { PropertyName = "pathName", DisplayName = "路径名称" },
new() { PropertyName = "pathType", DisplayName = "路径类型", FieldType = FieldType.Number },
new() { PropertyName = "pathCategory", DisplayName = "路径类别", FieldType = FieldType.Number },
new() { PropertyName = "startPoint", DisplayName = "起点" },
new() { PropertyName = "endPoint", DisplayName = "终点" },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<BasePathInfo, bool>>? BuildSearchExpression(string search)
=> x => (x.pathCode != null && x.pathCode.Contains(search))
|| (x.pathName != null && x.pathName.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Path.PathInfoListView();
}

@ -0,0 +1,40 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Task;
public class TaskDetailViewModel : CrudPageViewModel<LiveTaskDetail>
{
public TaskDetailViewModel(ILiveTaskDetailService service) : base(service)
{
PageTitle = "任务明细管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "taskCode", DisplayName = "任务编号" },
new() { PropertyName = "pathCode", DisplayName = "路径编号" },
new() { PropertyName = "materialCode", DisplayName = "物料编号" },
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 = "startPoint", DisplayName = "起始位置" },
new() { PropertyName = "endPoint", DisplayName = "结束位置" },
new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
new() { PropertyName = "isValidate", DisplayName = "校验物料", FieldType = FieldType.CheckBox },
new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<LiveTaskDetail, bool>>? BuildSearchExpression(string search)
=> x => (x.taskCode != null && x.taskCode.Contains(search))
|| (x.materialCode != null && x.materialCode.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Task.TaskDetailListView();
}

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.ViewModels.Task;
public class TaskQueueViewModel : CrudPageViewModel<LiveTaskQueue>
{
public TaskQueueViewModel(ILiveTaskQueueService service) : base(service)
{
PageTitle = "任务队列管理";
}
public override List<FieldConfig> FieldConfigs => new()
{
new() { PropertyName = "taskCode", DisplayName = "任务编号" },
new() { PropertyName = "materialCode", DisplayName = "物料编号" },
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 = "startPoint", DisplayName = "起始位置" },
new() { PropertyName = "endPoint", DisplayName = "结束位置" },
new() { PropertyName = "pathCode", DisplayName = "路径编号" },
new() { PropertyName = "taskStatus", DisplayName = "任务状态", FieldType = FieldType.Number },
new() { PropertyName = "taskSteps", DisplayName = "任务步骤", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
protected override Expression<Func<LiveTaskQueue, bool>>? BuildSearchExpression(string search)
=> x => (x.taskCode != null && x.taskCode.Contains(search))
|| (x.materialCode != null && x.materialCode.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Task.TaskQueueListView();
}

@ -0,0 +1,28 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Base.EntityEditWindow"
x:CompileBindings="False"
SizeToContent="WidthAndHeight"
MaxWidth="750" MaxHeight="700"
WindowStartupLocation="CenterOwner"
Title="编辑"
Background="#0F1620"
Foreground="#BCC8D6"
CanResize="False">
<Grid RowDefinitions="*,Auto" Margin="20,16,20,16">
<ScrollViewer Grid.Row="0">
<WrapPanel x:Name="FormPanel" />
</ScrollViewer>
<StackPanel Grid.Row="1" Orientation="Horizontal"
HorizontalAlignment="Right" Spacing="10" Margin="0,16,0,0">
<Button x:Name="CancelBtn" Content="取消"
Background="#0F1F38" Foreground="#8B9BB5"
Padding="22,8" />
<Button x:Name="SaveBtn" Content="保存"
Background="#1565C0" Foreground="White"
Padding="22,8" />
</StackPanel>
</Grid>
</Window>

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Base;
public partial class EntityEditWindow : Window
{
private object? _entity;
private TaskCompletionSource<bool>? _tcs;
public EntityEditWindow()
{
InitializeComponent();
CancelBtn.Click += (_, _) => CloseWith(false);
SaveBtn.Click += (_, _) => CloseWith(true);
}
public Task<bool> ShowDialog(object entity, List<FieldConfig> fields, bool isEdit, Window owner)
{
_entity = entity;
Title = isEdit ? "编辑" : "新增";
BuildForm(fields);
_tcs = new TaskCompletionSource<bool>();
ShowDialog(owner);
return _tcs.Task;
}
private void BuildForm(List<FieldConfig> fields)
{
FormPanel.Children.Clear();
if (_entity is null) return;
var type = _entity.GetType();
foreach (var field in fields)
{
var prop = type.GetProperty(field.PropertyName);
if (prop is null) continue;
var value = prop.GetValue(_entity);
// 每行一个字段容器: 标题 + 输入框
var cell = new Border
{
Width = 340,
Padding = new Thickness(0, 4, 10, 4),
};
var row = new Grid
{
ColumnDefinitions = new ColumnDefinitions("100,*"),
};
var label = new TextBlock
{
Text = field.DisplayName + ":",
FontSize = 12,
Foreground = Brush.Parse("#8B9BB5"),
VerticalAlignment = VerticalAlignment.Center,
};
row.Children.Add(label);
Control input;
if (field.FieldType == FieldType.CheckBox)
{
var cb = new CheckBox
{
IsChecked = value is int iv ? iv == 1 : (value as bool? ?? false),
IsEnabled = !field.IsReadOnly,
Foreground = Brush.Parse("#DDE4F0"),
VerticalAlignment = VerticalAlignment.Center,
};
cb.IsCheckedChanged += (_, _) =>
prop.SetValue(_entity, cb.IsChecked == true ? 1 : 0);
input = cb;
}
else
{
var tb = new TextBox
{
Text = value?.ToString() ?? "",
IsReadOnly = field.IsReadOnly,
Watermark = field.DisplayName,
Background = Brush.Parse("#0C1622"),
Foreground = Brush.Parse("#DDE4F0"),
BorderBrush = Brush.Parse("#1A2F4A"),
};
if (field.FieldType == FieldType.Number)
{
tb.TextChanged += (_, _) =>
{
if (int.TryParse(tb.Text, out var num))
prop.SetValue(_entity, num);
};
}
else
{
tb.TextChanged += (_, _) =>
prop.SetValue(_entity, tb.Text);
}
input = tb;
}
Grid.SetColumn(input, 1);
row.Children.Add(input);
cell.Child = row;
FormPanel.Children.Add(cell);
}
}
private void CloseWith(bool result)
{
_tcs?.TrySetResult(result);
Close();
}
}

@ -0,0 +1,52 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Base.LocationInfoListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索库位编号/名称..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1*,0.8*,0.7*,0.7*,0.5*,0.5*,0.5*,0.8*,1*,0.7*,0.7*,0.8*">
<TextBlock Grid.Column="0" Text="库位编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="库位名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="区域" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="仓库" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="排" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="列" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="层" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="AGV定位" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="8" Text="物料编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="9" Text="状态" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="10" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="11" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1*,0.8*,0.7*,0.7*,0.5*,0.5*,0.5*,0.8*,1*,0.7*,0.7*,0.8*">
<TextBlock Grid.Column="0" Text="{Binding locationCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding locationName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding locationArea}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding storeCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding locationRows}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding locationColumns}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding locationLayers}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="7" Text="{Binding agvPosition}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="8" Text="{Binding materialCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="9" Text="{Binding locationStatus}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="10" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="11" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Base;
public partial class LocationInfoListView : UserControl
{
public LocationInfoListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,44 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Base.MaterialInfoListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索物料编号/名称..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.2*,1.3*,0.9*,1.2*,1*,1*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="物料编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="物料名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="物料条码" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="最短存放" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="最长存放" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.2*,1.3*,0.9*,1.2*,1*,1*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="{Binding materialCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding materialName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding materialType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding materialBarcode}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding minStorageCycle}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding maxStorageCycle}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="7" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Base;
public partial class MaterialInfoListView : UserControl
{
public MaterialInfoListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,38 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Base.StoreInfoListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索仓库编号/名称..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.2*,1.5*,1*,1*,1*">
<TextBlock Grid.Column="0" Text="仓库编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="仓库名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="仓库类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.2*,1.5*,1*,1*,1*">
<TextBlock Grid.Column="0" Text="{Binding storeCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding storeName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding storeType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="4" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Base;
public partial class StoreInfoListView : UserControl
{
public StoreInfoListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,49 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Device.DeviceHostListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索主机编号/名称..." Text="{Binding SearchText}" Width="260"
Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.5*,1.5*,0.8*,1.2*,0.8*,1.2*,1*,1.2*">
<TextBlock Grid.Column="0" Text="主机编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="主机名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="主机IP" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="端口" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="路径" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.5*,1.5*,0.8*,1.2*,0.8*,1.2*,1*,1.2*">
<TextBlock Grid.Column="0" Text="{Binding hostCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding hostName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding hostType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding hostIP}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding hostPort}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding hostPath}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="7" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click"
Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click"
Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Device;
public partial class DeviceHostListView : UserControl
{
public DeviceHostListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,46 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Device.DeviceInfoListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索设备编号/名称..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.2*,1.2*,0.8*,0.7*,0.7*,1*,0.8*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="设备编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="设备名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="别名" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="状态" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="主机编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="序号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="8" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.2*,1.2*,0.8*,0.7*,0.7*,1*,0.8*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="{Binding deviceCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding deviceName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding deviceAlias}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding deviceType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding deviceStatus}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding hostCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding deviceSerialNo}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="7" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="8" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Device;
public partial class DeviceInfoListView : UserControl
{
public DeviceInfoListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,48 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Device.DeviceParamListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索参数编号/名称..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1*,1*,1.2*,1*,0.7*,0.6*,0.7*,0.7*,0.7*,0.8*">
<TextBlock Grid.Column="0" Text="参数编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="设备编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="参数名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="参数地址" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="值" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="频率" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="8" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="9" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1*,1*,1.2*,1*,0.7*,0.6*,0.7*,0.7*,0.7*,0.8*">
<TextBlock Grid.Column="0" Text="{Binding paramCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding deviceCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding paramName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding paramAddress}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding paramType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding paramValue}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding operationType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="7" Text="{Binding operationFrequency}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="8" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="9" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Device;
public partial class DeviceParamListView : UserControl
{
public DeviceParamListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,253 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.HomePageView">
<ScrollViewer Margin="20,14,20,14">
<StackPanel Spacing="14">
<!-- Intro Card -->
<Border Background="#0F1620" CornerRadius="8" Padding="22" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="10">
<StackPanel Orientation="Horizontal" Spacing="10">
<Rectangle Width="3" Height="20" Fill="#4FC3F7" RadiusX="2" RadiusY="2" />
<TextBlock Text="WCS 通用平台框架" FontSize="17" FontWeight="SemiBold" Foreground="#DDE4F0" />
</StackPanel>
<TextBlock Text="基于 .NET 8.0 + Avalonia UI 构建的跨平台仓库控制系统 (Warehouse Control System),支持多种硬件设备协议,提供统一的调度、监控和管理能力。采用 Apollo 配置中心实时下发配置Serilog 结构化日志按模块分类存储SqlSugar ORM 多库访问。"
FontSize="12" Foreground="#7B8FA8" TextWrapping="Wrap" LineHeight="20" />
<WrapPanel Margin="0,4,0,0">
<Border Background="#0F1F38" CornerRadius="4" Padding="10,5" Margin="0,0,8,8" BorderBrush="#1B3A5C" BorderThickness="1">
<TextBlock Text=".NET 8.0" FontSize="11" Foreground="#64B5F6" />
</Border>
<Border Background="#0F1F38" CornerRadius="4" Padding="10,5" Margin="0,0,8,8" BorderBrush="#1B3A5C" BorderThickness="1">
<TextBlock Text="Avalonia UI" FontSize="11" Foreground="#64B5F6" />
</Border>
<Border Background="#0F1F38" CornerRadius="4" Padding="10,5" Margin="0,0,8,8" BorderBrush="#1B3A5C" BorderThickness="1">
<TextBlock Text="MVVM" FontSize="11" Foreground="#64B5F6" />
</Border>
<Border Background="#0F1F38" CornerRadius="4" Padding="10,5" Margin="0,0,8,8" BorderBrush="#1B3A5C" BorderThickness="1">
<TextBlock Text="SqlSugar ORM" FontSize="11" Foreground="#64B5F6" />
</Border>
<Border Background="#0F1F38" CornerRadius="4" Padding="10,5" Margin="0,0,8,8" BorderBrush="#1B3A5C" BorderThickness="1">
<TextBlock Text="Serilog" FontSize="11" Foreground="#64B5F6" />
</Border>
<Border Background="#0F1F38" CornerRadius="4" Padding="10,5" Margin="0,0,8,8" BorderBrush="#1B3A5C" BorderThickness="1">
<TextBlock Text="Apollo 配置中心" FontSize="11" Foreground="#64B5F6" />
</Border>
<Border Background="#0F1F38" CornerRadius="4" Padding="10,5" Margin="0,0,8,8" BorderBrush="#1B3A5C" BorderThickness="1">
<TextBlock Text="FusionCache" FontSize="11" Foreground="#64B5F6" />
</Border>
</WrapPanel>
</StackPanel>
</Border>
<!-- Module Card -->
<Border Background="#0F1620" CornerRadius="8" Padding="22" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="10">
<Rectangle Width="3" Height="20" Fill="#4FC3F7" RadiusX="2" RadiusY="2" />
<TextBlock Text="核心模块" FontSize="17" FontWeight="SemiBold" Foreground="#DDE4F0" />
</StackPanel>
<WrapPanel>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="主控台程序负责系统启动、配置加载、DI 容器编排" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs.UI" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="跨平台桌面客户端,基于 Avalonia + MVVM提供设备监控界面" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs.Business" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="业务逻辑层,任务创建、库位筛选、物料入库/出库策略" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs.Repository" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="数据访问层,基于 SqlSugar ORM多数据库连接管理" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs.Strategy" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="策略层,物料/库位分配算法,支持多种策略可配置切换" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs.Cache" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="缓存层FusionCache + SQLite 分布式缓存,支持熔断" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs.Serilog" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="日志模块,按 Info/Plc/Agv/Rfid/Alarm/Error 分类存储" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
<Border Width="240" Padding="14,12" Margin="0,0,14,14" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<TextBlock Text="Sln.Wcs.Common / Model" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
<TextBlock Text="公共工具层与领域模型,提供基础类型、实体定义与配置" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" />
</StackPanel>
</Border>
</WrapPanel>
</StackPanel>
</Border>
<!-- 海康 AGV 模块 -->
<Border Background="#0F1620" CornerRadius="8" Padding="22" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="10">
<Rectangle Width="3" Height="20" Fill="#4FC3F7" RadiusX="2" RadiusY="2" />
<TextBlock Text="海康 AGV 调度模块" FontSize="17" FontWeight="SemiBold" Foreground="#DDE4F0" />
</StackPanel>
<TextBlock Text="封装海康机器人 RCS-2000 接口,采用三层架构实现 AGV 任务下发、路径规划、状态查询与设备通知。"
FontSize="12" Foreground="#7B8FA8" TextWrapping="Wrap" />
<WrapPanel>
<Border Width="280" Padding="12,10" Margin="0,0,12,12" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<StackPanel Orientation="Horizontal" Spacing="6">
<Border Background="#1565C0" CornerRadius="2" Width="4" Height="14" />
<TextBlock Text="HikRoBotSdk" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
</StackPanel>
<TextBlock Text="底层 HTTP 客户端,封装海康 RCS API 原始接口调用GbTaskSubmit / GenSchedulingTask / QueryStatus / CarrierBind 等)" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" LineHeight="16" />
</StackPanel>
</Border>
<Border Width="280" Padding="12,10" Margin="0,0,12,12" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<StackPanel Orientation="Horizontal" Spacing="6">
<Border Background="#0277BD" CornerRadius="2" Width="4" Height="14" />
<TextBlock Text="HikRoBotAdapter" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
</StackPanel>
<TextBlock Text="适配层,将 SDK 原始 DTO 转换为业务语义模型,含 TaskType 映射、状态枚举转换、异常处理与重试" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" LineHeight="16" />
</StackPanel>
</Border>
<Border Width="280" Padding="12,10" Margin="0,0,12,12" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<StackPanel Orientation="Horizontal" Spacing="6">
<Border Background="#00838F" CornerRadius="2" Width="4" Height="14" />
<TextBlock Text="HikRoBotDispatcher" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
</StackPanel>
<TextBlock Text="调度中枢,管理多 AGV 的任务队列、防死锁、路径冲突检测、优先级调度与设备状态同步" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" LineHeight="16" />
</StackPanel>
</Border>
</WrapPanel>
</StackPanel>
</Border>
<!-- 提升机模块 -->
<Border Background="#0F1620" CornerRadius="8" Padding="22" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="10">
<Rectangle Width="3" Height="20" Fill="#4FC3F7" RadiusX="2" RadiusY="2" />
<TextBlock Text="提升机调度模块" FontSize="17" FontWeight="SemiBold" Foreground="#DDE4F0" />
</StackPanel>
<TextBlock Text="封装提升机 (Hoist) 控制协议,实现出入库升降控制、托盘到位检测、报警设置与状态实时监控。"
FontSize="12" Foreground="#7B8FA8" TextWrapping="Wrap" />
<WrapPanel>
<Border Width="280" Padding="12,10" Margin="0,0,12,12" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<StackPanel Orientation="Horizontal" Spacing="6">
<Border Background="#1565C0" CornerRadius="2" Width="4" Height="14" />
<TextBlock Text="HoistSdk" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
</StackPanel>
<TextBlock Text="底层通信客户端,封装提升机原生协议(升降/取货/放货/状态查询),处理网络连接与超时" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" LineHeight="16" />
</StackPanel>
</Border>
<Border Width="280" Padding="12,10" Margin="0,0,12,12" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<StackPanel Orientation="Horizontal" Spacing="6">
<Border Background="#0277BD" CornerRadius="2" Width="4" Height="14" />
<TextBlock Text="HoistAdapter" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
</StackPanel>
<TextBlock Text="适配层,将原始协议 DTO 转换为业务领域模型,含楼层映射、托盘状态枚举、报警码解析" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" LineHeight="16" />
</StackPanel>
</Border>
<Border Width="280" Padding="12,10" Margin="0,0,12,12" Background="#0C1622" CornerRadius="6" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="4">
<StackPanel Orientation="Horizontal" Spacing="6">
<Border Background="#00838F" CornerRadius="2" Width="4" Height="14" />
<TextBlock Text="HoistDispatcher" FontSize="13" FontWeight="SemiBold" Foreground="#E1E8F0" />
</StackPanel>
<TextBlock Text="调度中枢,管理多台提升机的任务队列、楼层协同、托盘到位逻辑、报警联动与状态同步" FontSize="11" Foreground="#6B8CB5" TextWrapping="Wrap" LineHeight="16" />
</StackPanel>
</Border>
</WrapPanel>
</StackPanel>
</Border>
<!-- 调用关系 -->
<Border Background="#0F1620" CornerRadius="8" Padding="22" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="14">
<StackPanel Orientation="Horizontal" Spacing="10">
<Rectangle Width="3" Height="20" Fill="#4FC3F7" RadiusX="2" RadiusY="2" />
<TextBlock Text="调用关系" FontSize="17" FontWeight="SemiBold" Foreground="#DDE4F0" />
</StackPanel>
<Border Background="#0C1622" CornerRadius="6" Padding="16,12" BorderBrush="#1A2F4A" BorderThickness="1">
<StackPanel Spacing="8">
<TextBlock Text="Strategy → Dispatcher → Adapter → SDK → 设备"
FontSize="13" FontWeight="SemiBold" Foreground="#64B5F6" HorizontalAlignment="Center" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="0">
<Border Background="#6A1B9A" CornerRadius="4" Padding="14,10" Width="140">
<StackPanel HorizontalAlignment="Center" Spacing="2">
<TextBlock Text="Strategy" FontSize="12" FontWeight="SemiBold" Foreground="White" HorizontalAlignment="Center" />
<TextBlock Text="物料/库位分配" FontSize="9" Foreground="#CE93D8" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<TextBlock Text="→" FontSize="18" Foreground="#4FC3F7" VerticalAlignment="Center" Margin="8,0" />
<Border Background="#00838F" CornerRadius="4" Padding="14,10" Width="140">
<StackPanel HorizontalAlignment="Center" Spacing="2">
<TextBlock Text="Dispatcher" FontSize="12" FontWeight="SemiBold" Foreground="White" HorizontalAlignment="Center" />
<TextBlock Text="队列调度/防死锁" FontSize="9" Foreground="#B2DFDB" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<TextBlock Text="→" FontSize="18" Foreground="#4FC3F7" VerticalAlignment="Center" Margin="8,0" />
<Border Background="#0277BD" CornerRadius="4" Padding="14,10" Width="140">
<StackPanel HorizontalAlignment="Center" Spacing="2">
<TextBlock Text="Adapter" FontSize="12" FontWeight="SemiBold" Foreground="White" HorizontalAlignment="Center" />
<TextBlock Text="DTO转换/重试" FontSize="9" Foreground="#B3D4FC" HorizontalAlignment="Center" />
</StackPanel>
</Border>
<TextBlock Text="→" FontSize="18" Foreground="#4FC3F7" VerticalAlignment="Center" Margin="8,0" />
<Border Background="#0D47A1" CornerRadius="4" Padding="14,10" Width="140">
<StackPanel HorizontalAlignment="Center" Spacing="2">
<TextBlock Text="SDK" FontSize="12" FontWeight="SemiBold" Foreground="White" HorizontalAlignment="Center" />
<TextBlock Text="协议通信" FontSize="9" Foreground="#90CAF9" HorizontalAlignment="Center" />
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
</Border>
<WrapPanel HorizontalAlignment="Center">
<StackPanel Orientation="Horizontal" Margin="0,0,16,8">
<Border Background="#6A1B9A" CornerRadius="2" Width="12" Height="12" Margin="0,0,6,0" />
<TextBlock Text="Strategy 策略层" FontSize="11" Foreground="#8B9BB5" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,16,8">
<Border Background="#00838F" CornerRadius="2" Width="12" Height="12" Margin="0,0,6,0" />
<TextBlock Text="Dispatcher 调度层" FontSize="11" Foreground="#8B9BB5" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,16,8">
<Border Background="#0277BD" CornerRadius="2" Width="12" Height="12" Margin="0,0,6,0" />
<TextBlock Text="Adapter 适配层" FontSize="11" Foreground="#8B9BB5" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,16,8">
<Border Background="#0D47A1" CornerRadius="2" Width="12" Height="12" Margin="0,0,6,0" />
<TextBlock Text="SDK 通信层" FontSize="11" Foreground="#8B9BB5" />
</StackPanel>
</WrapPanel>
<TextBlock Text="Strategy 策略层根据物料属性与库位状态计算最优分配方案,将任务指令交由 Dispatcher 进行队列调度与冲突检测Dispatcher 通过 Adapter 将业务指令转换为设备协议格式,最终由 SDK 与硬件设备AGV RCS-2000 / 提升机 PLC通信执行。海康 AGV 与提升机均遵循此四层调用链仅底层通信协议不同HTTP vs TCP。"
FontSize="11" Foreground="#5B6F8A" TextWrapping="Wrap" LineHeight="18" />
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace Sln.Wcs.UI.Views;
public partial class HomePageView : UserControl
{
public HomePageView()
{
InitializeComponent();
}
}

@ -0,0 +1,41 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Sln.Wcs.UI.ViewModels"
x:Class="Sln.Wcs.UI.Views.MainWindow"
x:DataType="vm:NavigationViewModel"
Title="基于多场景应用的 WCS 通用平台"
Width="1280" Height="780"
MinWidth="1000" MinHeight="600"
WindowStartupLocation="CenterScreen"
Background="#0A0E14"
Foreground="#BCC8D6">
<Grid RowDefinitions="Auto,Auto,*">
<!-- Title Bar -->
<Border Grid.Row="0" Padding="16,10" Background="#0A0E14">
<StackPanel Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
<Canvas Width="24" Height="24">
<Rectangle Canvas.Left="1" Canvas.Top="1" Width="22" Height="18" RadiusX="2" RadiusY="2" Fill="#0F1F38" Stroke="#4FC3F7" StrokeThickness="1.2" />
<Rectangle Canvas.Left="4" Canvas.Top="4" Width="16" Height="5" RadiusX="1" RadiusY="1" Fill="#1B3A5C" />
<Line StartPoint="5,11" EndPoint="19,11" Stroke="#1B3A5C" StrokeThickness="1" />
<Ellipse Canvas.Left="5" Canvas.Top="5" Width="2" Height="2" Fill="#00E676" />
<Ellipse Canvas.Left="9" Canvas.Top="5" Width="2" Height="2" Fill="#4FC3F7" />
</Canvas>
<TextBlock Text="基于多场景应用的 WCS 通用平台" FontSize="16" FontWeight="Bold" Foreground="#E1E8F0" />
<Border Background="#1B3A5C" CornerRadius="3" Padding="6,2" VerticalAlignment="Center">
<TextBlock Text="V1.0.0" FontSize="10" Foreground="#4FC3F7" />
</Border>
</StackPanel>
</Border>
<!-- Menu Bar -->
<Border Grid.Row="1" Padding="16,0" Background="#0C1622" BorderBrush="#1A2F4A" BorderThickness="0,1,0,1">
<StackPanel x:Name="MenuContainer" Orientation="Horizontal" Spacing="0" />
</Border>
<!-- Content Area -->
<Border Grid.Row="2" x:Name="ContentArea" />
</Grid>
</Window>

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Sln.Wcs.UI.ViewModels;
namespace Sln.Wcs.UI.Views;
public partial class MainWindow : Window
{
private readonly NavigationViewModel _navVm;
private readonly List<Popup> _openPopups = new();
public MainWindow(NavigationViewModel navigationVm)
{
InitializeComponent();
_navVm = navigationVm;
DataContext = _navVm;
_navVm.PageChanged += OnPageChanged;
BuildMenu();
_navVm.LoadDefaultPage();
}
private void BuildMenu()
{
MenuContainer.Children.Clear();
foreach (var item in _navVm.TopMenuItems)
{
var btn = new Button
{
Content = item.Label,
Background = Brushes.Transparent,
Foreground = Brush.Parse("#8B9BB5"),
FontSize = 13,
Padding = new Thickness(14, 12),
Cursor = new Cursor(StandardCursorType.Hand),
BorderThickness = new Thickness(0),
};
if (item.Action != null)
{
btn.Click += (_, _) => item.Action();
}
if (item.Children != null && item.Children.Count > 0)
{
var popup = new Popup
{
PlacementTarget = btn,
Placement = PlacementMode.Bottom,
IsLightDismissEnabled = false,
};
var popupBorder = new Border
{
Background = Brush.Parse("#0F1620"),
BorderBrush = Brush.Parse("#1A2F4A"),
BorderThickness = new Thickness(1),
CornerRadius = new CornerRadius(4),
MinWidth = 160,
};
var stack = new StackPanel { Margin = new Thickness(4) };
foreach (var sub in item.Children)
{
var subBtn = new Button
{
Content = sub.Label,
Background = Brushes.Transparent,
Foreground = Brush.Parse("#8B9BB5"),
FontSize = 12,
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Left,
Padding = new Thickness(12, 8),
BorderThickness = new Thickness(0),
Cursor = new Cursor(StandardCursorType.Hand),
};
subBtn.Click += (_, _) =>
{
popup.Close();
sub.Action();
};
subBtn.PointerEntered += (_, _) =>
subBtn.Background = Brush.Parse("#1B3A5C");
subBtn.PointerExited += (_, _) =>
subBtn.Background = Brushes.Transparent;
stack.Children.Add(subBtn);
}
popupBorder.Child = stack;
popup.Child = popupBorder;
// Hover 展开,离开按钮或 popup 时关闭
var closeTimer = new System.Timers.Timer(200) { AutoReset = false };
bool mouseInPopup = false;
bool mouseInButton = false;
popupBorder.PointerEntered += (_, _) => { mouseInPopup = true; closeTimer.Stop(); };
popupBorder.PointerExited += (_, _) => { mouseInPopup = false; TryClose(); };
btn.PointerEntered += (_, _) =>
{
mouseInButton = true;
closeTimer.Stop();
btn.Background = Brush.Parse("#1B3A5C");
btn.Foreground = Brush.Parse("#4FC3F7");
CloseAllPopups();
popup.Open();
};
btn.PointerExited += (_, _) =>
{
mouseInButton = false;
TryClose();
};
void TryClose()
{
closeTimer.Stop();
closeTimer.Start();
closeTimer.Elapsed += (_, _) =>
{
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
{
if (!mouseInButton && !mouseInPopup)
{
popup.Close();
}
});
};
}
popup.Closed += (_, _) =>
{
btn.Background = Brushes.Transparent;
btn.Foreground = Brush.Parse("#8B9BB5");
};
_openPopups.Add(popup);
}
else
{
btn.PointerEntered += (_, _) =>
{
btn.Background = Brush.Parse("#1B3A5C");
btn.Foreground = Brush.Parse("#4FC3F7");
};
btn.PointerExited += (_, _) =>
{
btn.Background = Brushes.Transparent;
btn.Foreground = Brush.Parse("#8B9BB5");
};
}
MenuContainer.Children.Add(btn);
}
}
private void CloseAllPopups()
{
foreach (var p in _openPopups)
p.Close();
}
private void OnPageChanged(Control? view)
{
if (view != null)
{
view.HorizontalAlignment = HorizontalAlignment.Stretch;
view.VerticalAlignment = VerticalAlignment.Stretch;
ContentArea.Child = view;
}
}
}

@ -0,0 +1,42 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Path.PathDetailsListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索路径编号/名称..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.2*,1.3*,1.2*,1.2*,0.8*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="路径编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="路径名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="起点" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="终点" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="设备类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.2*,1.3*,1.2*,1.2*,0.8*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="{Binding pathCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding pathName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding startPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding endPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding deviceType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="6" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Path;
public partial class PathDetailsListView : UserControl
{
public PathDetailsListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,44 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Path.PathInfoListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索路径编号/名称..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.2*,1.3*,0.8*,0.8*,1.2*,1.2*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="路径编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="路径名称" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="路径类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="路径类别" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="起点" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="终点" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.2*,1.3*,0.8*,0.8*,1.2*,1.2*,0.8*,0.8*">
<TextBlock Grid.Column="0" Text="{Binding pathCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding pathName}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding pathType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding pathCategory}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding startPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding endPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="7" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Path;
public partial class PathInfoListView : UserControl
{
public PathInfoListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,54 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Task.TaskDetailListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索任务编号/物料编号..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.2*,0.9*,1*,1.1*,0.6*,0.6*,0.6*,0.9*,0.9*,0.7*,0.7*,0.7*,0.7*">
<TextBlock Grid.Column="0" Text="任务编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="路径编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="物料编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="托盘条码" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="数量" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="类别" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="起始位置" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="8" Text="结束位置" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="9" Text="设备" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="10" Text="状态" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="11" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="12" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.2*,0.9*,1*,1.1*,0.6*,0.6*,0.6*,0.9*,0.9*,0.7*,0.7*,0.7*,0.7*">
<TextBlock Grid.Column="0" Text="{Binding taskCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding pathCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding materialCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding palletBarcode}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding materialCount}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding taskType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding taskCategory}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="7" Text="{Binding startPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="8" Text="{Binding endPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="9" Text="{Binding deviceType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="10" Text="{Binding taskStatus}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="11" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="12" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Task;
public partial class TaskDetailListView : UserControl
{
public TaskDetailListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,52 @@
<UserControl x:CompileBindings="False" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sln.Wcs.UI.Views.Task.TaskQueueListView">
<Grid RowDefinitions="Auto,Auto,*" Margin="4" VerticalAlignment="Stretch">
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索任务编号/物料编号..." Text="{Binding SearchText}" Width="260" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="8,0,0,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="8,0,0,0" />
</Grid>
<Border Grid.Row="1" Background="#0C1622" Padding="8,6" BorderBrush="#1A2F4A" BorderThickness="1">
<Grid ColumnDefinitions="1.2*,1*,1.2*,0.6*,0.6*,0.6*,1*,1*,0.7*,0.6*,0.8*,0.7*">
<TextBlock Grid.Column="0" Text="任务编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="物料编号" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="托盘条码" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="数量" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="类型" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="类别" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="起始位置" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="结束位置" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="8" Text="状态" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="9" Text="步骤" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="10" Text="备注" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="11" Text="操作" FontSize="12" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="2" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" Background="#0F1620" Foreground="#DDE4F0" BorderBrush="#1A2F4A" BorderThickness="1,0,1,1">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,4" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="1.2*,1*,1.2*,0.6*,0.6*,0.6*,1*,1*,0.7*,0.6*,0.8*,0.7*">
<TextBlock Grid.Column="0" Text="{Binding taskCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding materialCode}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding palletBarcode}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding materialCount}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding taskType}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding taskCategory}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding startPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="7" Text="{Binding endPoint}" Foreground="#64B5F6" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="8" Text="{Binding taskStatus}" Foreground="#8B9BB5" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="9" Text="{Binding taskSteps}" Foreground="#DDE4F0" FontSize="12" VerticalAlignment="Center" />
<TextBlock Grid.Column="10" Text="{Binding remark}" Foreground="#6B8CB5" FontSize="12" VerticalAlignment="Center" />
<StackPanel Grid.Column="11" Orientation="Horizontal" Spacing="6">
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="8,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="8,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Sln.Wcs.UI.ViewModels.Base;
namespace Sln.Wcs.UI.Views.Task;
public partial class TaskQueueListView : UserControl
{
public TaskQueueListView()
{
InitializeComponent();
}
private void Edit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("EditCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private void Delete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag != null && DataContext is ICrudPageViewModel vm)
{
vm.GetType().GetProperty("SelectedItem")?.SetValue(vm, btn.Tag);
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
}

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>

@ -0,0 +1,18 @@
{
"exclude": [
"**/bin",
"**/bower_components",
"**/jspm_packages",
"**/node_modules",
"**/obj",
"**/platforms"
],
"apollo": {
"AppId": "SlnWcs",
"Env": "DEV",
"MetaServer": "http://119.45.202.115:4320",
"ConfigServer": [
"http://119.45.202.115:4320"
]
}
}

@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Wcs.HoistDispatcher", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Wcs.HikRoBotDispatcher", "Sln.Wcs.HikRoBotDispatcher\Sln.Wcs.HikRoBotDispatcher.csproj", "{A6387485-6B2E-4AA2-8FD1-F02AA5C5100C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Wcs.UI", "Sln.Wcs.UI\Sln.Wcs.UI.csproj", "{1AD871EB-0EAA-4F53-8CE7-691EEBEE3F22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -98,6 +100,10 @@ Global
{A6387485-6B2E-4AA2-8FD1-F02AA5C5100C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6387485-6B2E-4AA2-8FD1-F02AA5C5100C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6387485-6B2E-4AA2-8FD1-F02AA5C5100C}.Release|Any CPU.Build.0 = Release|Any CPU
{1AD871EB-0EAA-4F53-8CE7-691EEBEE3F22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1AD871EB-0EAA-4F53-8CE7-691EEBEE3F22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1AD871EB-0EAA-4F53-8CE7-691EEBEE3F22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1AD871EB-0EAA-4F53-8CE7-691EEBEE3F22}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Loading…
Cancel
Save