diff --git a/.idea/.idea.Seyounth.Hyosung/.idea/avalonia.xml b/.idea/.idea.Seyounth.Hyosung/.idea/avalonia.xml new file mode 100644 index 0000000..4a277e5 --- /dev/null +++ b/.idea/.idea.Seyounth.Hyosung/.idea/avalonia.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/App.axaml b/Seyounth.Hyosung.Ava/App.axaml new file mode 100644 index 0000000..7d89ab4 --- /dev/null +++ b/Seyounth.Hyosung.Ava/App.axaml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/App.axaml.cs b/Seyounth.Hyosung.Ava/App.axaml.cs new file mode 100644 index 0000000..043a349 --- /dev/null +++ b/Seyounth.Hyosung.Ava/App.axaml.cs @@ -0,0 +1,64 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; +using Seyounth.Hyosung.Ava.ViewModels; +using Seyounth.Hyosung.Ava.Views; +using Seyounth.Hyosung.Core; +using Seyounth.Hyosung.Core.Printer; +using Seyounth.Hyosung.Data; +using Seyounth.Hyosung.Runtime; +using SukiUI; +using SukiUI.Toasts; + +namespace Seyounth.Hyosung.Ava; + +public partial class App : Application +{ + public static IHost _Host { get; private set; } + + public override void Initialize() + { + var builder = Host + .CreateApplicationBuilder(); + builder.Logging.ClearProviders(); + builder.Logging.SetMinimumLevel(LogLevel.Trace); + builder.Logging.AddNLog("nlog.config"); + builder.Configuration.AddJsonFile("PrintTemp.json", true, true); + builder.Configuration.AddJsonFile("appsettings.json", true, true); + builder.Services.Configure(builder.Configuration.GetSection("Print")); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddHyosung(builder.Configuration); + + _Host = builder.Build(); + //_Host.Services.UseHyosung(); + _Host.RunAsync(); + AvaloniaXamlLoader.Load(this); + SukiTheme.GetInstance().ChangeBaseTheme(ThemeVariant.Dark); + } + + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = _Host.Services.GetRequiredService(); + } + + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/Assets/avalonia-logo.ico b/Seyounth.Hyosung.Ava/Assets/avalonia-logo.ico new file mode 100644 index 0000000..da8d49f Binary files /dev/null and b/Seyounth.Hyosung.Ava/Assets/avalonia-logo.ico differ diff --git a/Seyounth.Hyosung.Ava/Models/EnumBindingSourceExtension.cs b/Seyounth.Hyosung.Ava/Models/EnumBindingSourceExtension.cs new file mode 100644 index 0000000..035c3ac --- /dev/null +++ b/Seyounth.Hyosung.Ava/Models/EnumBindingSourceExtension.cs @@ -0,0 +1,23 @@ +using System; +using System.Windows.Markup; +using Avalonia.Markup.Xaml; + +namespace Seyounth.Hyosung.UI.Helpers; + +public class EnumBindingSourceExtension : MarkupExtension +{ + public Type EnumType { get; private set; } + + public EnumBindingSourceExtension(Type enumType) + { + if (enumType is null || !enumType.IsEnum) + throw new Exception("EnumType must be specified and be an Enum"); + + EnumType = enumType; + } + + public override object? ProvideValue(IServiceProvider serviceProvider) + { + return Enum.GetValues(EnumType); + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/Models/EnumDescriptionConverter.cs b/Seyounth.Hyosung.Ava/Models/EnumDescriptionConverter.cs new file mode 100644 index 0000000..1cfb0b8 --- /dev/null +++ b/Seyounth.Hyosung.Ava/Models/EnumDescriptionConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using Avalonia.Data.Converters; + +namespace Seyounth.Hyosung.UI.Helpers; + +public class EnumDescriptionConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value == null) return string.Empty; + + var field = value.GetType().GetField(value.ToString()); + var attr = field?.GetCustomAttribute(); + return attr?.Description ?? value.ToString(); + } + + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/Models/PalletTypeExtensions.cs b/Seyounth.Hyosung.Ava/Models/PalletTypeExtensions.cs new file mode 100644 index 0000000..c7d4d2c --- /dev/null +++ b/Seyounth.Hyosung.Ava/Models/PalletTypeExtensions.cs @@ -0,0 +1,9 @@ +using System; +using Seyounth.Hyosung.Data.Models; + +namespace Seyounth.Hyosung.UI.Helpers; + +public static class PalletTypeExtensions +{ + public static Array Values => Enum.GetValues(typeof(PalletType)); +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/PageBase.cs b/Seyounth.Hyosung.Ava/PageBase.cs new file mode 100644 index 0000000..331e7de --- /dev/null +++ b/Seyounth.Hyosung.Ava/PageBase.cs @@ -0,0 +1,9 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Material.Icons; + +namespace Seyounth.Hyosung.Ava; + +public partial class PageBase(string displayName, MaterialIconKind icon, int index = 0) : ObservableValidator +{ + +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/Program.cs b/Seyounth.Hyosung.Ava/Program.cs new file mode 100644 index 0000000..77e1c9d --- /dev/null +++ b/Seyounth.Hyosung.Ava/Program.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.ReactiveUI; +using System; + +namespace Seyounth.Hyosung.Ava; + +sealed class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace() + .UseReactiveUI(); +} diff --git a/Seyounth.Hyosung.Ava/Seyounth.Hyosung.Ava.csproj b/Seyounth.Hyosung.Ava/Seyounth.Hyosung.Ava.csproj new file mode 100644 index 0000000..63c4f3e --- /dev/null +++ b/Seyounth.Hyosung.Ava/Seyounth.Hyosung.Ava.csproj @@ -0,0 +1,36 @@ + + + WinExe + net8.0 + enable + true + app.manifest + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Seyounth.Hyosung.Ava/ViewLocator.cs b/Seyounth.Hyosung.Ava/ViewLocator.cs new file mode 100644 index 0000000..e3f2be4 --- /dev/null +++ b/Seyounth.Hyosung.Ava/ViewLocator.cs @@ -0,0 +1,32 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using Seyounth.Hyosung.Ava.ViewModels; + +namespace Seyounth.Hyosung.Ava; + +public class ViewLocator : IDataTemplate +{ + public Control? Build(object? data) + { + if (data is null) + return null; + + var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal); + var type = Type.GetType(name); + + if (type != null) + { + var control = (Control)Activator.CreateInstance(type)!; + control.DataContext = data; + return control; + } + + return new TextBlock { Text = "Not Found: " + name }; + } + + public bool Match(object? data) + { + return data is ViewModelBase; + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/ViewModels/HomeViewModel.cs b/Seyounth.Hyosung.Ava/ViewModels/HomeViewModel.cs new file mode 100644 index 0000000..d0bacca --- /dev/null +++ b/Seyounth.Hyosung.Ava/ViewModels/HomeViewModel.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Avalonia.Controls.Notifications; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Seyounth.Hyosung.Data.Models; +using Seyounth.Hyosung.Data.Services; +using Seyounth.Hyosung.Runtime; +using SukiUI.Toasts; + +namespace Seyounth.Hyosung.Ava.ViewModels; + +public partial class HomeViewModel : ObservableObject +{ + [ObservableProperty] private ObservableCollection _varieties; + + [ObservableProperty] private Variety _selectedVariety; + + [ObservableProperty] private List _yarnCarTypes; + + [ObservableProperty] private List _yarnCarSideType; + + [ObservableProperty] private int _selectedYarnCarTypeIndex; + + [ObservableProperty] private int _selectedYarnCarSideTypeIndex; + + + private readonly IVarietyService _varietyService; + + private readonly IHyosungRuntime _runtime; + + private readonly ISukiToastManager _toastManager; + + public HomeViewModel(IVarietyService varietyService, IHyosungRuntime runtime, ISukiToastManager toastManager) + { + _varietyService = varietyService; + _runtime = runtime; + _toastManager = toastManager; + YarnCarTypes = + [ + "A", + "B", + "C", + "D" + ]; + + YarnCarSideType = + [ + "正面", + "反面" + ]; + SelectedYarnCarTypeIndex = 0; + SelectedYarnCarSideTypeIndex = 0; + NavigatedTo(); + } + + public void NavigatedTo() + { + Varieties = new ObservableCollection(_varietyService.GetAll()); + } + + [RelayCommand] + private void OnChangeVariety() + { + try + { + SelectedVariety.YarnCarType = SelectedYarnCarTypeIndex + 1; + SelectedVariety.YarnCarSide = SelectedYarnCarSideTypeIndex + 1; + _runtime.SendVarietyToPlcAsync(SelectedVariety); + _toastManager.CreateToast() + .WithTitle("发送成功") + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Success) + .Dismiss().ByClicking() + .Queue(); + } + catch (Exception e) + { + _toastManager.CreateToast() + .WithTitle("发送失败") + .WithContent(e.Message) + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Error) + .Dismiss().ByClicking() + .Queue(); + } + } + + [RelayCommand] + private void OnChangeVarietyLastNo() + { + try + { + _varietyService.SetLastNo(SelectedVariety.Id, SelectedVariety.LastNo.Value); + _toastManager.CreateToast() + .WithTitle("更改控制号成功") + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Success) + .Dismiss().ByClicking() + .Queue(); + } + catch (Exception e) + { + _toastManager.CreateToast() + .WithTitle("更改控制号失败") + .WithContent(e.Message) + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Error) + .Dismiss().ByClicking() + .Queue(); + } + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/ViewModels/MainWindowViewModel.cs b/Seyounth.Hyosung.Ava/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..fa11023 --- /dev/null +++ b/Seyounth.Hyosung.Ava/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reactive; +using Avalonia.Controls; +using Material.Icons; +using Microsoft.Extensions.DependencyInjection; +using ReactiveUI; +using Seyounth.Hyosung.Ava.Views; +using Seyounth.Hyosung.Data.Models; +using Seyounth.Hyosung.Data.Services; +using SukiUI.Controls; +using SukiUI.Toasts; + +namespace Seyounth.Hyosung.Ava.ViewModels; + +public class MainWindowViewModel : ViewModelBase +{ + public ISukiToastManager ToastManager { get; } + public List ItemCollections { get; set; } + + public MainWindowViewModel(IServiceProvider provider, ISukiToastManager toastManager) + { + ToastManager = toastManager; + ItemCollections = + [ + new SukiSideMenuItem + { + Header = "首页", + PageContent = provider.GetService(), + Classes = { "Compact" } + }, + + new SukiSideMenuItem + { + Header = "品类管理", + PageContent = provider.GetService(), + Classes = { "Compact" } + }, + + new SukiSideMenuItem + { + Header = "辅料管理", + PageContent = provider.GetService(), + Classes = { "Compact" } + } + ]; + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/ViewModels/PalletManagerViewModel.cs b/Seyounth.Hyosung.Ava/ViewModels/PalletManagerViewModel.cs new file mode 100644 index 0000000..dff9674 --- /dev/null +++ b/Seyounth.Hyosung.Ava/ViewModels/PalletManagerViewModel.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Avalonia.Controls.Notifications; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Seyounth.Hyosung.Data.Models; +using Seyounth.Hyosung.Data.Services; +using SukiUI.Toasts; + +namespace Seyounth.Hyosung.Ava.ViewModels; + +public partial class PalletManagerViewModel : ObservableObject +{ + public class SavePalletCompletedMessage; + + public string Title => "辅料管理"; + [ObservableProperty] private ObservableCollection _pallets; + + private readonly ISukiToastManager _toastManager; + + private readonly IPalletService _palletService; + + public PalletManagerViewModel(IPalletService palletService, ISukiToastManager toastManager) + { + _toastManager = toastManager; + _palletService = palletService; + Pallets = new ObservableCollection(palletService.GetPallets()); + } + + [RelayCommand] + private void AddNewRow() + { + Pallets.Add(new Pallet()); + } + + [RelayCommand] + private void AddPallet() + { + var newPallet = new Pallet + { + /* 初始化默认值 */ + }; + Pallets.Add(newPallet); + } + + [RelayCommand] + private void OnDeletePallet(object obj) + { + if (obj is Pallet pallet) + { + Pallets.Remove(pallet); + _palletService.DeletePalletAsync(pallet); + _toastManager.CreateToast() + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Success) + .Dismiss().ByClicking() + .Queue(); + } + else + { + Console.Write("Object is not a Pallet"); + } + } + + [RelayCommand] + private void OnSavePallet(object obj) + { + if (obj is Pallet pallet) + { + if (pallet.Id == 0) + { + var id = _palletService.AddPalletAsync(pallet).Result; + _toastManager.CreateToast() + .WithTitle("新增辅料成功") + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Success) + .Dismiss().ByClicking() + .Queue(); + } + else + { + _palletService.UpdatePalletAsync(pallet); + _toastManager.CreateToast() + .WithTitle("修改辅料成功") + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Success) + .Dismiss().ByClicking() + .Queue(); + } + } + + + WeakReferenceMessenger.Default.Send(new SavePalletCompletedMessage()); + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/ViewModels/VarietyManagerViewModel.cs b/Seyounth.Hyosung.Ava/ViewModels/VarietyManagerViewModel.cs new file mode 100644 index 0000000..718e348 --- /dev/null +++ b/Seyounth.Hyosung.Ava/ViewModels/VarietyManagerViewModel.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Controls.Notifications; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Seyounth.Hyosung.Data.Models; +using Seyounth.Hyosung.Data.Services; +using SukiUI.Toasts; + +namespace Seyounth.Hyosung.Ava.ViewModels; + +public partial class VarietyManagerViewModel : ObservableObject +{ + [ObservableProperty] private ObservableCollection _varieties; + [ObservableProperty] private ObservableCollection _pallets; + + [ObservableProperty] private ObservableCollection _honeyPallets = new(); + + [ObservableProperty] private ObservableCollection _paperPallets = new(); + + [ObservableProperty] private ObservableCollection _trays = new(); + + + private readonly ISukiToastManager _toastManager; + private readonly IVarietyService _varietyService; + private readonly IPalletService _palletService; + + public void Reload() + { + Pallets = new ObservableCollection(_palletService.GetPallets()); + HoneyPallets = new ObservableCollection(Pallets.Where(p => p.Type == PalletType.Honey)); + var papers = Pallets.Where(p => p.Type == PalletType.Paper).ToList(); + papers.Add(new Pallet()); + papers = papers.OrderBy(p => p.Length).ToList(); + PaperPallets = new ObservableCollection(papers); + Trays = new ObservableCollection(Pallets.Where(p => + p.Type is PalletType.Plywood or PalletType.Wood)); + var varieties = _varietyService.GetAll().Select(v => + { + // 匹配相同ID的托盘对象 + v.TopAndBottomPallet = HoneyPallets.FirstOrDefault(p => p.Id == v.TopAndBottomPallet.Id); + v.MiddlePallet = HoneyPallets.FirstOrDefault(p => p.Id == v.MiddlePallet.Id); + v.Tray = Trays.FirstOrDefault(p => p.Id == v.Tray.Id); + v.PaperTray = v.PaperTray is null + ? PaperPallets.First() + : PaperPallets.FirstOrDefault(p => + p.Id == v.PaperTray.Id); + return v; + }); + + Varieties = new ObservableCollection(varieties); + } + + public VarietyManagerViewModel(IVarietyService varietyService, ISukiToastManager toastManager, + IPalletService palletService) + { + _toastManager = toastManager; + _palletService = palletService; + _varietyService = varietyService; + Reload(); + } + + + [RelayCommand] + private void AddNewRow() + { + Varieties.Add(new Variety()); + } + + + [RelayCommand] + private void OnDeletePallet(object obj) + { + if (obj is Variety variety) + { + Varieties.Remove(variety); + _varietyService.DeleteVarietyAsync(variety); + _toastManager.CreateToast() + .WithTitle("删除成功") + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Success) + .Dismiss().ByClicking() + .Queue(); + } + else + { + Console.Write("Object is not a Pallet"); + } + } + + [RelayCommand] + private async Task OnSavePallet(object obj) + { + if (obj is Variety variety) + { + await _varietyService.AddVarietyAsync(variety); + _toastManager.CreateToast() + .WithTitle("保存成功") + .Dismiss().After(TimeSpan.FromSeconds(3)) + .OfType(NotificationType.Success) + .Dismiss().ByClicking() + .Queue(); + //Pallets = new ObservableCollection(_palletService.GetPallets()); + } + } +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/ViewModels/ViewModelBase.cs b/Seyounth.Hyosung.Ava/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..260fd58 --- /dev/null +++ b/Seyounth.Hyosung.Ava/ViewModels/ViewModelBase.cs @@ -0,0 +1,7 @@ +using ReactiveUI; + +namespace Seyounth.Hyosung.Ava.ViewModels; + +public class ViewModelBase : ReactiveObject +{ +} \ No newline at end of file diff --git a/Seyounth.Hyosung.Ava/Views/HomePage.axaml b/Seyounth.Hyosung.Ava/Views/HomePage.axaml new file mode 100644 index 0000000..494ca08 --- /dev/null +++ b/Seyounth.Hyosung.Ava/Views/HomePage.axaml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + +