refactor: #141 #202 重构检票口/候车室数据结构

This commit is contained in:
denglihong2007
2025-08-10 16:46:36 +08:00
parent 9143d6b1ff
commit 9ad7ea432a
33 changed files with 450 additions and 533 deletions

View File

@@ -13,8 +13,8 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Downloader" Version="4.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageReference Include="Downloader" Version="4.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.8" />
</ItemGroup>
</Project>

View File

@@ -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;
}
}
}

View File

@@ -15,9 +15,7 @@ namespace CRSim.Core.Models
[JsonConverter(typeof(TimeSpanJsonConverter))]
public TimeSpan? DepartureTime { get; set; }
public List<string>? TicketChecks { get; set; }
public string? WaitingArea { get; set; }
public List<Guid>? TicketCheckIds { get; set; }
public string? Origin { get; set; }

View File

@@ -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<string> TicketChecks { get; set; } = [];
public ObservableCollection<TicketCheck> TicketChecks { get; set; } = [];
}
}

View File

@@ -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>(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,

View File

@@ -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; }

View File

@@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
<PackageReference Include="PinYinConverterCore" Version="1.0.2" />
<PackageReference Include="PP.Wpf" Version="1.0.8" />
</ItemGroup>

View File

@@ -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; }
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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<StartWindow>();
splashScreenWindow.SplashScreenClosed += OnSplashScreenClosed;
var splashScreenWindow = AppHost.Services.GetRequiredService<StartWindow>();
splashScreenWindow.Activate();
_ = PerformInitializationAsync(splashScreenWindow);
await PerformInitializationAsync(splashScreenWindow);
MainWindow = AppHost.Services.GetRequiredService<MainWindow>();
MainWindow.Activate();
splashScreenWindow.Close();
}
private static async Task PerformInitializationAsync(StartWindow splashScreen)
private static async Task PerformInitializationAsync(StartWindow startWindow)
{
try
{
var startWindow = AppHost.Services.GetService<StartWindow>();
await Task.Delay(2000);
if(!Debugger.IsAttached) await Task.Delay(2000);
startWindow.Status = "正在加载设置...";
AppHost.Services.GetService<ISettingsService>().LoadSettings();
await Task.Delay(500);
if (!Debugger.IsAttached) await Task.Delay(500);
startWindow.Status = "正在加载数据库...";
AppHost.Services.GetService<IDatabaseService>().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>();
mainWindow.Activate();
MainWindow = mainWindow;
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]

View File

@@ -55,8 +55,8 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
<PackageReference Include="EPPlus" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
<PackageReference Include="Microsoft.UI.Xaml" Version="2.8.7" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4654" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />

View File

@@ -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<string> ticketChecks)
{
// 使用自定义的 Separator 将字符串连接
return string.Join(Separator, ticketChecks);
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
// 通常不需要实现 ConvertBack
return null;
}
}
}

View File

@@ -0,0 +1,27 @@
using Microsoft.UI.Xaml.Data;
namespace CRSim.Converters
{
public partial class TicketChecksToStringConverter : IValueConverter
{
public string Separator { get; set; } = "";
public ObservableCollection<WaitingArea> WaitingAreas { get; set; } = [];
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is List<Guid> 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();
}
}
}

View File

@@ -26,7 +26,7 @@
<NavigationView Grid.Row="1" x:Name="nvSample" SelectionChanged="nvSample_SelectionChanged"
IsBackButtonVisible="Collapsed" IsBackEnabled="False">
<NavigationView.MenuItems>
<NavigationViewItem Icon="Home" Content="主页" Tag="DashboardPage" />
<NavigationViewItem Icon="Home" Content="主页" Tag="DashboardPage" IsSelected="True"/>
<NavigationViewItem Content="插件" Tag="PluginManagementPage">
<NavigationViewItem.Icon>
<FontIcon Glyph="&#xEA86;" />

View File

@@ -1,9 +0,0 @@
namespace CRSim.Models
{
public partial class CheckListItem : ObservableObject
{
[ObservableProperty]
public partial bool IsSelected { get; set; }
public string Name { get; set; }
}
}

View File

@@ -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)
};
}
}
}

View File

