change - 显示效果修改

dev
WenJY 4 hours ago
parent 5543fcbc76
commit b09baf31e0

@ -16,7 +16,8 @@
"Bash(sed -i '' '/Console.WriteLine/d' Views/MainWindow.axaml.cs)",
"Bash(sed -i '' '/System.Console.WriteLine/d' ViewModels/Device/DeviceHostViewModel.cs)",
"Bash(sed -i '' '/System.Console.WriteLine/d' Views/Device/DeviceHostListView.axaml.cs)",
"Bash(git status *)"
"Bash(git status *)",
"Bash(dotnet /Users/wenxiansheng/.nuget/packages/avalonia/11.1.5/lib/netstandard2.0/Avalonia.Base.dll type list)"
]
}
}

@ -1,15 +1,32 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
using Sln.Wcs.UI.Views.Base;
namespace Sln.Wcs.UI.ViewModels.Device;
public class DeviceInfoViewModel : CrudPageViewModel<BaseDeviceInfo>
public partial class DeviceInfoViewModel : CrudPageViewModel<BaseDeviceInfo>
{
public DeviceInfoViewModel(IBaseDeviceInfoService service) : base(service)
private readonly IBaseDeviceParamService _paramService;
private BaseDeviceInfo? _currentDevice;
[ObservableProperty]
private bool _isPanelOpen;
[ObservableProperty]
private ObservableCollection<BaseDeviceParam> _deviceParams = new();
[ObservableProperty]
private string _slidePanelTitle = string.Empty;
public DeviceInfoViewModel(IBaseDeviceInfoService service, IBaseDeviceParamService paramService) : base(service)
{
_paramService = paramService;
PageTitle = "设备信息管理";
}
@ -30,4 +47,77 @@ public class DeviceInfoViewModel : CrudPageViewModel<BaseDeviceInfo>
=> 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();
public void LoadParamsData(BaseDeviceInfo device)
{
_currentDevice = device;
LoadParams();
SlidePanelTitle = $"设备参数 - {device.deviceCode}";
}
[RelayCommand]
public void ClosePanel()
{
IsPanelOpen = false;
}
[RelayCommand]
private async System.Threading.Tasks.Task AddParam()
{
if (_currentDevice is null) return;
var entity = new BaseDeviceParam { deviceCode = _currentDevice.deviceCode };
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(entity, ParamFieldConfigs, false, GetMainWindow());
if (result)
{
_paramService.Insert(entity);
LoadParams();
}
}
public async System.Threading.Tasks.Task EditParamAsync(BaseDeviceParam param)
{
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(param, ParamFieldConfigs, true, GetMainWindow());
if (result)
{
_paramService.Update(param);
LoadParams();
}
}
public void DeleteParam(BaseDeviceParam param)
{
_paramService.DeleteById(param.objId);
LoadParams();
}
private void LoadParams()
{
if (_currentDevice is null) return;
var list = _paramService.Query(x => x.deviceCode == _currentDevice.deviceCode);
DeviceParams = new ObservableCollection<BaseDeviceParam>(list);
}
public List<FieldConfig> ParamFieldConfigs => new()
{
new() { PropertyName = "paramCode", DisplayName = "参数编号", IsRequired = true },
new() { PropertyName = "deviceCode", DisplayName = "设备编号", IsRequired = true, IsReadOnly = true },
new() { PropertyName = "paramName", DisplayName = "参数名称", IsRequired = true },
new() { PropertyName = "paramAddress", DisplayName = "参数地址", IsRequired = true },
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 = "备注" },
};
private Avalonia.Controls.Window GetMainWindow()
{
return (Avalonia.Controls.Window)Avalonia.Application.Current!
.ApplicationLifetime!.GetType()
.GetProperty("MainWindow")!
.GetValue(Avalonia.Application.Current.ApplicationLifetime)!;
}
}

@ -1,15 +1,32 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
using Sln.Wcs.UI.Views.Base;
namespace Sln.Wcs.UI.ViewModels.Path;
public class PathInfoViewModel : CrudPageViewModel<BasePathInfo>
public partial class PathInfoViewModel : CrudPageViewModel<BasePathInfo>
{
public PathInfoViewModel(IBasePathInfoService service) : base(service)
private readonly IBasePathDetailsService _detailService;
private BasePathInfo? _currentPath;
[ObservableProperty]
private bool _isPanelOpen;
[ObservableProperty]
private ObservableCollection<BasePathDetails> _detailItems = new();
[ObservableProperty]
private string _slidePanelTitle = string.Empty;
public PathInfoViewModel(IBasePathInfoService service, IBasePathDetailsService detailService) : base(service)
{
_detailService = detailService;
PageTitle = "路径信息管理";
}
@ -30,4 +47,74 @@ public class PathInfoViewModel : CrudPageViewModel<BasePathInfo>
|| (x.pathName != null && x.pathName.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Path.PathInfoListView();
public void LoadDetailsData(BasePathInfo path)
{
_currentPath = path;
LoadDetails();
SlidePanelTitle = $"路径明细 - {path.pathCode}";
}
[RelayCommand]
public void ClosePanel()
{
IsPanelOpen = false;
}
[RelayCommand]
private async System.Threading.Tasks.Task AddDetail()
{
if (_currentPath is null) return;
var entity = new BasePathDetails { pathCode = _currentPath.pathCode };
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(entity, DetailFieldConfigs, false, GetMainWindow());
if (result)
{
_detailService.Insert(entity);
LoadDetails();
}
}
public async System.Threading.Tasks.Task EditDetailAsync(BasePathDetails detail)
{
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(detail, DetailFieldConfigs, true, GetMainWindow());
if (result)
{
_detailService.Update(detail);
LoadDetails();
}
}
public void DeleteDetail(BasePathDetails detail)
{
_detailService.DeleteById(detail.objId);
LoadDetails();
}
private void LoadDetails()
{
if (_currentPath is null) return;
var list = _detailService.Query(x => x.pathCode == _currentPath.pathCode);
DetailItems = new ObservableCollection<BasePathDetails>(list);
}
public List<FieldConfig> DetailFieldConfigs => new()
{
new() { PropertyName = "pathCode", DisplayName = "路径编号", IsRequired = true, IsReadOnly = true },
new() { PropertyName = "pathName", DisplayName = "路径名称" },
new() { PropertyName = "startPoint", DisplayName = "起点", IsRequired = true },
new() { PropertyName = "endPoint", DisplayName = "终点", IsRequired = true },
new() { PropertyName = "deviceType", DisplayName = "设备类型", FieldType = FieldType.Number },
new() { PropertyName = "isFlag", DisplayName = "启用", FieldType = FieldType.CheckBox },
new() { PropertyName = "remark", DisplayName = "备注" },
};
private Avalonia.Controls.Window GetMainWindow()
{
return (Avalonia.Controls.Window)Avalonia.Application.Current!
.ApplicationLifetime!.GetType()
.GetProperty("MainWindow")!
.GetValue(Avalonia.Application.Current.ApplicationLifetime)!;
}
}

@ -1,15 +1,32 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
using Sln.Wcs.UI.ViewModels.Base;
using Sln.Wcs.UI.Views.Base;
namespace Sln.Wcs.UI.ViewModels.Task;
public class TaskQueueViewModel : CrudPageViewModel<LiveTaskQueue>
public partial class TaskQueueViewModel : CrudPageViewModel<LiveTaskQueue>
{
public TaskQueueViewModel(ILiveTaskQueueService service) : base(service)
private readonly ILiveTaskDetailService _detailService;
private LiveTaskQueue? _currentTask;
[ObservableProperty]
private bool _isPanelOpen;
[ObservableProperty]
private ObservableCollection<LiveTaskDetail> _detailItems = new();
[ObservableProperty]
private string _slidePanelTitle = string.Empty;
public TaskQueueViewModel(ILiveTaskQueueService service, ILiveTaskDetailService detailService) : base(service)
{
_detailService = detailService;
PageTitle = "任务队列管理";
}
@ -36,4 +53,77 @@ public class TaskQueueViewModel : CrudPageViewModel<LiveTaskQueue>
|| (x.materialCode != null && x.materialCode.Contains(search));
public override Avalonia.Controls.Control CreateView() => new Sln.Wcs.UI.Views.Task.TaskQueueListView();
public void LoadDetailsData(LiveTaskQueue task)
{
_currentTask = task;
LoadDetails();
SlidePanelTitle = $"任务明细 - {task.taskCode}";
}
[RelayCommand]
public void ClosePanel()
{
IsPanelOpen = false;
}
[RelayCommand]
private async System.Threading.Tasks.Task AddDetail()
{
if (_currentTask is null) return;
var entity = new LiveTaskDetail { taskCode = _currentTask.taskCode };
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(entity, DetailFieldConfigs, false, GetMainWindow());
if (result)
{
_detailService.Insert(entity);
LoadDetails();
}
}
public async System.Threading.Tasks.Task EditDetailAsync(LiveTaskDetail detail)
{
var editor = new EntityEditWindow();
var result = await editor.ShowDialog(detail, DetailFieldConfigs, true, GetMainWindow());
if (result)
{
_detailService.Update(detail);
LoadDetails();
}
}
public void DeleteDetail(LiveTaskDetail detail)
{
_detailService.DeleteById(detail.objId);
LoadDetails();
}
private void LoadDetails()
{
if (_currentTask is null) return;
var list = _detailService.Query(x => x.taskCode == _currentTask.taskCode);
DetailItems = new ObservableCollection<LiveTaskDetail>(list);
}
public List<FieldConfig> DetailFieldConfigs => new()
{
new() { PropertyName = "taskCode", DisplayName = "任务编号", IsRequired = true, IsReadOnly = true },
new() { PropertyName = "pathCode", DisplayName = "路径编号" },
new() { PropertyName = "materialCode", DisplayName = "物料编号" },
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 = "备注" },
};
private Avalonia.Controls.Window GetMainWindow()
{
return (Avalonia.Controls.Window)Avalonia.Application.Current!
.ApplicationLifetime!.GetType()
.GetProperty("MainWindow")!
.GetValue(Avalonia.Application.Current.ApplicationLifetime)!;
}
}

@ -1,14 +1,14 @@
<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="20,14,20,14" VerticalAlignment="Stretch">
<Grid RowDefinitions="Auto,Auto,*" Margin="20,14,20,14" VerticalAlignment="Stretch" ClipToBounds="True">
<Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索设备编号/名称..." Text="{Binding SearchText}" Width="220" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="12,0,6,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="12,0,6,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*">
<Grid ColumnDefinitions="1.1*,1.1*,0.7*,0.6*,0.6*,0.9*,0.6*,0.6*,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" />
@ -24,7 +24,7 @@
<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*">
<Grid ColumnDefinitions="1.1*,1.1*,0.7*,0.6*,0.6*,0.9*,0.6*,0.6*,1*">
<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" />
@ -33,14 +33,80 @@
<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.Column="8" Orientation="Horizontal" Spacing="4">
<Button Content="参数" Tag="{Binding}" Click="Params_Click" Background="#0D7377" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="6,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Slide-out overlay panel (half-screen) -->
<Panel Grid.Row="0" Grid.RowSpan="3" IsVisible="{Binding IsPanelOpen}" ZIndex="10">
<Grid ColumnDefinitions="0.6*,1.4*">
<!-- Left portion: backdrop that closes on tap -->
<Border Grid.Column="0" x:Name="Backdrop" Background="#80000000" Opacity="0" PointerPressed="Backdrop_Pressed" />
<!-- Right half: slide panel -->
<Border Grid.Column="1" x:Name="SlideContent"
Background="#0C1622"
BorderBrush="#1A2F4A"
BorderThickness="1,0,0,0">
<Border.RenderTransform>
<TranslateTransform X="3000" />
</Border.RenderTransform>
<Grid RowDefinitions="Auto,*">
<!-- Panel header -->
<Border Grid.Row="0" Background="#0F1F38" Padding="14,10" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBlock Grid.Column="0" Text="{Binding SlidePanelTitle}" FontSize="14" FontWeight="SemiBold" Foreground="#DDE4F0" VerticalAlignment="Center" />
<Button Grid.Column="1" Content="新增" Command="{Binding AddParamCommand}" Background="#1565C0" Foreground="White" FontSize="12" Padding="12,5" Margin="0,0,10,0" />
<Button Grid.Column="2" Content="✕" Click="ClosePanel_Click" Background="Transparent" Foreground="#8B9BB5" FontSize="14" Padding="6,2" />
</Grid>
</Border>
<!-- Params list -->
<Grid Grid.Row="1" RowDefinitions="Auto,*">
<!-- Column header -->
<Border Grid.Row="0" Background="#0C1622" Padding="10,6" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="0.6*,1.5*,1.2*,0.8*,0.5*,0.7*,0.6*,1*">
<TextBlock Grid.Column="0" Text="参数编号" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="参数名称" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="参数地址" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="参数类型" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="值" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="操作类型" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="启用" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="操作" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<!-- Param item list -->
<ListBox Grid.Row="1" ItemsSource="{Binding DeviceParams}" Background="Transparent" Foreground="#DDE4F0" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="10,5" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="0.6*,1.5*,1.2*,0.8*,0.5*,0.7*,0.6*,1*">
<TextBlock Grid.Column="0" Text="{Binding paramCode}" Foreground="#DDE4F0" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding paramName}" Foreground="#DDE4F0" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding paramAddress}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding paramType}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding paramValue}" Foreground="#6B8CB5" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding operationType}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding isFlag}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<StackPanel Grid.Column="7" Orientation="Horizontal" Spacing="4">
<Button Content="编辑" Tag="{Binding}" Click="ParamEdit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="删除" Tag="{Binding}" Click="ParamDelete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="6,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Border>
</Grid>
</Panel>
</Grid>
</UserControl>

