From 9ad7ea432ab2c6bf89589acf08113812cb3f3dfc Mon Sep 17 00:00:00 2001 From: denglihong2007 <98096191+denglihong2007@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:46:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20#141=20#202=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=A3=80=E7=A5=A8=E5=8F=A3/=E5=80=99=E8=BD=A6=E5=AE=A4?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CRSim.Core/CRSim.Core.csproj | 4 +- CRSim.Core/Models/Simulator/TicketCheck.cs | 5 +- CRSim.Core/Models/Simulator/TrainStop.cs | 4 +- CRSim.Core/Models/Simulator/WaitingArea.cs | 8 +- CRSim.Core/Services/DatabaseService.cs | 20 +- .../Abstractions/IScreenViewModel.cs | 2 +- .../CRSim.ScreenSimulator.csproj | 2 +- CRSim.ScreenSimulator/Models/Session.cs | 2 +- .../ViewModels/BaseScreenViewModel.cs | 22 +- .../MetroPlatformScreenViewModel.cs | 2 +- CRSim/App.xaml.cs | 46 +-- CRSim/CRSim.csproj | 4 +- CRSim/Converters/ListToStringConverter.cs | 23 -- .../TicketChecksToStringConverter.cs | 27 ++ CRSim/MainWindow.xaml | 2 +- CRSim/Models/CheckListItem.cs | 9 - CRSim/Selectors/FileName.cs | 18 + CRSim/Services/DialogService.cs | 96 ++--- CRSim/Services/IDialogService.cs | 11 +- .../PluginManagementPageViewModel.cs | 4 +- .../ScreenSimulatorPageViewModel.cs | 22 +- CRSim/ViewModels/SettingsPageViewModel.cs | 4 +- .../StationManagementPageViewModel.cs | 332 +++++++++++------- CRSim/Views/DialogContents/InputDialog.xaml | 2 +- .../Views/DialogContents/InputDialog.xaml.cs | 4 +- .../DialogContents/TicketCheckDialog.xaml | 15 - .../DialogContents/TicketCheckDialog.xaml.cs | 90 ----- .../Views/DialogContents/TrainStopDialog.xaml | 26 +- .../DialogContents/TrainStopDialog.xaml.cs | 67 ++-- CRSim/Views/ScreenSimulatorPage.xaml | 2 +- CRSim/Views/StartWindow.xaml.cs | 12 - CRSim/Views/StationManagementPage.xaml | 94 ++--- Directory.Build.props | 2 +- 33 files changed, 450 insertions(+), 533 deletions(-) delete mode 100644 CRSim/Converters/ListToStringConverter.cs create mode 100644 CRSim/Converters/TicketChecksToStringConverter.cs delete mode 100644 CRSim/Models/CheckListItem.cs create mode 100644 CRSim/Selectors/FileName.cs delete mode 100644 CRSim/Views/DialogContents/TicketCheckDialog.xaml delete mode 100644 CRSim/Views/DialogContents/TicketCheckDialog.xaml.cs diff --git a/CRSim.Core/CRSim.Core.csproj b/CRSim.Core/CRSim.Core.csproj index df37937..c982424 100644 --- a/CRSim.Core/CRSim.Core.csproj +++ b/CRSim.Core/CRSim.Core.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/CRSim.Core/Models/Simulator/TicketCheck.cs b/CRSim.Core/Models/Simulator/TicketCheck.cs index 6404bdb..c6591d0 100644 --- a/CRSim.Core/Models/Simulator/TicketCheck.cs +++ b/CRSim.Core/Models/Simulator/TicketCheck.cs @@ -2,7 +2,8 @@ { public class TicketCheck { - public string WaitingAreaName { get; set; } + public Guid Id { get; set; } = Guid.NewGuid(); public string Name { get; set; } + public override string ToString() => Name; } -} +} \ No newline at end of file diff --git a/CRSim.Core/Models/Simulator/TrainStop.cs b/CRSim.Core/Models/Simulator/TrainStop.cs index 9893649..dcd516f 100644 --- a/CRSim.Core/Models/Simulator/TrainStop.cs +++ b/CRSim.Core/Models/Simulator/TrainStop.cs @@ -15,9 +15,7 @@ namespace CRSim.Core.Models [JsonConverter(typeof(TimeSpanJsonConverter))] public TimeSpan? DepartureTime { get; set; } - public List? TicketChecks { get; set; } - - public string? WaitingArea { get; set; } + public List? TicketCheckIds { get; set; } public string? Origin { get; set; } diff --git a/CRSim.Core/Models/Simulator/WaitingArea.cs b/CRSim.Core/Models/Simulator/WaitingArea.cs index 440166f..0b96f71 100644 --- a/CRSim.Core/Models/Simulator/WaitingArea.cs +++ b/CRSim.Core/Models/Simulator/WaitingArea.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.ObjectModel; namespace CRSim.Core.Models { public class WaitingArea { public string Name { get; set; } - public List TicketChecks { get; set; } = []; + public ObservableCollection TicketChecks { get; set; } = []; } } diff --git a/CRSim.Core/Services/DatabaseService.cs b/CRSim.Core/Services/DatabaseService.cs index 979945b..5ed5e23 100644 --- a/CRSim.Core/Services/DatabaseService.cs +++ b/CRSim.Core/Services/DatabaseService.cs @@ -20,21 +20,10 @@ namespace CRSim.Core.Services { try { - var json = File.ReadAllText(jsonFilePath).Replace("StationStop", "TrainStop");// 修复旧版本的数据 + var json = File.ReadAllText(jsonFilePath).Replace("StationStop", "TrainStop"); var data = JsonSerializer.Deserialize(json,JsonContext.Default.Json); _stations = data.Stations; _trainNumbers = data.TrainNumbers; - - foreach (var station in _stations) - { - foreach(var trainStop in station.TrainStops) - { - if (trainStop.WaitingArea == null && trainStop.DepartureTime.HasValue) - { - trainStop.WaitingArea = station.WaitingAreas.Where(x => x.TicketChecks.Contains(trainStop.TicketChecks[0])).FirstOrDefault().Name; - } - } - }// 修复旧版本的数据 } catch { @@ -157,9 +146,9 @@ namespace CRSim.Core.Services foreach (var ticketCheckName in ticketCheck.Split('|')) { - if (!string.IsNullOrWhiteSpace(ticketCheckName) && !waitingArea.TicketChecks.Contains(ticketCheckName)) + if (!string.IsNullOrWhiteSpace(ticketCheckName) && !waitingArea.TicketChecks.Any(x => x.Name == ticketCheckName)) { - waitingArea.TicketChecks.Add(ticketCheckName); + waitingArea.TicketChecks.Add(new TicketCheck { Name = ticketCheckName }); } } @@ -187,10 +176,9 @@ namespace CRSim.Core.Services Number = data[0], Terminal = data[3], Origin = data[2], - TicketChecks = [.. ticketCheck.Split('|')], + TicketCheckIds = [.. waitingArea.TicketChecks.Select(x=>x.Id)], ArrivalTime = arrivalTime, DepartureTime = departureTime, - WaitingArea = waitingAreaName, Platform = platform, Length = data[0].StartsWith('G') || data[0].StartsWith('D') || data[0].StartsWith('C') ? Math.Abs(data[0].GetHashCode()) % 3 == 0 ? 8 : 16 : 18, Landmark = data[8] + "色" ?? null, diff --git a/CRSim.ScreenSimulator/Abstractions/IScreenViewModel.cs b/CRSim.ScreenSimulator/Abstractions/IScreenViewModel.cs index 9f3f22d..7a961af 100644 --- a/CRSim.ScreenSimulator/Abstractions/IScreenViewModel.cs +++ b/CRSim.ScreenSimulator/Abstractions/IScreenViewModel.cs @@ -8,7 +8,7 @@ namespace CRSim.ScreenSimulator.Abstractions { ITimeService TimeService { get; } Dispatcher UIDispatcher { get; set; } - void LoadData(Station station, string ticketCheck, string platform); + void LoadData(Station station, TicketCheck? ticketCheck, string platform); string? Text { get; set; } Uri Video { get; set; } int Location { get; set; } diff --git a/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj b/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj index 586c9c9..74ab5dc 100644 --- a/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj +++ b/CRSim.ScreenSimulator/CRSim.ScreenSimulator.csproj @@ -18,7 +18,7 @@ - + diff --git a/CRSim.ScreenSimulator/Models/Session.cs b/CRSim.ScreenSimulator/Models/Session.cs index 1543667..6665abd 100644 --- a/CRSim.ScreenSimulator/Models/Session.cs +++ b/CRSim.ScreenSimulator/Models/Session.cs @@ -10,7 +10,7 @@ namespace CRSim.ScreenSimulator.Models public string? Text { get; set; } public Uri? Video { get; set; } public Station? Station { get; set; } - public string? TicketCheck { get; set; } + public TicketCheck? TicketCheck { get; set; } public string? PlatformName { get; set; } public int? Loaction { get; set; } } diff --git a/CRSim.ScreenSimulator/ViewModels/BaseScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/BaseScreenViewModel.cs index 9e5d0ed..5a6ee1d 100644 --- a/CRSim.ScreenSimulator/ViewModels/BaseScreenViewModel.cs +++ b/CRSim.ScreenSimulator/ViewModels/BaseScreenViewModel.cs @@ -1,10 +1,11 @@ using CommunityToolkit.Mvvm.ComponentModel; -using CRSim.Core.Models; -using CRSim.ScreenSimulator.Models; -using System.Windows; -using System.Collections.ObjectModel; using CRSim.Core.Abstractions; +using CRSim.Core.Models; using CRSim.ScreenSimulator.Abstractions; +using CRSim.ScreenSimulator.Models; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; namespace CRSim.ScreenSimulator.ViewModels { @@ -104,11 +105,11 @@ namespace CRSim.ScreenSimulator.ViewModels OnPropertyChanged(nameof(ScreenB)); } - public void LoadData(Station station, string ticketCheck, string platform) + public void LoadData(Station station, TicketCheck? ticketCheck, string platform) { ThisStation = station; ThisPlatform = platform; - if (ticketCheck != string.Empty) ThisTicketCheck = ticketCheck.Split(" - ")[1];//重复检票口名称的临时解决方案 + ThisTicketCheck = ticketCheck?.Name; var trains = station.TrainStops; foreach (var trainNumber in trains) { @@ -117,7 +118,7 @@ namespace CRSim.ScreenSimulator.ViewModels (StationType == StationType.Both || trainNumber.StationType == StationType.Both || StationType == trainNumber.StationType) && - (ticketCheck == string.Empty || (trainNumber.WaitingArea==ticketCheck.Split(" - ")[0] && trainNumber.TicketChecks.Contains(ticketCheck.Split(" - ")[1]))) &&//重复检票口名称的临时解决方案 + (ticketCheck == null || trainNumber.TicketCheckIds.Contains(ticketCheck.Id)) && (platform == string.Empty || trainNumber.Platform == platform)) { var now = TimeService.GetDateTimeNow(); @@ -138,8 +139,11 @@ namespace CRSim.ScreenSimulator.ViewModels Origin = trainNumber.Origin, ArrivalTime = AdjustTime(trainNumber.ArrivalTime), DepartureTime = AdjustTime(trainNumber.DepartureTime), - TicketChecks = trainNumber.TicketChecks, - WaitingArea = trainNumber.StationType == StationType.Arrival ? string.Empty : trainNumber.WaitingArea, + TicketChecks = [.. station.WaitingAreas + .SelectMany(w => w.TicketChecks) + .Where(tc => trainNumber.TicketCheckIds.Contains(tc.Id)) + .Select(tc => tc.Name)], + WaitingArea = trainNumber.StationType == StationType.Arrival ? string.Empty : string.Join(" ", station.WaitingAreas.Where(x => x.TicketChecks.Any(y => trainNumber.TicketCheckIds.Contains(y.Id))).Select(x=>x.Name)), Platform = trainNumber.Platform, Length = trainNumber.Length, Landmark = trainNumber.Landmark, diff --git a/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs index 20c6f24..b0db7f8 100644 --- a/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs +++ b/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs @@ -44,7 +44,7 @@ namespace CRSim.ScreenSimulator.ViewModels OnPropertyChanged(nameof(FirstTrain)); OnPropertyChanged(nameof(SecondTrain)); } - public void LoadData(Station station, string _ticketCheck, string platform) + public void LoadData(Station station, TicketCheck? _ticketCheck, string platform) { var trains = station.TrainStops; foreach (var trainNumber in trains) diff --git a/CRSim/App.xaml.cs b/CRSim/App.xaml.cs index a7a68ee..afbee34 100644 --- a/CRSim/App.xaml.cs +++ b/CRSim/App.xaml.cs @@ -1,4 +1,6 @@ -namespace CRSim +using System.Diagnostics; + +namespace CRSim { public partial class App : Application { @@ -7,12 +9,11 @@ public string[] commandLineArgs; public CommandLineOptions parsedOptions; public static string AppVersion { get; set; } = Assembly.GetAssembly(typeof(App)).GetName().Version.ToString(); - private Mutex mutex; + private readonly Mutex mutex; public App() { - bool isNewInstance; - mutex = new Mutex(true,"CRSim",out isNewInstance); + mutex = new Mutex(true, "CRSim", out bool isNewInstance); if (!isNewInstance) { Environment.Exit(0); @@ -52,49 +53,34 @@ InitializeComponent(); } - protected override void OnLaunched(LaunchActivatedEventArgs args) + protected override async void OnLaunched(LaunchActivatedEventArgs args) { - ShowSplashScreen(); - } - - private void ShowSplashScreen() - { - var splashScreenWindow = AppHost.Services.GetService(); - splashScreenWindow.SplashScreenClosed += OnSplashScreenClosed; + var splashScreenWindow = AppHost.Services.GetRequiredService(); splashScreenWindow.Activate(); - _ = PerformInitializationAsync(splashScreenWindow); + await PerformInitializationAsync(splashScreenWindow); + MainWindow = AppHost.Services.GetRequiredService(); + MainWindow.Activate(); + splashScreenWindow.Close(); } - private static async Task PerformInitializationAsync(StartWindow splashScreen) + private static async Task PerformInitializationAsync(StartWindow startWindow) { try { - var startWindow = AppHost.Services.GetService(); - await Task.Delay(2000); + if(!Debugger.IsAttached) await Task.Delay(2000); startWindow.Status = "正在加载设置..."; AppHost.Services.GetService().LoadSettings(); - await Task.Delay(500); + if (!Debugger.IsAttached) await Task.Delay(500); startWindow.Status = "正在加载数据库..."; AppHost.Services.GetService().ImportData(AppPaths.ConfigFilePath); - await Task.Delay(500); + if (!Debugger.IsAttached) await Task.Delay(500); startWindow.Status = new[] { "正在控票…", "正在关闭垃圾桶盖…", "正在刷绿车底…", "正在准备降弓用刑…" }[new Random().Next(4)]; - await Task.Delay(500); + if (!Debugger.IsAttached) await Task.Delay(500); } catch (Exception ex) { Console.WriteLine($"初始化错误: {ex.Message}"); } - finally - { - splashScreen.CompleteInitialization(); - } - } - - private void OnSplashScreenClosed(object sender, EventArgs e) - { - var mainWindow = AppHost.Services.GetService(); - mainWindow.Activate(); - MainWindow = mainWindow; } [System.Runtime.InteropServices.DllImport("kernel32.dll")] diff --git a/CRSim/CRSim.csproj b/CRSim/CRSim.csproj index cf1d521..bd782c0 100644 --- a/CRSim/CRSim.csproj +++ b/CRSim/CRSim.csproj @@ -55,8 +55,8 @@ - - + + diff --git a/CRSim/Converters/ListToStringConverter.cs b/CRSim/Converters/ListToStringConverter.cs deleted file mode 100644 index abf61b9..0000000 --- a/CRSim/Converters/ListToStringConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.UI.Xaml.Data; - -namespace CRSim.Converters -{ - public partial class ListToStringConverter : IValueConverter - { - public string Separator { get; set; } = ""; // 默认分隔符是双引号间隔 - public object Convert(object value, Type targetType, object parameter, string language) - { - if (value is List ticketChecks) - { - // 使用自定义的 Separator 将字符串连接 - return string.Join(Separator, ticketChecks); - } - return string.Empty; - } - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - // 通常不需要实现 ConvertBack - return null; - } - } -} diff --git a/CRSim/Converters/TicketChecksToStringConverter.cs b/CRSim/Converters/TicketChecksToStringConverter.cs new file mode 100644 index 0000000..9e94aba --- /dev/null +++ b/CRSim/Converters/TicketChecksToStringConverter.cs @@ -0,0 +1,27 @@ +using Microsoft.UI.Xaml.Data; + +namespace CRSim.Converters +{ + public partial class TicketChecksToStringConverter : IValueConverter + { + public string Separator { get; set; } = ""; + public ObservableCollection WaitingAreas { get; set; } = []; + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is List ticketCheckIds) + { + var ticketChecks = WaitingAreas + .SelectMany(w => w.TicketChecks + .Where(tc => ticketCheckIds.Contains(tc.Id)) + .Select(tc => $"{w.Name}|{tc.Name}")); + return string.Join(Separator, ticketChecks); + + } + return string.Empty; + } + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/CRSim/MainWindow.xaml b/CRSim/MainWindow.xaml index 64f11a4..1d246d0 100644 --- a/CRSim/MainWindow.xaml +++ b/CRSim/MainWindow.xaml @@ -26,7 +26,7 @@ - + diff --git a/CRSim/Models/CheckListItem.cs b/CRSim/Models/CheckListItem.cs deleted file mode 100644 index b4e85e3..0000000 --- a/CRSim/Models/CheckListItem.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace CRSim.Models -{ - public partial class CheckListItem : ObservableObject - { - [ObservableProperty] - public partial bool IsSelected { get; set; } - public string Name { get; set; } - } -} diff --git a/CRSim/Selectors/FileName.cs b/CRSim/Selectors/FileName.cs new file mode 100644 index 0000000..32dde24 --- /dev/null +++ b/CRSim/Selectors/FileName.cs @@ -0,0 +1,18 @@ +namespace CRSim.Selectors +{ + public partial class TreeViewTemplateSelector : DataTemplateSelector + { + public DataTemplate WaitingAreaTemplate { get; set; } + public DataTemplate TicketCheckTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item) + { + return item switch + { + WaitingArea => WaitingAreaTemplate, + TicketCheck => TicketCheckTemplate, + _ => base.SelectTemplateCore(item) + }; + } + } +} diff --git a/CRSim/Services/DialogService.cs b/CRSim/Services/DialogService.cs index 2fb76e2..19d1eca 100644 --- a/CRSim/Services/DialogService.cs +++ b/CRSim/Services/DialogService.cs @@ -1,4 +1,5 @@ -using Windows.Storage; +using Microsoft.Win32; +using Windows.Storage; using Windows.Storage.Pickers; namespace CRSim.Services @@ -6,7 +7,7 @@ namespace CRSim.Services public class DialogService : IDialogService { public XamlRoot? XamlRoot { get; set; } - public async Task GetInputAsync(string title) + public async Task GetInputAsync(string title,string placeholder) { ContentDialog dialog = new() { @@ -16,7 +17,7 @@ namespace CRSim.Services PrimaryButtonText = "确定", CloseButtonText = "取消", DefaultButton = ContentDialogButton.Primary, - Content = new InputDialog() + Content = new InputDialog(placeholder) }; var result = await dialog.ShowAsync(); if (result == ContentDialogResult.Primary) @@ -72,50 +73,25 @@ namespace CRSim.Services }; await dialog.ShowAsync(); } - public async Task<(string, List)> GetInputTicketCheckAsync(List waitingAreaNames) - { - bool isButtonEnabled = false; - ContentDialog dialog = new() - { - XamlRoot = XamlRoot, - Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, - Title = "新增检票口", - PrimaryButtonText = "确定", - CloseButtonText = "取消", - DefaultButton = ContentDialogButton.Primary, - IsPrimaryButtonEnabled = false - }; - var ticketCheckDialog = new TicketCheckDialog(waitingAreaNames, isValid => dialog.IsPrimaryButtonEnabled = isValid); - dialog.Content = ticketCheckDialog; - dialog.PrimaryButtonClick += (s, e) => - { - ticketCheckDialog.GenerateTicketChecks(); - }; - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - var inputDialog = dialog.Content as TicketCheckDialog; - return (inputDialog.SelectedWaitingArea, inputDialog.GeneratedTicketChecks); - } - return (null, null); - } - public async Task GetFileAsync(string[] filter) + public string? GetFile(string[] extensions) { - var openPicker = new FileOpenPicker(); - var window = App.MainWindow; - var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window); - WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd); - openPicker.ViewMode = PickerViewMode.Thumbnail; - openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; - foreach (var f in filter) + var dlg = new OpenFileDialog(); + dlg.Multiselect = false; + + var filters = extensions + .Select(ext => + { + var extNoDot = ext.TrimStart('.').ToUpperInvariant(); + return $"{extNoDot} 文件 (*{ext})|*{ext}"; + }); + + dlg.Filter = string.Join("|", filters); + + bool? result = dlg.ShowDialog(); + if (result == true) { - openPicker.FileTypeFilter.Add(f); - } - var file = await openPicker.PickSingleFileAsync(); - if (file != null) - { - return file.Path; + return dlg.FileName; } return null; } @@ -134,7 +110,7 @@ namespace CRSim.Services return null; } - public async Task GetInputTrainStopAsync(List ticketChecks, List platforms) + public async Task GetInputTrainStopAsync(List waitingAreas, List platforms) { bool isButtonEnabled = false; ContentDialog dialog = new() @@ -147,7 +123,7 @@ namespace CRSim.Services DefaultButton = ContentDialogButton.Primary, IsPrimaryButtonEnabled = false }; - var platformDialog = new TrainStopDialog(ticketChecks,platforms,isValid => dialog.IsPrimaryButtonEnabled = isValid); + var platformDialog = new TrainStopDialog(waitingAreas, platforms,isValid => dialog.IsPrimaryButtonEnabled = isValid); dialog.Content = platformDialog; dialog.PrimaryButtonClick += (s, e) => { @@ -162,7 +138,7 @@ namespace CRSim.Services return null; } - public async Task EditInputTrainStopAsync(List ticketChecks, List platforms, TrainStop trainStop) + public async Task EditInputTrainStopAsync(List waitingAreas, List platforms, TrainStop trainStop) { bool isButtonEnabled = false; ContentDialog dialog = new() @@ -175,7 +151,7 @@ namespace CRSim.Services DefaultButton = ContentDialogButton.Primary, IsPrimaryButtonEnabled = false }; - var platformDialog = new TrainStopDialog(ticketChecks, platforms, trainStop, isValid => dialog.IsPrimaryButtonEnabled = isValid); + var platformDialog = new TrainStopDialog(waitingAreas, platforms, trainStop, isValid => dialog.IsPrimaryButtonEnabled = isValid); dialog.Content = platformDialog; dialog.PrimaryButtonClick += (s, e) => { @@ -190,17 +166,21 @@ namespace CRSim.Services return null; } - public async Task SaveFileAsync(string filter, string f) + public string? SaveFile(string extension, string suggestedFileName) { - FileSavePicker savePicker = new(); - var window = App.MainWindow; - var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window); - WinRT.Interop.InitializeWithWindow.Initialize(savePicker, hWnd); - savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary; - savePicker.SuggestedFileName = f; - savePicker.FileTypeChoices.Add("File", [filter]); - StorageFile file = await savePicker.PickSaveFileAsync(); - return file?.Path; + var dlg = new SaveFileDialog + { + FileName = suggestedFileName + }; + var extNoDot = extension.TrimStart('.').ToUpperInvariant(); + var filter = $"{extNoDot} Files (*{extension})|*{extension}"; + dlg.Filter = filter; + bool? result = dlg.ShowDialog(); + if (result == true) + { + return dlg.FileName; + } + return null; } public async Task?> GetInputPlatformAsync() diff --git a/CRSim/Services/IDialogService.cs b/CRSim/Services/IDialogService.cs index 269e513..f7309d1 100644 --- a/CRSim/Services/IDialogService.cs +++ b/CRSim/Services/IDialogService.cs @@ -2,17 +2,16 @@ { public interface IDialogService { - Task GetFileAsync(string[] filter); - Task SaveFileAsync(string filter,string fileName); - Task GetInputAsync(string title); + string? GetFile(string[] filter); + string? SaveFile(string filter,string fileName); + Task GetInputAsync(string title,string placeholder); Task GetConfirmAsync(string title); Task ShowMessageAsync(string title, string message); Task ShowTextAsync(string title, string message); TrainStop? GetInputTrainNumberStop(TrainStop? t); - Task GetInputTrainStopAsync(List ticketChecks, List platforms); - Task EditInputTrainStopAsync(List ticketChecks, List platforms, TrainStop trainStop); + Task GetInputTrainStopAsync(List waitingAreas, List platforms); + Task EditInputTrainStopAsync(List waitingAreas, List platforms, TrainStop trainStop); Task?> GetInputPlatformAsync(); - Task<(string, List)> GetInputTicketCheckAsync(List waitingAreaNames); XamlRoot? XamlRoot { get; set; } } } diff --git a/CRSim/ViewModels/PluginManagementPageViewModel.cs b/CRSim/ViewModels/PluginManagementPageViewModel.cs index 7561694..fd3af09 100644 --- a/CRSim/ViewModels/PluginManagementPageViewModel.cs +++ b/CRSim/ViewModels/PluginManagementPageViewModel.cs @@ -56,7 +56,7 @@ public partial class PluginManagementPageViewModel : ObservableObject [RelayCommand] public async Task InstallPluginLocal() { - if (await _dialogService.GetFileAsync([".crsp"]) is string filePath) + if (_dialogService.GetFile([".crsp"]) is string filePath) { try { @@ -85,7 +85,7 @@ public partial class PluginManagementPageViewModel : ObservableObject public async Task PackPlugin() { if (SelectedPlugin == null) return; - string? filePath = await _dialogService.SaveFileAsync(".crsp", $"{SelectedPlugin.Manifest.Id}"); + string? filePath = _dialogService.SaveFile(".crsp", $"{SelectedPlugin.Manifest.Id}"); if (filePath == null) return; try { diff --git a/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs index bb6a5fd..964172a 100644 --- a/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs +++ b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs @@ -29,7 +29,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable plugin [ObservableProperty] public partial bool LocationNeeded { get; set; } public List StationNames => [.. databaseService.GetAllStations().Select(x => x.Name)]; - public ObservableCollection TicketChecks { get; private set; } = []; + public ObservableCollection TicketChecks { get; private set; } = []; public ObservableCollection Platforms { get; private set; } = []; public ObservableCollection Locations { get; private set; } = []; @@ -41,7 +41,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable plugin public string SelectedStationName = ""; - public string SelectedTicketCheck = ""; + public TicketCheck? SelectedTicketCheck; public string SelectedPlatformName = ""; @@ -79,14 +79,18 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable plugin var station = databaseService.GetStationByName(v); SelectedStationName = station.Name; SelectedPlatformName = ""; - SelectedTicketCheck = ""; + SelectedTicketCheck = null; SelectedLoaction = 0; TicketChecks.Clear(); foreach (var waitingArea in station.WaitingAreas) { - foreach (string ticketCheck in waitingArea.TicketChecks) + foreach (var ticketCheck in waitingArea.TicketChecks) { - TicketChecks.Add($"{waitingArea.Name} - {ticketCheck}");//重复检票口名称的临时解决方案 + TicketChecks.Add(new TicketCheck() + { + Id = ticketCheck.Id, + Name = waitingArea.Name + " - " + ticketCheck.Name, + }); } } Platforms.Clear(); @@ -101,7 +105,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable plugin [RelayCommand] public void SelectTicketCheck(object s) { - if (s is string ticketCheck) + if (s is TicketCheck ticketCheck) { SelectedTicketCheck = ticketCheck; } @@ -134,7 +138,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable plugin [RelayCommand] public async Task SelectVideo() { - string? Path = await dialogService.GetFileAsync([".wmv",".asf",".mp4",".m4v",".mov",".mpg",".mpeg"]); + string? Path = dialogService.GetFile([".wmv",".asf",".mp4",".m4v",".mov",".mpg",".mpeg"]); if (Path == null) return; Video = Path; } @@ -150,7 +154,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable plugin Text = (TextNeeded && !string.IsNullOrWhiteSpace(Text)) ? Text : null, Video = (VideoNeeded && !string.IsNullOrWhiteSpace(Video)) ? new Uri(Video) : null, Station = StationNeeded ? databaseService.GetStationByName(SelectedStationName) : null, - TicketCheck = TicketCheckNeeded ? SelectedTicketCheck : string.Empty, + TicketCheck = TicketCheckNeeded ? SelectedTicketCheck : null, PlatformName = PlatformNeeded ? SelectedPlatformName : string.Empty, Loaction = (LocationNeeded && SelectedLoaction != 0) ? SelectedLoaction : 0 }; @@ -161,7 +165,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable plugin { IsStartSimulationAvailable = SelectedStyle != null && (!StationNeeded || !string.IsNullOrWhiteSpace(SelectedStationName)) && - (!TicketCheckNeeded || !string.IsNullOrWhiteSpace(SelectedTicketCheck)) && + (!TicketCheckNeeded || SelectedTicketCheck is not null) && (!LocationNeeded || SelectedLoaction != 0) && (!PlatformNeeded || !string.IsNullOrWhiteSpace(SelectedPlatformName)); } diff --git a/CRSim/ViewModels/SettingsPageViewModel.cs b/CRSim/ViewModels/SettingsPageViewModel.cs index f38cc23..c43928f 100644 --- a/CRSim/ViewModels/SettingsPageViewModel.cs +++ b/CRSim/ViewModels/SettingsPageViewModel.cs @@ -113,14 +113,14 @@ namespace CRSim.ViewModels [RelayCommand] public async Task ExportData() { - string? path = await _dialogService.SaveFileAsync(".json", "data"); + string? path = _dialogService.SaveFile(".json", "data"); if (string.IsNullOrWhiteSpace(path)) return; await _databaseService.ExportData(path); } [RelayCommand] public async Task ImportData() { - string? path = await _dialogService.GetFileAsync([".json"]); + string? path = _dialogService.GetFile([".json"]); if (string.IsNullOrWhiteSpace(path)) return; _databaseService.ImportData(path); await _databaseService.SaveData(); diff --git a/CRSim/ViewModels/StationManagementPageViewModel.cs b/CRSim/ViewModels/StationManagementPageViewModel.cs index d9a9517..0878a31 100644 --- a/CRSim/ViewModels/StationManagementPageViewModel.cs +++ b/CRSim/ViewModels/StationManagementPageViewModel.cs @@ -1,4 +1,4 @@ -using CRSim.Core.Abstractions; +using System.Text.RegularExpressions; namespace CRSim.ViewModels; @@ -18,10 +18,9 @@ public partial class StationManagementPageViewModel : ObservableObject [ObservableProperty] public partial Station SelectedStation { get; set; } = new(); - public ObservableCollection WaitingAreaNames { get; private set; } = []; - public ObservableCollection Platforms { get; private set; } = []; + public ObservableCollection WaitingAreas { get; private set; } = []; - public ObservableCollection TicketChecks { get; private set; } = []; + public ObservableCollection Platforms { get; private set; } = []; public ObservableCollection TrainStops { get; private set; } = []; @@ -53,13 +52,13 @@ public partial class StationManagementPageViewModel : ObservableObject { IconGlyph = "\uECA5", Title = "候车室数", - Detail = WaitingAreaNames.Count.ToString() + Detail = WaitingAreas.Count.ToString() }); InfoItems.Add(new InfoItem() { IconGlyph = "\uF161", Title = "检票口数", - Detail = TicketChecks.Count.ToString() + Detail = WaitingAreas.SelectMany(x=>x.TicketChecks).Count().ToString() }); InfoItems.Add(new InfoItem() { @@ -69,7 +68,7 @@ public partial class StationManagementPageViewModel : ObservableObject }); } -#region Station + #region Station public void RefreshStations() { var stationsList = _databaseService.GetAllStations(); @@ -78,20 +77,15 @@ public partial class StationManagementPageViewModel : ObservableObject [RelayCommand] public void StationSelected(object args) { - WaitingAreaNames.Clear(); + WaitingAreas.Clear(); Platforms.Clear(); - TicketChecks.Clear(); TrainStops.Clear(); if (args is SelectionChangedEventArgs selectedStation && selectedStation.AddedItems.Count > 0) { SelectedStation = _databaseService.GetStationByName(selectedStation.AddedItems[0].ToString()); foreach (WaitingArea waitingArea in SelectedStation.WaitingAreas) { - WaitingAreaNames.Add(waitingArea.Name); - foreach (string ticketCheck in waitingArea.TicketChecks) - { - TicketChecks.Add(new TicketCheck { WaitingAreaName = waitingArea.Name, Name = ticketCheck }); - } + WaitingAreas.Add(waitingArea); } foreach (TrainStop trainStop in SelectedStation.TrainStops) { @@ -113,7 +107,7 @@ public partial class StationManagementPageViewModel : ObservableObject [RelayCommand] public async Task AddStation() { - string? station = await _dialogService.GetInputAsync("请输入车站名称"); + string? station = await _dialogService.GetInputAsync("请输入车站名称",string.Empty); if (station != null) { if (StationNames.Contains(station)) @@ -147,29 +141,21 @@ public partial class StationManagementPageViewModel : ObservableObject await _databaseService.SaveData(); RefreshStations(); } - public static Station GenerateStation(string stationName, ObservableCollection waitingAreaNames, ObservableCollection ticketChecks, ObservableCollection trainStops, ObservableCollection platforms) + public static Station GenerateStation(string stationName, ObservableCollection waitingAreas, ObservableCollection trainStops, ObservableCollection platforms) { var station = new Station { Name = stationName, - WaitingAreas = [.. waitingAreaNames.Select(name => new WaitingArea { Name = name })], + WaitingAreas = [.. waitingAreas], TrainStops = [.. trainStops], Platforms = [.. platforms] }; - - // 将 TicketCheck 映射到对应的 WaitingArea - foreach (var ticketCheck in ticketChecks) - { - var waitingArea = station.WaitingAreas.FirstOrDefault(wa => wa.Name == ticketCheck.WaitingAreaName); - waitingArea?.TicketChecks.Add(ticketCheck.Name); - } - return station; } [RelayCommand] public async Task ImportFrom7D() { - var path = await _dialogService.GetFileAsync([".exe"]); + var path = _dialogService.GetFile([".exe"]); if (path == null) return; try { @@ -184,69 +170,144 @@ public partial class StationManagementPageViewModel : ObservableObject } #endregion - #region TicketCheck - [RelayCommand] - public async Task AddTicketCheck() - { - (string waitingAreaName, List ticketChecks) = await _dialogService.GetInputTicketCheckAsync([.. WaitingAreaNames]); - if (ticketChecks != null && waitingAreaName != null) - { - foreach (string ticketCheck in ticketChecks) - { - if (TicketChecks.Where(x => x.WaitingAreaName == waitingAreaName).Select(x => x.Name).Contains(ticketCheck)) - { - await _dialogService.ShowMessageAsync("添加失败", $"编号 {ticketCheck} 检票口已存在。"); - return; - } - TicketChecks.Add(new TicketCheck { WaitingAreaName = waitingAreaName, Name = ticketCheck }); - } - } - } - [RelayCommand] - public void DeleteTicketCheck(object selectedTicketCheck) - { - if (selectedTicketCheck is TicketCheck ticketCheck) - { - TicketChecks.Remove(ticketCheck); - } - } - [RelayCommand] - public async Task DeleteAllTicketCheck() - { - if (!await _dialogService.GetConfirmAsync("当前操作会删除全部检票口,是否继续?")) return; - TicketChecks.Clear(); - } - #endregion - #region WaitingArea [RelayCommand] public async Task AddWaitingArea() { - string? waitingAreaName = await _dialogService.GetInputAsync("请输入候车室名称"); + string? waitingAreaName = await _dialogService.GetInputAsync("请输入候车室名称",string.Empty); if (waitingAreaName != null) { - if (WaitingAreaNames.Contains(waitingAreaName)) + if (WaitingAreas.Any(x=>x.Name==waitingAreaName)) { await _dialogService.ShowMessageAsync("添加失败", $"名称 {waitingAreaName} 候车室已存在。"); return; } - WaitingAreaNames.Add(waitingAreaName); + WaitingAreas.Add(new WaitingArea { Name = waitingAreaName }); } } [RelayCommand] public void DeleteWaitingArea(object selectedWaitingArea) { - if (selectedWaitingArea is string n) + if (selectedWaitingArea is WaitingArea n) { - WaitingAreaNames.Remove(n); + WaitingAreas.Remove(n); } } [RelayCommand] - public async Task DeleteAllWaitingArea() + public async Task DeleteAllTicketChecks() { - if (!await _dialogService.GetConfirmAsync("当前操作会删除全部候车室,是否继续?")) return; - WaitingAreaNames.Clear(); + if (!await _dialogService.GetConfirmAsync("当前操作会删除全部候车室和检票口,是否继续?")) return; + WaitingAreas.Clear(); } + + [RelayCommand] + public async Task AddTicketCheck(object selectedWaitingArea) + { + if(selectedWaitingArea is WaitingArea waitingArea) + { + var input = await _dialogService.GetInputAsync("请输入检票口", "试试“1-3”/“1A-5B”"); + if (input == null) return; + var ticketCheckNames = GenerateTicketChecks(input); + var duplicatedNames = waitingArea.TicketChecks + .Where(x => ticketCheckNames.Contains(x.Name)) + .Select(x => x.Name); + if (duplicatedNames.Any()) + { + string duplicatedNamesStr = string.Join(", ", duplicatedNames); + await _dialogService.ShowMessageAsync("添加失败", $"检票口 {duplicatedNamesStr} 已存在。"); + return; + } + foreach (var name in ticketCheckNames) + { + waitingArea.TicketChecks.Add(new TicketCheck { Name = name }); + } + OnPropertyChanged(nameof(WaitingAreas)); + } + else + { + await _dialogService.ShowMessageAsync("错误", "请先选择候车室"); + } + } + + [RelayCommand] + public void DeleteItem(object selected) + { + if (selected is WaitingArea waitingArea) + { + WaitingAreas.Remove(waitingArea); + } + if (selected is TicketCheck ticketCheck) + { + var area = WaitingAreas.FirstOrDefault(w => w.TicketChecks.Contains(ticketCheck)); + area?.TicketChecks.Remove(ticketCheck); + } + } + + public static List GenerateTicketChecks(string input) + { + if (!input.Contains('-')) + { + return [input]; + } + + string[] parts = input.Split(['-'], 2); + if (parts.Length != 2) + { + return [input]; + } + + var startMatch = MyRegex().Match(parts[0]); + var endMatch = MyRegex().Match(parts[1]); + + if (!startMatch.Success || !endMatch.Success) + { + return [input]; + } + + int startNum = int.Parse(startMatch.Groups[1].Value); + string sLetter = startMatch.Groups[2].Value; + int endNum = int.Parse(endMatch.Groups[1].Value); + string eLetter = endMatch.Groups[2].Value; + + bool startHasLetter = !string.IsNullOrEmpty(sLetter); + bool endHasLetter = !string.IsNullOrEmpty(eLetter); + + if (startHasLetter != endHasLetter) + { + return [input]; + } + + int min = Math.Min(startNum, endNum); + int max = Math.Max(startNum, endNum); + List numbers = [.. Enumerable.Range(min, max - min + 1)]; + + List result = []; + + if (startHasLetter) + { + if (sLetter == eLetter) + { + numbers.ForEach(n => result.Add($"{n}{sLetter}")); + } + else + { + numbers.ForEach(n => + { + result.Add($"{n}A"); + result.Add($"{n}B"); + }); + } + } + else + { + numbers.ForEach(n => result.Add(n.ToString())); + } + return result; + } + + [GeneratedRegex(@"^(\d+)([AB]?)$")] + private static partial Regex MyRegex(); + #endregion #region Platform @@ -308,7 +369,7 @@ public partial class StationManagementPageViewModel : ObservableObject public async Task ImportFromLulutong() { if (!await CheckCanImport()) return; - var path = await _dialogService.GetFileAsync([".csv"]); + var path = _dialogService.GetFile([".csv"]); if (path == null) return; try { @@ -364,7 +425,7 @@ public partial class StationManagementPageViewModel : ObservableObject { if (!await CheckCanImport()) return; - var path = await _dialogService.GetFileAsync([".pyetgr",".pyetdb",".json"]); + var path = _dialogService.GetFile([".pyetgr",".pyetdb",".json"]); if (path == null) return; try { @@ -450,7 +511,7 @@ public partial class StationManagementPageViewModel : ObservableObject return; } } - var path = await _dialogService.GetFileAsync([".xlsx"]); + var path = _dialogService.GetFile([".xlsx"]); if (path == null) return; try { @@ -465,9 +526,8 @@ public partial class StationManagementPageViewModel : ObservableObject worksheet.Cells[row, 2].Text.Trim() == "" || worksheet.Cells[row, 5].Text.Trim() == "" || worksheet.Cells[row, 6].Text.Trim() == "" || - worksheet.Cells[row, 7].Text.Trim() == "" || - worksheet.Cells[row, 9].Text.Trim() == "" || - worksheet.Cells[row, 10].Text.Trim() == "") + worksheet.Cells[row, 8].Text.Trim() == "" || + worksheet.Cells[row, 9].Text.Trim() == "") { continue; } @@ -478,12 +538,42 @@ public partial class StationManagementPageViewModel : ObservableObject Length = int.TryParse(worksheet.Cells[row, 2].Text, out int length) ? length : 0, ArrivalTime = TimeSpan.TryParseExact(worksheet.Cells[row, 3].Text, @"hh\:mm", null, out TimeSpan arrival) ? arrival : null, DepartureTime = TimeSpan.TryParseExact(worksheet.Cells[row, 4].Text, @"hh\:mm", null, out TimeSpan departure) ? departure : null, - WaitingArea = worksheet.Cells[row, 5].Text.Trim(), - TicketChecks = [.. worksheet.Cells[row, 6].Text.Split(' ', StringSplitOptions.RemoveEmptyEntries)], - Platform = worksheet.Cells[row, 7].Text.Trim(), - Landmark = worksheet.Cells[row, 8].Text.Trim() == "" ? "无" : worksheet.Cells[row, 8].Text.Trim(), - Origin = worksheet.Cells[row, 9].Text.Trim(), - Terminal = worksheet.Cells[row, 10].Text.Trim() + TicketCheckIds = [.. worksheet.Cells[row, 5].Text + .Split(' ', StringSplitOptions.RemoveEmptyEntries) + .Select(entry => + { + var parts = entry.Split('|'); + if (parts.Length < 2) + return Guid.Empty; + var waitingAreaName = parts[0]; + var ticketCheckName = parts[1]; + var waitingArea = WaitingAreas.FirstOrDefault(w => w.Name == waitingAreaName); + TicketCheck tc; + if (waitingArea == null) + { + tc = new TicketCheck { Name = ticketCheckName }; + WaitingAreas.Add(new WaitingArea + { + Name = waitingAreaName, + TicketChecks = [tc] + }); + } + else + { + tc = waitingArea.TicketChecks.FirstOrDefault(c => c.Name == ticketCheckName); + if (tc == null) + { + tc = new TicketCheck { Name = ticketCheckName }; + waitingArea.TicketChecks.Add(tc); + } + } + return tc.Id; + }) + .Where(id => id != Guid.Empty)], + Platform = worksheet.Cells[row, 6].Text.Trim(), + Landmark = worksheet.Cells[row, 7].Text.Trim() == "" ? "无" : worksheet.Cells[row, 8].Text.Trim(), + Origin = worksheet.Cells[row, 8].Text.Trim(), + Terminal = worksheet.Cells[row, 9].Text.Trim() }); } } @@ -495,7 +585,7 @@ public partial class StationManagementPageViewModel : ObservableObject [RelayCommand] public async Task ExportToExcel() { - var path = await _dialogService.SaveFileAsync(".xlsx", "data"); + var path = _dialogService.SaveFile(".xlsx", "data"); if (path == null) return; ExcelPackage.License.SetNonCommercialPersonal("CRSim"); using var package = new ExcelPackage(); @@ -504,31 +594,39 @@ public partial class StationManagementPageViewModel : ObservableObject worksheet.Cells[1, 2].Value = "长度"; worksheet.Cells[1, 3].Value = "到时"; worksheet.Cells[1, 4].Value = "发时"; - worksheet.Cells[1, 5].Value = "候车区"; - worksheet.Cells[1, 6].Value = "检票口"; - worksheet.Cells[1, 7].Value = "站台"; - worksheet.Cells[1, 8].Value = "地标"; - worksheet.Cells[1, 9].Value = "始发站"; - worksheet.Cells[1, 10].Value = "终到站"; + worksheet.Cells[1, 5].Value = "检票口"; + worksheet.Cells[1, 6].Value = "站台"; + worksheet.Cells[1, 7].Value = "地标"; + worksheet.Cells[1, 8].Value = "始发站"; + worksheet.Cells[1, 9].Value = "终到站"; for (int i = 0; i < TrainStops.Count; i++) { worksheet.Cells[i + 2, 1].Value = TrainStops[i].Number; worksheet.Cells[i + 2, 2].Value = TrainStops[i].Length; worksheet.Cells[i + 2, 3].Value = TrainStops[i].ArrivalTime?.ToString(@"hh\:mm") ?? string.Empty; worksheet.Cells[i + 2, 4].Value = TrainStops[i].DepartureTime?.ToString(@"hh\:mm") ?? string.Empty; - worksheet.Cells[i + 2, 5].Value = TrainStops[i].WaitingArea; - worksheet.Cells[i + 2, 6].Value = TrainStops[i].TicketChecks.Count == 0 ? string.Empty : string.Join(" ", TrainStops[i].TicketChecks); - worksheet.Cells[i + 2, 7].Value = TrainStops[i].Platform; - worksheet.Cells[i + 2, 8].Value = TrainStops[i].Landmark; - worksheet.Cells[i + 2, 9].Value = TrainStops[i].Origin; - worksheet.Cells[i + 2, 10].Value = TrainStops[i].Terminal; + worksheet.Cells[i + 2, 5].Value = TrainStops[i].TicketCheckIds is null ? string.Empty : string.Join(" ", TrainStops[i].TicketCheckIds + .Select(id => + { + var area = WaitingAreas.FirstOrDefault(a => a.TicketChecks.Any(tc => tc.Id == id)); + var check = area?.TicketChecks.FirstOrDefault(tc => tc.Id == id); + if (area != null && check != null) + return $"{area.Name}|{check.Name}"; + else + return null; + }) + .Where(x => x != null)); + worksheet.Cells[i + 2, 6].Value = TrainStops[i].Platform; + worksheet.Cells[i + 2, 7].Value = TrainStops[i].Landmark; + worksheet.Cells[i + 2, 8].Value = TrainStops[i].Origin; + worksheet.Cells[i + 2, 9].Value = TrainStops[i].Terminal; } await package.SaveAsAsync(new FileInfo(path)); } [RelayCommand] public async Task AddTrainStop() { - var newTrainStop = await _dialogService.GetInputTrainStopAsync([.. TicketChecks.Select(x => $"{x.WaitingAreaName} - {x.Name}")], [.. Platforms.Select(x => x.Name)]); + var newTrainStop = await _dialogService.GetInputTrainStopAsync([.. WaitingAreas], [.. Platforms.Select(x => x.Name)]); if (newTrainStop != null) { if (TrainStops.Any(x => x.Number == newTrainStop.Number)) @@ -558,7 +656,7 @@ public partial class StationManagementPageViewModel : ObservableObject { if (_selectedTrainStop is TrainStop selectedTrainStop) { - var newTrainStop = await _dialogService.EditInputTrainStopAsync([.. TicketChecks.Select(x => $"{x.WaitingAreaName} - {x.Name}")], [.. Platforms.Select(x => x.Name)], selectedTrainStop); + var newTrainStop = await _dialogService.EditInputTrainStopAsync([.. WaitingAreas], [.. Platforms.Select(x => x.Name)], selectedTrainStop); if (newTrainStop != null) { if (TrainStops.Where(x => x.Number == newTrainStop.Number).Count() > 1) @@ -590,7 +688,7 @@ public partial class StationManagementPageViewModel : ObservableObject } private async Task CheckCanImport() { - if (TicketChecks.Count == 0) + if (WaitingAreas.All(x => x.TicketChecks.Count == 0)) { await _dialogService.ShowMessageAsync("导入失败", "请先添加检票口。"); return false; @@ -616,9 +714,19 @@ public partial class StationManagementPageViewModel : ObservableObject t.Platform = Platforms[new Random().Next(Platforms.Count)].Name; if (t.DepartureTime.HasValue) { - t.TicketChecks = [.. TicketChecks.Where(x => x.Name == t.Platform + "A" || x.Name == t.Platform + "B").Select(x => x.Name)]; - if (t.TicketChecks.Count == 0) t.TicketChecks = [TicketChecks[new Random().Next(TicketChecks.Count)].Name]; - t.WaitingArea = TicketChecks.Where(x => x.Name == t.TicketChecks[0]).ToList()[new Random().Next(TicketChecks.Where(x => x.Name == t.TicketChecks[0]).ToList().Count)].WaitingAreaName; + //t.TicketCheckIds = [.. WaitingAreas + // .FirstOrDefault(x => x.TicketChecks.Any(x => x.Name == t.Platform + "A") + // && x.TicketChecks.Any(x => x.Name == t.Platform + "B"))? + // .TicketChecks + // .Where(x => x.Name == t.Platform + "A" || x.Name == t.Platform + "B") + // .Select(x => x.Id)]; + //根据站台选择检票口 + if (t.TicketCheckIds is null || t.TicketCheckIds.Count == 0) t.TicketCheckIds = [WaitingAreas + .SelectMany(x => x.TicketChecks) + .Select(x => x.Id) + .OrderBy(_ => new Random().Next()) + .FirstOrDefault()]; + //若没有则随机匹配 } return t; } @@ -628,7 +736,7 @@ public partial class StationManagementPageViewModel : ObservableObject public async Task SaveChanges() { if (!await Validate()) return; - _databaseService.UpdateStation(SelectedStation.Name, GenerateStation(SelectedStation.Name, WaitingAreaNames, TicketChecks, TrainStops, Platforms)); + _databaseService.UpdateStation(SelectedStation.Name, GenerateStation(SelectedStation.Name, WaitingAreas, TrainStops, Platforms)); await _databaseService.SaveData(); UpdateInfoItems(); } @@ -636,6 +744,7 @@ public partial class StationManagementPageViewModel : ObservableObject private async Task Validate() { string warningMessage = ""; + string errorMessage = ""; var platformGroups = TrainStops .Where(ts => !string.IsNullOrEmpty(ts.Platform) && ts.DepartureTime != null && ts.ArrivalTime != null) .GroupBy(ts => ts.Platform); @@ -655,30 +764,17 @@ public partial class StationManagementPageViewModel : ObservableObject } } - string errorMessage = ""; - foreach (var t in TicketChecks) - { - if (!WaitingAreaNames.Contains(t.WaitingAreaName)) - { - errorMessage += $"\n检票口 {t.Name} 所在的候车室 {t.WaitingAreaName} 不存在;"; - } - } foreach (var s in TrainStops) { if (s.DepartureTime.HasValue) { - if (!WaitingAreaNames.Contains(s.WaitingArea)) + List allTicketChecks = [.. WaitingAreas.SelectMany(x => x.TicketChecks.Select(x=>x.Id))]; + foreach (var ticketCheckId in s.TicketCheckIds) { - errorMessage += $"\n车次 {s.Number} 所分配的候车区 {s.WaitingArea} 不存在;"; - } - else - { - foreach (var t in s.TicketChecks) + if (!allTicketChecks.Contains(ticketCheckId)) { - if (!TicketChecks.Any(x => x.Name == t && x.WaitingAreaName == s.WaitingArea)) - { - errorMessage += $"\n车次 {s.Number} 所分配的检票口 {t} 在候车区 {s.WaitingArea} 中不存在;"; - } + warningMessage += $"\n车次 {s.Number} 所分配的检票口 {ticketCheckId} 不存在,已自动删除;"; + s.TicketCheckIds.Remove(ticketCheckId);//感觉这个代码放在这个位置不合适,但我也不知放在哪里好QAQ } } } diff --git a/CRSim/Views/DialogContents/InputDialog.xaml b/CRSim/Views/DialogContents/InputDialog.xaml index f189073..a298cd9 100644 --- a/CRSim/Views/DialogContents/InputDialog.xaml +++ b/CRSim/Views/DialogContents/InputDialog.xaml @@ -8,6 +8,6 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - + diff --git a/CRSim/Views/DialogContents/InputDialog.xaml.cs b/CRSim/Views/DialogContents/InputDialog.xaml.cs index f0ec009..d5de69f 100644 --- a/CRSim/Views/DialogContents/InputDialog.xaml.cs +++ b/CRSim/Views/DialogContents/InputDialog.xaml.cs @@ -2,8 +2,10 @@ namespace CRSim.Views; public sealed partial class InputDialog : Page { public string InputText { get; set; } - public InputDialog() + public string Placeholder { get; set; } + public InputDialog(string placeholder) { + Placeholder = placeholder; InitializeComponent(); } } diff --git a/CRSim/Views/DialogContents/TicketCheckDialog.xaml b/CRSim/Views/DialogContents/TicketCheckDialog.xaml deleted file mode 100644 index a1f593f..0000000 --- a/CRSim/Views/DialogContents/TicketCheckDialog.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - diff --git a/CRSim/Views/DialogContents/TicketCheckDialog.xaml.cs b/CRSim/Views/DialogContents/TicketCheckDialog.xaml.cs deleted file mode 100644 index f01d422..0000000 --- a/CRSim/Views/DialogContents/TicketCheckDialog.xaml.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Text.RegularExpressions; - -namespace CRSim.Views; -public sealed partial class TicketCheckDialog : Page -{ - private readonly Action _onValidityChanged; - public List GeneratedTicketChecks = []; - public string SelectedWaitingArea => WaitingAreasComboBox.SelectedValue.ToString(); - private List WaitingAreaNames { get; set; } = []; - private void Validate(object sender, object e) - { - bool isValid = !string.IsNullOrWhiteSpace(TicketCheckTextBox.Text) && !string.IsNullOrWhiteSpace(WaitingAreasComboBox.SelectedValue?.ToString()); - _onValidityChanged?.Invoke(isValid); - } - public TicketCheckDialog(List waitingAreaNames, Action onValidityChanged) - { - _onValidityChanged = onValidityChanged; - WaitingAreaNames = waitingAreaNames; - InitializeComponent(); - } - public void GenerateTicketChecks() - { - var input = TicketCheckTextBox.Text.Trim(); - if (!input.Contains('-')) - { - GeneratedTicketChecks = [input]; - return; - } - - string[] parts = input.Split(['-'], 2); - if (parts.Length != 2) - { - GeneratedTicketChecks = [input]; - return; - } - - var startMatch = MyRegex().Match(parts[0]); - var endMatch = MyRegex().Match(parts[1]); - - if (!startMatch.Success || !endMatch.Success) - { - GeneratedTicketChecks = [input]; - return; - } - - int startNum = int.Parse(startMatch.Groups[1].Value); - string sLetter = startMatch.Groups[2].Value; - int endNum = int.Parse(endMatch.Groups[1].Value); - string eLetter = endMatch.Groups[2].Value; - - bool startHasLetter = !string.IsNullOrEmpty(sLetter); - bool endHasLetter = !string.IsNullOrEmpty(eLetter); - - if (startHasLetter != endHasLetter) - { - GeneratedTicketChecks = [input]; - } - - int min = Math.Min(startNum, endNum); - int max = Math.Max(startNum, endNum); - List numbers = [.. Enumerable.Range(min, max - min + 1)]; - - List result = []; - - if (startHasLetter) - { - if (sLetter == eLetter) - { - numbers.ForEach(n => result.Add($"{n}{sLetter}")); - } - else - { - numbers.ForEach(n => - { - result.Add($"{n}A"); - result.Add($"{n}B"); - }); - } - } - else - { - numbers.ForEach(n => result.Add(n.ToString())); - } - GeneratedTicketChecks = result; - return; - } - - [GeneratedRegex(@"^(\d+)([AB]?)$")] - private static partial Regex MyRegex(); -} diff --git a/CRSim/Views/DialogContents/TrainStopDialog.xaml b/CRSim/Views/DialogContents/TrainStopDialog.xaml index 24db57f..c771c82 100644 --- a/CRSim/Views/DialogContents/TrainStopDialog.xaml +++ b/CRSim/Views/DialogContents/TrainStopDialog.xaml @@ -6,12 +6,25 @@ xmlns:local="using:CRSim.Views" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:models="using:CRSim.Models" + xmlns:coremodels="using:CRSim.Core.Models" + xmlns:selectors="using:CRSim.Selectors" mc:Ignorable="d"> + + + + + + + + + - + @@ -57,13 +70,8 @@ - - - - - - - + diff --git a/CRSim/Views/DialogContents/TrainStopDialog.xaml.cs b/CRSim/Views/DialogContents/TrainStopDialog.xaml.cs index d196eea..3a1c330 100644 --- a/CRSim/Views/DialogContents/TrainStopDialog.xaml.cs +++ b/CRSim/Views/DialogContents/TrainStopDialog.xaml.cs @@ -1,53 +1,38 @@ -using Microsoft.UI.Xaml.Controls.Primitives; +using CRSim.Core.Models; namespace CRSim.Views; public sealed partial class TrainStopDialog : Page { public TrainStop GeneratedTrainStop; + public List Landmarks { get; set; } = ["", "ɫ", "ɫ", "ɫ", "ɫ", "ɫ", "ɫ", "ɫ"]; + public List Platforms { get; set; } = []; - public ObservableCollection TicketChecksList { get; set; } = []; + + public List WaitingAreas { get; set; } + private readonly Action _onValidityChanged; - public TrainStopDialog(List ticketChecks, List platforms, Action onValidityChanged) + + public TrainStopDialog(List waitingAreas, List platforms, Action onValidityChanged) { - InitializeComponent(); - _onValidityChanged = onValidityChanged; - TicketChecksList.Clear(); - foreach (string s in ticketChecks) - { - var checkItem = new CheckListItem() - { - Name = s, - IsSelected = false, - }; - checkItem.PropertyChanged += Validate; - TicketChecksList.Add(checkItem); - } + WaitingAreas = waitingAreas; foreach (string s in platforms) { Platforms.Add(s); } + InitializeComponent(); + _onValidityChanged = onValidityChanged; Validate(null, null); } - public TrainStopDialog(List ticketChecks, List platforms,TrainStop trainStop, Action onValidityChanged) + public TrainStopDialog(List waitingAreas, List platforms, TrainStop trainStop, Action onValidityChanged) { - InitializeComponent(); - _onValidityChanged = onValidityChanged; - TicketChecksList.Clear(); - foreach (string s in ticketChecks) - { - var checkItem = new CheckListItem() - { - Name = s, - IsSelected = false, - }; - checkItem.PropertyChanged += Validate; - TicketChecksList.Add(checkItem); - } + WaitingAreas = waitingAreas; foreach (string s in platforms) { Platforms.Add(s); } + InitializeComponent(); + _onValidityChanged = onValidityChanged; NumberTextBox.Text = trainStop.Number; LengthTextBox.Text = trainStop.Length.ToString(); ArrivalTextBox.Text = trainStop.Terminal; @@ -76,13 +61,14 @@ public sealed partial class TrainStopDialog : Page EndMinute.IsEnabled = false; StationKindPanelRadioButtons.SelectedIndex = 2; } - if(trainStop.TicketChecks != null && trainStop.TicketChecks.Count > 0) + if (trainStop.TicketCheckIds != null) { - foreach (string s in trainStop.TicketChecks) + foreach (var id in trainStop.TicketCheckIds) { - TicketChecksList.FirstOrDefault(x => x.Name == trainStop.WaitingArea + " - " + s)!.IsSelected = true; + //WaitingAreasList.SelectedItems.Add(waitingAreas.SelectMany(x => x.TicketChecks).FirstOrDefault(x => x.Id == id)); } } + Validate(null, null); } private static bool ValidateTime(string input, int maxValue) @@ -101,8 +87,8 @@ public sealed partial class TrainStopDialog : Page (!EndHour.IsEnabled || ValidateTime(EndHour.Text, 24)) && (!EndMinute.IsEnabled || ValidateTime(EndMinute.Text, 60)); bool isValid = - (!TicketChecksCheckList.IsEnabled || - (TicketChecksList.Any(x => x.IsSelected) && TicketChecksList.Where(x => x.IsSelected).Select(x => x.Name.Split(" - ")[0]).Distinct().Count() <= 1)) && + (!WaitingAreasList.IsEnabled || + WaitingAreasList.SelectedItems?.Count != 0) && areTextBoxesFilled && !string.IsNullOrWhiteSpace(NumberTextBox.Text) && !string.IsNullOrWhiteSpace(ArrivalTextBox.Text) && @@ -138,8 +124,9 @@ public sealed partial class TrainStopDialog : Page Origin = DepartureTextBox.Text, ArrivalTime = StartHour.IsEnabled ? TimeSpan.Parse($"{StartHour.Text}:{StartMinute.Text}") : null, DepartureTime = EndHour.IsEnabled ? TimeSpan.Parse($"{EndHour.Text}:{EndMinute.Text}") : null, - WaitingArea = EndHour.IsEnabled ? TicketChecksList.Where(x => x.IsSelected).FirstOrDefault().Name.Split(" - ")[0] : null, - TicketChecks = EndHour.IsEnabled ? [.. TicketChecksList.Where(x => x.IsSelected).Select(x => x.Name.Split(" - ")[1])] : null, + TicketCheckIds = EndHour.IsEnabled ? [.. WaitingAreasList.SelectedItems + .OfType() + .Select(x => x.Id)] : null, Platform = (string)PlatformComboBox.SelectedItem, Length = int.Parse(LengthTextBox.Text), Landmark = (string)LandmarkComboBox.SelectedItem == "" ? null : (string)LandmarkComboBox.SelectedItem, @@ -159,21 +146,21 @@ public sealed partial class TrainStopDialog : Page StartMinute.IsEnabled = false; EndHour.IsEnabled = true; EndMinute.IsEnabled = true; - TicketChecksCheckList.IsEnabled = true; + WaitingAreasList.IsEnabled = true; break; case "мվ": StartHour.IsEnabled = true; StartMinute.IsEnabled = true; EndHour.IsEnabled = true; EndMinute.IsEnabled = true; - TicketChecksCheckList.IsEnabled = true; + WaitingAreasList.IsEnabled = true; break; case "յվ": StartHour.IsEnabled = true; StartMinute.IsEnabled = true; EndHour.IsEnabled = false; EndMinute.IsEnabled = false; - TicketChecksCheckList.IsEnabled = false; + WaitingAreasList.IsEnabled = false; break; } Validate(null, null); diff --git a/CRSim/Views/ScreenSimulatorPage.xaml b/CRSim/Views/ScreenSimulatorPage.xaml index e5b62a0..9e8caf2 100644 --- a/CRSim/Views/ScreenSimulatorPage.xaml +++ b/CRSim/Views/ScreenSimulatorPage.xaml @@ -130,7 +130,7 @@ - + diff --git a/CRSim/Views/StartWindow.xaml.cs b/CRSim/Views/StartWindow.xaml.cs index 7a27a3f..4414cb5 100644 --- a/CRSim/Views/StartWindow.xaml.cs +++ b/CRSim/Views/StartWindow.xaml.cs @@ -2,13 +2,9 @@ using System.ComponentModel; namespace CRSim.Views { - // ¼ί - public delegate void SplashScreenClosedEventHandler(object sender, EventArgs e); - public sealed partial class StartWindow : Window, INotifyPropertyChanged { // ¼ - public event SplashScreenClosedEventHandler SplashScreenClosed; public string AppVersion { get; set; } = App.AppVersion; private string _status = "..."; @@ -24,7 +20,6 @@ namespace CRSim.Views } } } - public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged(string propertyName) @@ -60,12 +55,5 @@ namespace CRSim.Views } - - // ɳʼر - public void CompleteInitialization() - { - SplashScreenClosed?.Invoke(this, EventArgs.Empty); - this.Close(); - } } } diff --git a/CRSim/Views/StationManagementPage.xaml b/CRSim/Views/StationManagementPage.xaml index 9a77ba0..d5239ff 100644 --- a/CRSim/Views/StationManagementPage.xaml +++ b/CRSim/Views/StationManagementPage.xaml @@ -13,12 +13,23 @@ xmlns:viewmodels="using:CRSim.ViewModels" xmlns:models="using:CRSim.Models" xmlns:coremodels="using:CRSim.Core.Models" + xmlns:selectors="using:CRSim.Selectors" mc:Ignorable="d" Loaded="Page_Loaded" d:DataContext="{d:DesignInstance Type=viewmodels:StationManagementPageViewModel}"> - + + + + + + + + @@ -81,52 +92,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -164,6 +129,17 @@ + + + + + + + + + + @@ -195,18 +171,16 @@ - - - - - - - + + + + + @@ -222,18 +196,16 @@ - - - - - - - + + + + + diff --git a/Directory.Build.props b/Directory.Build.props index eb6a662..49591e6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 2.3.107.0 + 2.3.108.0 电排骨 https://github.com/denglihong2007/CRSim