@@ -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<string?> GetInputAsync(string title)
public async Task<string?> 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<string>)> GetInputTicketCheckAsync(List<string> 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<string?> 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<TrainStop?> GetInputTrainStopAsync(List<string> ticketChecks, List<string> platforms)
public async Task<TrainStop?> GetInputTrainStopAsync(List<WaitingArea> waitingAreas, List<string> 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<TrainStop?> EditInputTrainStopAsync(List<string> ticketChecks, List<string> platforms, TrainStop trainStop)
public async Task<TrainStop?> EditInputTrainStopAsync(List<WaitingArea> waitingAreas, List<string> 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<string?> 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<List<Platform>?> GetInputPlatformAsync()

View File

@@ -2,17 +2,16 @@
{
public interface IDialogService
{
Task<string?> GetFileAsync(string[] filter);
Task<string?> SaveFileAsync(string filter,string fileName);
Task<string?> GetInputAsync(string title);
string? GetFile(string[] filter);
string? SaveFile(string filter,string fileName);
Task<string?> GetInputAsync(string title,string placeholder);
Task<bool> GetConfirmAsync(string title);
Task ShowMessageAsync(string title, string message);
Task ShowTextAsync(string title, string message);
TrainStop? GetInputTrainNumberStop(TrainStop? t);
Task<TrainStop?> GetInputTrainStopAsync(List<string> ticketChecks, List<string> platforms);
Task<TrainStop?> EditInputTrainStopAsync(List<string> ticketChecks, List<string> platforms, TrainStop trainStop);
Task<TrainStop?> GetInputTrainStopAsync(List<WaitingArea> waitingAreas, List<string> platforms);
Task<TrainStop?> EditInputTrainStopAsync(List<WaitingArea> waitingAreas, List<string> platforms, TrainStop trainStop);
Task<List<Platform>?> GetInputPlatformAsync();
Task<(string, List<string>)> GetInputTicketCheckAsync(List<string> waitingAreaNames);
XamlRoot? XamlRoot { get; set; }
}
}

View File

@@ -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
{

View File

@@ -29,7 +29,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable<PluginBase> plugin
[ObservableProperty]
public partial bool LocationNeeded { get; set; }
public List<string> StationNames => [.. databaseService.GetAllStations().Select(x => x.Name)];
public ObservableCollection<string> TicketChecks { get; private set; } = [];
public ObservableCollection<TicketCheck> TicketChecks { get; private set; } = [];
public ObservableCollection<Platform> Platforms { get; private set; } = [];
public ObservableCollection<int> Locations { get; private set; } = [];
@@ -41,7 +41,7 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable<PluginBase> plugin
public string SelectedStationName = "";
public string SelectedTicketCheck = "";
public TicketCheck? SelectedTicketCheck;
public string SelectedPlatformName = "";
@@ -79,14 +79,18 @@ public partial class ScreenSimulatorPageViewModel(IEnumerable<PluginBase> 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<PluginBase> 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<PluginBase> 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<PluginBase> 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<PluginBase> plugin
{
IsStartSimulationAvailable = SelectedStyle != null &&
(!StationNeeded || !string.IsNullOrWhiteSpace(SelectedStationName)) &&
(!TicketCheckNeeded || !string.IsNullOrWhiteSpace(SelectedTicketCheck)) &&
(!TicketCheckNeeded || SelectedTicketCheck is not null) &&
(!LocationNeeded || SelectedLoaction != 0) &&
(!PlatformNeeded || !string.IsNullOrWhiteSpace(SelectedPlatformName));
}

View File

@@ -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();

View File

@@ -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<string> WaitingAreaNames { get; private set; } = [];
public ObservableCollection<Platform> Platforms { get; private set; } = [];
public ObservableCollection<WaitingArea> WaitingAreas { get; private set; } = [];
public ObservableCollection<TicketCheck> TicketChecks { get; private set; } = [];
public ObservableCollection<Platform> Platforms { get; private set; } = [];
public ObservableCollection<TrainStop> 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<string> waitingAreaNames, ObservableCollection<TicketCheck> ticketChecks, ObservableCollection<TrainStop> trainStops, ObservableCollection<Platform> platforms)
public static Station GenerateStation(string stationName, ObservableCollection<WaitingArea> waitingAreas, ObservableCollection<TrainStop> trainStops, ObservableCollection<Platform> 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<string> 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<string> 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<int> numbers = [.. Enumerable.Range(min, max - min + 1)];
List<string> 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<bool> 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<bool> 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<Guid> 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
}
}
}

View File

@@ -8,6 +8,6 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBox Text="{x:Bind InputText, Mode=TwoWay}" />
<TextBox PlaceholderText="{x:Bind Placeholder}" Text="{x:Bind InputText, Mode=TwoWay}" />
</StackPanel>
</Page>

View File

@@ -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();
}
}

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Page
x:Class="CRSim.Views.TicketCheckDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CRSim.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ComboBox ItemsSource="{x:Bind WaitingAreaNames}" Margin="0,0,0,16" x:Name="WaitingAreasComboBox"
SelectionChanged="Validate" MinWidth="100" Header="候车室"/>
<TextBox PlaceholderText="试试“1-3”/“1A-5B”" x:Name="TicketCheckTextBox" TextChanged="Validate" Header="编号"/>
</StackPanel>
</Page>