@ -1,6 +1,9 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Sln.Wcs.UI.ViewModels.Base;
using Sln.Wcs.UI.ViewModels.Device;
namespace Sln.Wcs.UI.Views.Device;
@ -28,4 +31,103 @@ public partial class DeviceInfoListView : UserControl
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private async void Params_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BaseDeviceInfo device && DataContext is DeviceInfoViewModel vm)
{
vm.LoadParamsData(device);
Backdrop.Opacity = 0;
if (SlideContent.RenderTransform is TranslateTransform transform)
transform.X = 3000;
vm.IsPanelOpen = true;
await AnimateSlideIn();
}
}
private async void ClosePanel_Click(object? sender, RoutedEventArgs e)
{
await AnimateSlideOut();
if (DataContext is DeviceInfoViewModel vm)
vm.ClosePanel();
}
private async void Backdrop_Pressed(object? sender, PointerPressedEventArgs e)
{
await AnimateSlideOut();
if (DataContext is DeviceInfoViewModel vm)
vm.ClosePanel();
}
private async void ParamEdit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BaseDeviceParam param && DataContext is DeviceInfoViewModel vm)
await vm.EditParamAsync(param);
}
private void ParamDelete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BaseDeviceParam param && DataContext is DeviceInfoViewModel vm)
vm.DeleteParam(param);
}
private async System.Threading.Tasks.Task AnimateSlideIn()
{
var transform = SlideContent.RenderTransform as TranslateTransform;
if (transform is null) return;
// Phase 1: slide panel in (no backdrop)
double slideFrom = transform.X;
int steps = 16;
int delay = 16;
for (int i = 1; i <= steps; i++)
{
double t = (double)i / steps;
double eased = 1.0 - System.Math.Pow(1.0 - t, 3.0);
transform.X = slideFrom + (0 - slideFrom) * eased;
await System.Threading.Tasks.Task.Delay(delay);
}
transform.X = 0;
// Phase 2: fade in backdrop after slide completes
int fadeSteps = 10;
int fadeDelay = 15;
for (int i = 1; i <= fadeSteps; i++)
{
Backdrop.Opacity = (double)i / fadeSteps;
await System.Threading.Tasks.Task.Delay(fadeDelay);
}
Backdrop.Opacity = 1;
}
private async System.Threading.Tasks.Task AnimateSlideOut()
{
var transform = SlideContent.RenderTransform as TranslateTransform;
if (transform is null) return;
// Phase 1: fade out backdrop first
int fadeSteps = 8;
int fadeDelay = 12;
for (int i = fadeSteps; i >= 0; i--)
{
Backdrop.Opacity = (double)i / fadeSteps;
await System.Threading.Tasks.Task.Delay(fadeDelay);
}
Backdrop.Opacity = 0;
// Phase 2: slide panel out after backdrop is gone
double slideFrom = transform.X;
int slideSteps = 16;
int slideDelay = 14;
for (int i = 1; i <= slideSteps; i++)
{
double t = (double)i / slideSteps;
double eased = t * t * t;
transform.X = slideFrom + (3000 - slideFrom) * eased;
await System.Threading.Tasks.Task.Delay(slideDelay);
}
transform.X = 3000;
}
}

@ -94,6 +94,10 @@
<Ellipse x:Name="NetStatusDot" Width="6" Height="6" Fill="#00E676" VerticalAlignment="Center" />
<TextBlock Text="网络" FontSize="11" Foreground="#6B8CB5" VerticalAlignment="Center" />
<TextBlock x:Name="NetStatusText" Text="已连接" FontSize="11" Foreground="#BCC8D6" VerticalAlignment="Center" />
<TextBlock Text="↓" FontSize="10" Foreground="#4B6A8A" VerticalAlignment="Center" Margin="4,0,0,0" />
<TextBlock x:Name="NetDownText" Text="0 KB/s" FontSize="11" Foreground="#4FC3F7" VerticalAlignment="Center" />
<TextBlock Text="↑" FontSize="10" Foreground="#4B6A8A" VerticalAlignment="Center" Margin="2,0,0,0" />
<TextBlock x:Name="NetUpText" Text="0 KB/s" FontSize="11" Foreground="#4FC3F7" VerticalAlignment="Center" />
</StackPanel>
<!-- Uptime -->
@ -106,7 +110,7 @@
<!-- WCS Version -->
<StackPanel Grid.Column="7" Orientation="Horizontal" Spacing="12" VerticalAlignment="Center" Margin="24,0,0,0">
<TextBlock Text="WCS Core" FontSize="10" Foreground="#4B6A8A" VerticalAlignment="Center" />
<TextBlock Text="Sln.Wcs.UI" FontSize="10" Foreground="#4B6A8A" VerticalAlignment="Center" />
<TextBlock Text="V1.0.0" FontSize="10" Foreground="#4FC3F7" VerticalAlignment="Center" />
</StackPanel>
</Grid>

@ -21,6 +21,9 @@ public partial class MainWindow : Window
private TimeSpan _lastCpuTime;
private DateTime _lastCpuCheck;
private readonly DateTime _startTime;
private long _prevBytesSent;
private long _prevBytesReceived;
private DateTime _prevNetCheck;
public MainWindow(NavigationViewModel navigationVm)
{
@ -48,6 +51,10 @@ public partial class MainWindow : Window
timer.Start();
ClockText.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
// 网络速率初始采样
(_prevBytesSent, _prevBytesReceived) = GetNetworkBytes();
_prevNetCheck = DateTime.UtcNow;
// 退出按钮
LogoutBtn.Click += (_, _) => Close();
}
@ -92,21 +99,28 @@ public partial class MainWindow : Window
};
MemStatusDot.Fill = Brush.Parse(memColor);
// Network status
var isNetworkAvailable = NetworkInterface.GetIsNetworkAvailable();
var activeInterface = false;
if (isNetworkAvailable)
// Network status & speed
var (bytesSent, bytesReceived) = GetNetworkBytes();
var netNow = DateTime.UtcNow;
var netElapsed = (netNow - _prevNetCheck).TotalSeconds;
var activeInterface = bytesSent >= 0;
if (activeInterface && netElapsed > 0)
{
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
if (ni.OperationalStatus == OperationalStatus.Up
&& ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
{
activeInterface = true;
break;
}
}
var downSpeed = (bytesReceived - _prevBytesReceived) / netElapsed;
var upSpeed = (bytesSent - _prevBytesSent) / netElapsed;
NetDownText.Text = FormatSpeed(downSpeed);
NetUpText.Text = FormatSpeed(upSpeed);
}
else
{
NetDownText.Text = "0 KB/s";
NetUpText.Text = "0 KB/s";
}
_prevBytesSent = bytesSent;
_prevBytesReceived = bytesReceived;
_prevNetCheck = netNow;
NetStatusDot.Fill = Brush.Parse(activeInterface ? "#00E676" : "#FF5252");
NetStatusText.Text = activeInterface ? "已连接" : "未连接";
@ -265,4 +279,39 @@ public partial class MainWindow : Window
ContentArea.Child = view;
}
}
private static (long sent, long received) GetNetworkBytes()
{
if (!NetworkInterface.GetIsNetworkAvailable())
return (-1, -1);
long totalSent = 0, totalReceived = 0;
var hasActive = false;
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
if (ni.OperationalStatus == OperationalStatus.Up
&& ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
{
var stats = ni.GetIPStatistics();
totalSent += stats.BytesSent;
totalReceived += stats.BytesReceived;
hasActive = true;
}
}
return hasActive ? (totalSent, totalReceived) : (-1, -1);
}
private static string FormatSpeed(double bytesPerSecond)
{
if (bytesPerSecond < 0) bytesPerSecond = 0;
return bytesPerSecond switch
{
>= 1024 * 1024 => $"{bytesPerSecond / 1024 / 1024:F1} MB/s",
>= 1024 => $"{bytesPerSecond / 1024:F1} KB/s",
_ => $"{bytesPerSecond:F0} B/s"
};
}
}