View File

@@ -1,90 +0,0 @@
using System.Text.RegularExpressions;
namespace CRSim.Views;
public sealed partial class TicketCheckDialog : Page
{
private readonly Action<bool> _onValidityChanged;
public List<string> GeneratedTicketChecks = [];
public string SelectedWaitingArea => WaitingAreasComboBox.SelectedValue.ToString();
private List<string> 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<string> waitingAreaNames, Action<bool> 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<int> numbers = [.. Enumerable.Range(min, max - min + 1)];
List<string> 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();
}

View File

@@ -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">
<Page.Resources>
<DataTemplate x:Key="WaitingAreaTemplate" x:DataType="coremodels:WaitingArea">
<TreeViewItem Content="{x:Bind Name}" ItemsSource="{x:Bind TicketChecks}" />
</DataTemplate>
<DataTemplate x:Key="TicketCheckTemplate" x:DataType="coremodels:TicketCheck">
<TextBlock Text="{x:Bind Name}" />
</DataTemplate>
<selectors:TreeViewTemplateSelector
x:Key="TreeViewTemplateSelector"
WaitingAreaTemplate="{StaticResource WaitingAreaTemplate}"
TicketCheckTemplate="{StaticResource TicketCheckTemplate}" />
</Page.Resources>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<Grid ColumnSpacing="24" RowSpacing="16">
@@ -57,13 +70,8 @@
<TextBlock Text="检票口" Margin="0,0,0,12"/>
<Border Style="{StaticResource ListViewBorder}">
<ScrollViewer VerticalScrollBarVisibility="Visible" MaxHeight="300">
<ListView x:Name="TicketChecksCheckList" ItemsSource="{x:Bind TicketChecksList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:CheckListItem">
<CheckBox Content="{x:Bind Name}" IsChecked="{x:Bind IsSelected, Mode=TwoWay}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TreeView SelectionMode="Multiple" Name="WaitingAreasList" Width="120" SelectionChanged="Validate"
ItemsSource="{x:Bind WaitingAreas, Mode=OneWay}" ItemTemplateSelector="{StaticResource TreeViewTemplateSelector}" />
</ScrollViewer>
</Border>
</StackPanel>

View File

@@ -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<string> Landmarks { get; set; } = ["无", "红色", "绿色", "褐色", "蓝色", "紫色", "黄色", "橙色"];
public List<string> Platforms { get; set; } = [];
public ObservableCollection<CheckListItem> TicketChecksList { get; set; } = [];
public List<WaitingArea> WaitingAreas { get; set; }
private readonly Action<bool> _onValidityChanged;
public TrainStopDialog(List<string> ticketChecks, List<string> platforms, Action<bool> onValidityChanged)
public TrainStopDialog(List<WaitingArea> waitingAreas, List<string> platforms, Action<bool> 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<string> ticketChecks, List<string> platforms,TrainStop trainStop, Action<bool> onValidityChanged)
public TrainStopDialog(List<WaitingArea> waitingAreas, List<string> platforms, TrainStop trainStop, Action<bool> 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<TicketCheck>()
.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);

View File

@@ -130,7 +130,7 @@
</controls:SettingsCard>
<controls:SettingsCard Header="检票口" Description="仅加载某个检票口的数据" HeaderIcon="{winui:FontIcon Glyph=&#xF161;}"
Visibility="{x:Bind ViewModel.TicketCheckNeeded,Converter={StaticResource BoolToVisibilityConverter},Mode=OneWay}">
<ComboBox ItemsSource="{x:Bind ViewModel.TicketChecks}" x:Name="TicketChecksList">
<ComboBox ItemsSource="{x:Bind ViewModel.TicketChecks}" x:Name="TicketChecksList" DisplayMemberPath="Name">
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="SelectionChanged">
<interactivity:InvokeCommandAction Command="{x:Bind ViewModel.SelectTicketCheckCommand}" CommandParameter="{x:Bind TicketChecksList.SelectedItem,Mode=OneWay}" />

View File

@@ -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();
}
}
}

View File

@@ -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}">
<Page.Resources>
<converters:TimeSpanToStringConverter x:Key="TimeSpanToStringConverter" />
<converters:ListToStringConverter x:Key="ListToStringConverter" Separator=", " />
<converters:TicketChecksToStringConverter x:Key="TicketChecksToStringConverter" Separator=" " WaitingAreas="{x:Bind ViewModel.WaitingAreas}"/>
<DataTemplate x:Key="WaitingAreaTemplate" x:DataType="coremodels:WaitingArea">
<TreeViewItem Content="{x:Bind Name}" ItemsSource="{x:Bind TicketChecks}" />
</DataTemplate>
<DataTemplate x:Key="TicketCheckTemplate" x:DataType="coremodels:TicketCheck">
<TextBlock Text="{x:Bind Name}" />
</DataTemplate>
<selectors:TreeViewTemplateSelector
x:Key="TreeViewTemplateSelector"
WaitingAreaTemplate="{StaticResource WaitingAreaTemplate}"
TicketCheckTemplate="{StaticResource TicketCheckTemplate}" />
</Page.Resources>
<Grid x:Name="ContentPagePane" Margin="36,40,0,36">
<Grid.RowDefinitions>
@@ -81,52 +92,6 @@
<controls:WrapLayout Orientation="Horizontal" VerticalSpacing="8" HorizontalSpacing="24"/>
</ItemsRepeater.Layout>
</ItemsRepeater>
<TextBlock Text="候车室" Style="{StaticResource BodyStrongTextBlockStyle}" Margin="0,24,0,0"/>
<CommandBar DefaultLabelPosition="Right" HorizontalAlignment="Right" Margin="0,12,0,0">
<AppBarButton Icon="Add" Label="添加" Command="{x:Bind ViewModel.AddWaitingAreaCommand}" />
<AppBarButton Icon="Clear" Label="删除" Command="{x:Bind ViewModel.DeleteWaitingAreaCommand}" CommandParameter="{x:Bind WaitingAreaList.SelectedItem, Mode=OneWay}"/>
<AppBarButton Icon="Delete" Label="清空" Command="{x:Bind ViewModel.DeleteAllWaitingAreaCommand}" />
</CommandBar>
<Border Style="{StaticResource ListViewBorder}" Margin="0,8,0,0">
<ListView x:Name="WaitingAreaList" ItemsSource="{x:Bind ViewModel.WaitingAreaNames}" MaxHeight="300"/>
</Border>
<TextBlock Text="检票口" Style="{StaticResource BodyStrongTextBlockStyle}" Margin="0,24,0,0"/>
<CommandBar DefaultLabelPosition="Right" HorizontalAlignment="Right" Margin="0,12,0,0">
<AppBarButton Icon="Add" Label="添加" Command="{x:Bind ViewModel.AddTicketCheckCommand}" />
<AppBarButton Icon="Clear" Label="删除" Command="{x:Bind ViewModel.DeleteTicketCheckCommand}" CommandParameter="{x:Bind TicketChecksList.SelectedItem, Mode=OneWay}" />
<AppBarButton Icon="Delete" Label="清空" Command="{x:Bind ViewModel.DeleteAllTicketCheckCommand}" />
</CommandBar>
<Border Style="{StaticResource ListViewBorder}" Margin="0,8,0,0">
<ListView x:Name="TicketChecksList" ItemsSource="{x:Bind ViewModel.TicketChecks}" MaxHeight="300">
<ListView.Header>
<Grid Padding="16,12" ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="编号" />
<TextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="候车室" />
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate x:DataType="coremodels:TicketCheck">
<Grid ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{x:Bind Name}" />
<TextBlock Grid.Column="1" Text="{x:Bind WaitingAreaName}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Border>
<TextBlock Text="站台" Style="{StaticResource BodyStrongTextBlockStyle}" Margin="0,24,0,0"/>
<CommandBar DefaultLabelPosition="Right" HorizontalAlignment="Right" Margin="0,12,0,0">
<AppBarButton Icon="Add" Label="添加" Command="{x:Bind ViewModel.AddPlatformCommand}" />
@@ -164,6 +129,17 @@
</ListView.ItemContainerStyle>
</ListView>
</Border>
<TextBlock Text="检票口" Style="{StaticResource BodyStrongTextBlockStyle}" Margin="0,24,0,0"/>
<CommandBar DefaultLabelPosition="Right" HorizontalAlignment="Right" Margin="0,12,0,0">
<AppBarButton Icon="Add" Label="添加候车区" Command="{x:Bind ViewModel.AddWaitingAreaCommand}" />
<AppBarButton Icon="Add" Label="添加检票口" Command="{x:Bind ViewModel.AddTicketCheckCommand}" CommandParameter="{x:Bind WaitingAreasList.SelectedItem, Mode=OneWay}"/>
<AppBarButton Icon="Clear" Label="删除" Command="{x:Bind ViewModel.DeleteItemCommand}" CommandParameter="{x:Bind WaitingAreasList.SelectedItem, Mode=OneWay}" />
<AppBarButton Icon="Delete" Label="清空" Command="{x:Bind ViewModel.DeleteAllTicketChecksCommand}" />
</CommandBar>
<Border Style="{StaticResource ListViewBorder}" Margin="0,8,0,0">
<TreeView MaxHeight="300" Name="WaitingAreasList"
ItemsSource="{x:Bind ViewModel.WaitingAreas, Mode=OneWay}" ItemTemplateSelector="{StaticResource TreeViewTemplateSelector}" />
</Border>
<TextBlock Text="车次" Style="{StaticResource BodyStrongTextBlockStyle}" Margin="0,24,0,0"/>
<CommandBar DefaultLabelPosition="Right" HorizontalAlignment="Right" Margin="0,12,0,0">
<AppBarButton Icon="Add" Label="添加" Command="{x:Bind ViewModel.AddTrainStopCommand}" />
@@ -195,18 +171,16 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="车次" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="1" Text="长度" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="2" Text="到时" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="3" Text="发时" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="4" Text="候车区" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="5" Text="检票口" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="6" Text="站台" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="7" Text="地标" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="8" Text="始发站" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="9" Text="终到站" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="4" Text="检票口" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}"/>
<TextBlock Grid.Column="5" Text="站台" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="6" Text="地标" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="7" Text="始发站" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
<TextBlock Grid.Column="8" Text="终到站" Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}" />
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
@@ -222,18 +196,16 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{x:Bind Number}" />
<TextBlock Grid.Column="1" Text="{x:Bind Length}" />
<TextBlock Grid.Column="2" Text="{x:Bind ArrivalTime, Converter={StaticResource TimeSpanToStringConverter}}" />
<TextBlock Grid.Column="3" Text="{x:Bind DepartureTime, Converter={StaticResource TimeSpanToStringConverter}}" />
<TextBlock Grid.Column="4" Text="{x:Bind WaitingArea}" />
<TextBlock Grid.Column="5" Text="{x:Bind TicketChecks, Converter={StaticResource ListToStringConverter}}" />
<TextBlock Grid.Column="6" Text="{x:Bind Platform}" />
<TextBlock Grid.Column="7" Text="{x:Bind Landmark}" />
<TextBlock Grid.Column="8" Text="{x:Bind Origin}" />
<TextBlock Grid.Column="9" Text="{x:Bind Terminal}" />
<TextBlock Grid.Column="4" Text="{x:Bind TicketCheckIds, Converter={StaticResource TicketChecksToStringConverter}}"/>
<TextBlock Grid.Column="5" Text="{x:Bind Platform}" />
<TextBlock Grid.Column="6" Text="{x:Bind Landmark}" />
<TextBlock Grid.Column="7" Text="{x:Bind Origin}" />
<TextBlock Grid.Column="8" Text="{x:Bind Terminal}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>2.3.107.0</Version>
<Version>2.3.108.0</Version>
<Authors>电排骨</Authors>
<RepositoryUrl>https://github.com/denglihong2007/CRSim</RepositoryUrl>
</PropertyGroup>