@ -1,14 +1,14 @@
<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="20,14,20,14" VerticalAlignment="Stretch">
<Grid RowDefinitions="Auto,Auto,*" Margin="20,14,20,14" VerticalAlignment="Stretch" ClipToBounds="True">
<Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索路径编号/名称..." Text="{Binding SearchText}" Width="220" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="12,0,6,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="12,0,6,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*">
<Grid ColumnDefinitions="1.2*,1.3*,0.8*,0.8*,1.2*,1.2*,0.8*,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" />
@ -23,7 +23,7 @@
<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*">
<Grid ColumnDefinitions="1.2*,1.3*,0.8*,0.8*,1.2*,1.2*,0.8*,1*">
<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" />
@ -31,14 +31,72 @@
<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.Column="7" Orientation="Horizontal" Spacing="4">
<Button Content="明细" Tag="{Binding}" Click="Details_Click" Background="#0D7377" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="6,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Slide-out overlay panel -->
<Panel Grid.Row="0" Grid.RowSpan="3" IsVisible="{Binding IsPanelOpen}" ZIndex="10">
<Grid ColumnDefinitions="0.6*,1.4*">
<Border Grid.Column="0" x:Name="Backdrop" Background="#80000000" Opacity="0" PointerPressed="Backdrop_Pressed" />
<Border Grid.Column="1" x:Name="SlideContent"
Background="#0C1622"
BorderBrush="#1A2F4A"
BorderThickness="1,0,0,0">
<Border.RenderTransform>
<TranslateTransform X="3000" />
</Border.RenderTransform>
<Grid RowDefinitions="Auto,*">
<Border Grid.Row="0" Background="#0F1F38" Padding="14,10" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBlock Grid.Column="0" Text="{Binding SlidePanelTitle}" FontSize="14" FontWeight="SemiBold" Foreground="#DDE4F0" VerticalAlignment="Center" />
<Button Grid.Column="1" Content="新增" Command="{Binding AddDetailCommand}" Background="#1565C0" Foreground="White" FontSize="12" Padding="12,5" Margin="0,0,10,0" />
<Button Grid.Column="2" Content="✕" Click="ClosePanel_Click" Background="Transparent" Foreground="#8B9BB5" FontSize="14" Padding="6,2" />
</Grid>
</Border>
<Grid Grid.Row="1" RowDefinitions="Auto,*">
<Border Grid.Row="0" Background="#0C1622" Padding="10,6" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="0.7*,1*,0.9*,0.9*,0.7*,0.5*,0.8*">
<TextBlock Grid.Column="0" Text="路径编号" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="路径名称" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="起点" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="终点" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="设备类型" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="启用" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="操作" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="1" ItemsSource="{Binding DetailItems}" Background="Transparent" Foreground="#DDE4F0" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="10,5" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="0.7*,1*,0.9*,0.9*,0.7*,0.5*,0.8*">
<TextBlock Grid.Column="0" Text="{Binding pathCode}" Foreground="#DDE4F0" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding pathName}" Foreground="#DDE4F0" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding startPoint}" Foreground="#64B5F6" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding endPoint}" Foreground="#64B5F6" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding deviceType}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding isFlag}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<StackPanel Grid.Column="6" Orientation="Horizontal" Spacing="4">
<Button Content="编辑" Tag="{Binding}" Click="DetailEdit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="删除" Tag="{Binding}" Click="DetailDelete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="6,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Border>
</Grid>
</Panel>
</Grid>
</UserControl>

@ -1,6 +1,9 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Sln.Wcs.UI.ViewModels.Base;
using Sln.Wcs.UI.ViewModels.Path;
namespace Sln.Wcs.UI.Views.Path;
@ -28,4 +31,99 @@ public partial class PathInfoListView : UserControl
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private async void Details_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BasePathInfo path && DataContext is PathInfoViewModel vm)
{
vm.LoadDetailsData(path);
Backdrop.Opacity = 0;
if (SlideContent.RenderTransform is TranslateTransform transform)
transform.X = 3000;
vm.IsPanelOpen = true;
await AnimateSlideIn();
}
}
private async void ClosePanel_Click(object? sender, RoutedEventArgs e)
{
await AnimateSlideOut();
if (DataContext is PathInfoViewModel vm)
vm.ClosePanel();
}
private async void Backdrop_Pressed(object? sender, PointerPressedEventArgs e)
{
await AnimateSlideOut();
if (DataContext is PathInfoViewModel vm)
vm.ClosePanel();
}
private async void DetailEdit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BasePathDetails detail && DataContext is PathInfoViewModel vm)
await vm.EditDetailAsync(detail);
}
private void DetailDelete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.BasePathDetails detail && DataContext is PathInfoViewModel vm)
vm.DeleteDetail(detail);
}
private async System.Threading.Tasks.Task AnimateSlideIn()
{
var transform = SlideContent.RenderTransform as TranslateTransform;
if (transform is null) return;
double slideFrom = transform.X;
int steps = 16;
int delay = 16;
for (int i = 1; i <= steps; i++)
{
double t = (double)i / steps;
double eased = 1.0 - System.Math.Pow(1.0 - t, 3.0);
transform.X = slideFrom + (0 - slideFrom) * eased;
await System.Threading.Tasks.Task.Delay(delay);
}
transform.X = 0;
int fadeSteps = 10;
int fadeDelay = 15;
for (int i = 1; i <= fadeSteps; i++)
{
Backdrop.Opacity = (double)i / fadeSteps;
await System.Threading.Tasks.Task.Delay(fadeDelay);
}
Backdrop.Opacity = 1;
}
private async System.Threading.Tasks.Task AnimateSlideOut()
{
var transform = SlideContent.RenderTransform as TranslateTransform;
if (transform is null) return;
int fadeSteps = 8;
int fadeDelay = 12;
for (int i = fadeSteps; i >= 0; i--)
{
Backdrop.Opacity = (double)i / fadeSteps;
await System.Threading.Tasks.Task.Delay(fadeDelay);
}
Backdrop.Opacity = 0;
double slideFrom = transform.X;
int slideSteps = 16;
int slideDelay = 14;
for (int i = 1; i <= slideSteps; i++)
{
double t = (double)i / slideSteps;
double eased = t * t * t;
transform.X = slideFrom + (3000 - slideFrom) * eased;
await System.Threading.Tasks.Task.Delay(slideDelay);
}
transform.X = 3000;
}
}

@ -1,14 +1,14 @@
<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="20,14,20,14" VerticalAlignment="Stretch">
<Grid RowDefinitions="Auto,Auto,*" Margin="20,14,20,14" VerticalAlignment="Stretch" ClipToBounds="True">
<Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,Auto" Margin="0,0,0,8">
<TextBox Grid.Column="0" Watermark="搜索任务编号/物料编号..." Text="{Binding SearchText}" Width="220" Background="#0C1622" Foreground="#DDE4F0" BorderBrush="#1A2F4A" />
<Button Grid.Column="1" Content="搜索" Command="{Binding SearchCommand}" Background="#0F1F38" Foreground="#8B9BB5" Padding="14,6" Margin="12,0,6,0" />
<Button Grid.Column="2" Content="新增" Command="{Binding AddCommand}" Background="#1565C0" Foreground="White" Padding="14,6" Margin="12,0,6,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*">
<Grid ColumnDefinitions="1.2*,1*,1.2*,0.6*,0.6*,0.6*,1*,1*,0.6*,0.6*,0.7*,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" />
@ -27,7 +27,7 @@
<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*">
<Grid ColumnDefinitions="1.2*,1*,1.2*,0.6*,0.6*,0.6*,1*,1*,0.6*,0.6*,0.7*,1*">
<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" />
@ -39,14 +39,74 @@
<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.Column="11" Orientation="Horizontal" Spacing="4">
<Button Content="明细" Tag="{Binding}" Click="Details_Click" Background="#0D7377" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="编辑" Tag="{Binding}" Click="Edit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="删除" Tag="{Binding}" Click="Delete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="6,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Slide-out overlay panel -->
<Panel Grid.Row="0" Grid.RowSpan="3" IsVisible="{Binding IsPanelOpen}" ZIndex="10">
<Grid ColumnDefinitions="0.6*,1.4*">
<Border Grid.Column="0" x:Name="Backdrop" Background="#80000000" Opacity="0" PointerPressed="Backdrop_Pressed" />
<Border Grid.Column="1" x:Name="SlideContent"
Background="#0C1622"
BorderBrush="#1A2F4A"
BorderThickness="1,0,0,0">
<Border.RenderTransform>
<TranslateTransform X="3000" />
</Border.RenderTransform>
<Grid RowDefinitions="Auto,*">
<Border Grid.Row="0" Background="#0F1F38" Padding="14,10" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBlock Grid.Column="0" Text="{Binding SlidePanelTitle}" FontSize="14" FontWeight="SemiBold" Foreground="#DDE4F0" VerticalAlignment="Center" />
<Button Grid.Column="1" Content="新增" Command="{Binding AddDetailCommand}" Background="#1565C0" Foreground="White" FontSize="12" Padding="12,5" Margin="0,0,10,0" />
<Button Grid.Column="2" Content="✕" Click="ClosePanel_Click" Background="Transparent" Foreground="#8B9BB5" FontSize="14" Padding="6,2" />
</Grid>
</Border>
<Grid Grid.Row="1" RowDefinitions="Auto,*">
<Border Grid.Row="0" Background="#0C1622" Padding="10,6" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="0.8*,1*,1.2*,1.2*,0.8*,0.7*,0.7*,1*">
<TextBlock Grid.Column="0" Text="路径编号" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="1" Text="物料编号" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="2" Text="起始位置" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="3" Text="结束位置" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="4" Text="设备类型" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="5" Text="状态" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="6" Text="启用" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
<TextBlock Grid.Column="7" Text="操作" FontSize="11" FontWeight="SemiBold" Foreground="#8B9BB5" />
</Grid>
</Border>
<ListBox Grid.Row="1" ItemsSource="{Binding DetailItems}" Background="Transparent" Foreground="#DDE4F0" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="10,5" BorderBrush="#1A2F4A" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="0.8*,1*,1.2*,1.2*,0.8*,0.7*,0.7*,1*">
<TextBlock Grid.Column="0" Text="{Binding pathCode}" Foreground="#DDE4F0" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="{Binding materialCode}" Foreground="#DDE4F0" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="2" Text="{Binding startPoint}" Foreground="#64B5F6" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="3" Text="{Binding endPoint}" Foreground="#64B5F6" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="4" Text="{Binding deviceType}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="5" Text="{Binding taskStatus}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<TextBlock Grid.Column="6" Text="{Binding isFlag}" Foreground="#8B9BB5" FontSize="11" VerticalAlignment="Center" />
<StackPanel Grid.Column="7" Orientation="Horizontal" Spacing="4">
<Button Content="编辑" Tag="{Binding}" Click="DetailEdit_Click" Background="#0277BD" Foreground="White" FontSize="11" Padding="6,3" />
<Button Content="删除" Tag="{Binding}" Click="DetailDelete_Click" Background="#B71C1C" Foreground="White" FontSize="11" Padding="6,3" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Border>
</Grid>
</Panel>
</Grid>
</UserControl>

@ -1,6 +1,9 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Sln.Wcs.UI.ViewModels.Base;
using Sln.Wcs.UI.ViewModels.Task;
namespace Sln.Wcs.UI.Views.Task;
@ -28,4 +31,99 @@ public partial class TaskQueueListView : UserControl
(vm.GetType().GetProperty("DeleteCommand")?.GetValue(vm) as System.Windows.Input.ICommand)?.Execute(null);
}
}
private async void Details_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.LiveTaskQueue task && DataContext is TaskQueueViewModel vm)
{
vm.LoadDetailsData(task);
Backdrop.Opacity = 0;
if (SlideContent.RenderTransform is TranslateTransform transform)
transform.X = 3000;
vm.IsPanelOpen = true;
await AnimateSlideIn();
}
}
private async void ClosePanel_Click(object? sender, RoutedEventArgs e)
{
await AnimateSlideOut();
if (DataContext is TaskQueueViewModel vm)
vm.ClosePanel();
}
private async void Backdrop_Pressed(object? sender, PointerPressedEventArgs e)
{
await AnimateSlideOut();
if (DataContext is TaskQueueViewModel vm)
vm.ClosePanel();
}
private async void DetailEdit_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.LiveTaskDetail detail && DataContext is TaskQueueViewModel vm)
await vm.EditDetailAsync(detail);
}
private void DetailDelete_Click(object? sender, RoutedEventArgs e)
{
if (sender is Button btn && btn.Tag is Sln.Wcs.Model.Domain.LiveTaskDetail detail && DataContext is TaskQueueViewModel vm)
vm.DeleteDetail(detail);
}
private async System.Threading.Tasks.Task AnimateSlideIn()
{
var transform = SlideContent.RenderTransform as TranslateTransform;
if (transform is null) return;
double slideFrom = transform.X;
int steps = 16;
int delay = 16;
for (int i = 1; i <= steps; i++)
{
double t = (double)i / steps;
double eased = 1.0 - System.Math.Pow(1.0 - t, 3.0);
transform.X = slideFrom + (0 - slideFrom) * eased;
await System.Threading.Tasks.Task.Delay(delay);
}
transform.X = 0;
int fadeSteps = 10;
int fadeDelay = 15;
for (int i = 1; i <= fadeSteps; i++)
{
Backdrop.Opacity = (double)i / fadeSteps;
await System.Threading.Tasks.Task.Delay(fadeDelay);
}
Backdrop.Opacity = 1;
}
private async System.Threading.Tasks.Task AnimateSlideOut()
{
var transform = SlideContent.RenderTransform as TranslateTransform;
if (transform is null) return;
int fadeSteps = 8;
int fadeDelay = 12;
for (int i = fadeSteps; i >= 0; i--)
{
Backdrop.Opacity = (double)i / fadeSteps;
await System.Threading.Tasks.Task.Delay(fadeDelay);
}
Backdrop.Opacity = 0;
double slideFrom = transform.X;
int slideSteps = 16;
int slideDelay = 14;
for (int i = 1; i <= slideSteps; i++)
{
double t = (double)i / slideSteps;
double eased = t * t * t;
transform.X = slideFrom + (3000 - slideFrom) * eased;
await System.Threading.Tasks.Task.Delay(slideDelay);
}
transform.X = 3000;
}
}

Loading…
Cancel
Save