diff --git a/.github/ISSUE_TEMPLATE/BugReport.yml b/.github/ISSUE_TEMPLATE/BugReport.yml new file mode 100644 index 0000000..3c6b18b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BugReport.yml @@ -0,0 +1,92 @@ +name: Bug 反馈 +description: 在使用 CRSim 的过程中遇到了 Bug。 +title: (将此栏【替换】为你的标题) +labels: ["Bug", "待查看", "优先级:无"] +type: Bug +body: + - type: markdown + attributes: + value: | + 感谢您进行 Bug 反馈。请在上面的文本框⬆️起一个能够清晰描述您的问题的标题,便于开发者解决您的问题。 + + > [!important] + > 重要:如果要提出**多个 Bug**,请为每一个 Bug 开一个单独的 issue。 + > + > 如果您不知道如何有效、精准地表述,我们建议您先阅读《提问的智慧》([链接](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md))。 + + - type: checkboxes + id: checklist + attributes: + label: 检查清单 + description: 在开始反馈这个问题之前,请先检查: + options: + - label: 我已更新到最新版 ![最新的版本号](https://img.shields.io/github/v/release/denglihong2007/CRSim?include_prereleases&style=flat-square&label=) 或 ![最新的版本号](https://img.shields.io/github/v/release/denglihong2007/CRSim?style=flat-square&label=) 测试,并看过 [最新提交](https://github.com/denglihong2007/CRSim/commits/dev/),确认这一 Bug 还没有修复。我也已在 [Issues](https://github.com/denglihong2007/CRSim/issues?q=type%3ABug) 中检索,确认这一 Bug 未被提交过。 + required: true + - label: 我已经仔细阅读过选项里的内容,并且知道这个选项不用勾选。 + - label: 我已知晓并同意,此处仅用于汇报程序中存在的问题(关于其他非程序本身的问题应当在 Discussion 板块提出)。 + required: true + - type: markdown + attributes: + value: | + ### Bug 信息 + + 描述您遇到的 Bug。您可以附上截图、录屏、堆栈跟踪、日志等材料,便于开发者追踪问题。 + + > [!note] + > 上传附件时**请优先使用 GitHub 的附件系统上传附件**,将需要上传的附件粘贴或拖动到撰写区域即可上传。多个文件可打包为 zip 格式后上传。**尽量避免使用需要登陆或安装客户端才能接收附件的网盘(如\*度网盘、\*克网盘、1\*3网盘等)上传附件**,以避免开发者因不具有对应网盘客户端或账户而延误问题诊断。 + - type: textarea + id: excepted + attributes: + label: 期望的行为 + description: 详细的描述你期望发生的行为,突出与目前(可能不正确的)行为的不同。 + validations: + required: true + - type: textarea + id: what-happened + attributes: + label: 实际结果 + description: 实际发生的行为。 + validations: + required: true + - type: textarea + id: reproduce-steps + attributes: + label: 重现步骤 + description: | + 详细描述要怎么操作才能再次触发这个 Bug。 + placeholder: | + 1. 首先…… + 2. 然后…… + 3. …… + validations: + required: true + - type: textarea + id: stacktrace + attributes: + label: 堆栈跟踪(可选) + description: 如果在遇到这个 Bug 时发生了崩溃(弹出崩溃提示),或者产生了错误日志,请将产生的堆栈跟踪信息粘贴到此处,便于开发者定位 Bug。 + render: shell + - type: input + id: app_version + attributes: + label: 应用版本 + description: 您当前使用的 CRSim 版本号,可以在【设置】->【关于 CRSim】中查看。 + placeholder: 如:2.3.102.0 + validations: + required: true + - type: input + id: os_version + attributes: + label: 操作系统版本 + description: | + 您当前使用的 Windows 操作系统版本,可以运行 `winver.exe` 查看。 + 如果您不能肯定此 Bug 与操作系统无关,请填写此项。 + placeholder: 如:10.0.22000.0 + - type: checkboxes + id: revision + attributes: + label: 最后一步 + description: 回顾您的回答 + options: + - label: 我认为上述的描述已经足以详细,以允许开发人员能复现该问题。如果我的 issue 没有按照上述的要求填写,可能会被无条件关闭。 + required: true diff --git a/.github/ISSUE_TEMPLATE/FeatureRequest.yml b/.github/ISSUE_TEMPLATE/FeatureRequest.yml new file mode 100644 index 0000000..d04550a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FeatureRequest.yml @@ -0,0 +1,55 @@ +name: 功能请求 +description: 提出一项新的功能。 +title: (将此栏【替换】为你的标题) +labels: ["新功能", "待查看", "优先级:无"] +type: 特性 +body: + - type: markdown + attributes: + value: | + 感谢您贡献您的想法。请在上面的文本框⬆️起一个能够清晰概括此功能的标题,便于开发者了解您的想法。 + + > [!important] + > 重要:如果您想要提出 **多项功能**,请为每一个功能开一个单独的 issue。 + > + > 由于开发者时间精力有限,加上已经积压了大量的功能请求,提交的功能请求可能需要比较久的时间才能实现。 + - type: checkboxes + id: checklist + attributes: + label: 检查清单 + description: 在开始反馈这个问题之前,请先检查: + options: + - label: 我已将本应用更新到最新测试版 ![最新的版本号](https://img.shields.io/github/v/release/denglihong2007/CRSim?include_prereleases&style=flat-square&label=),并看过 [最新提交](https://github.com/denglihong2007/CRSim/commits/dev/),确认我想要的功能还没有实现。我也已在 [Issues](https://github.com/denglihong2007/CRSim/issues?q=-label%3ABug) 确认我想要的功能未被提交过。 + required: true + - label: 我认为我想要的功能是用户广泛需要的。 + required: true + - label: 我已经仔细阅读过选项里的内容,并且知道这个选项不用勾选。 + - label: 我已知晓由于开发者时间精力有限,加上已经积压了大量的功能请求,我提交的功能请求可能需要比较久的时间才能实现。 + required: true + - type: markdown + attributes: + value: | + > [!note] + > 上传附件时**请优先使用 GitHub 的附件系统上传附件**,将需要上传的附件粘贴或拖动到撰写区域即可上传。多个文件可打包为 zip 格式后上传。**尽量避免使用需要登陆或安装客户端才能接收附件的网盘(如\*度网盘、\*克网盘、1\*3网盘等)上传附件**,以避免开发者因不具有对应网盘客户端或账户而延误问题诊断。 + - type: textarea + id: back + attributes: + label: 背景与动机 + description: 为什么你需要这个功能?这个功能对你和其他用户有多重要? + validations: + required: true + - type: textarea + id: req + attributes: + label: 想要实现或优化的功能 + description: 详细地描述一下你想要的功能,描述的越具体,采纳的可能性越高。 + validations: + required: true + - type: checkboxes + id: revision + attributes: + label: 最后一步 + description: 回顾您的回答 + options: + - label: 我认为上述的描述已经足以详细,以便开发人员理解您想要的功能。如果我的 issue 没有按照上述的要求填写,可能会被无条件关闭。 + required: true diff --git a/CRSim.Core/Services/IDatabaseService.cs b/CRSim.Core/Abstractions/IDatabaseService.cs similarity index 86% rename from CRSim.Core/Services/IDatabaseService.cs rename to CRSim.Core/Abstractions/IDatabaseService.cs index 3d44443..5db1362 100644 --- a/CRSim.Core/Services/IDatabaseService.cs +++ b/CRSim.Core/Abstractions/IDatabaseService.cs @@ -1,8 +1,7 @@ using CRSim.Core.Models; -using static System.Collections.Specialized.BitVector32; using Section = CRSim.Core.Models.Section; -namespace CRSim.Core.Services +namespace CRSim.Core.Abstractions { public interface IDatabaseService { @@ -13,7 +12,7 @@ namespace CRSim.Core.Services void AddStationByName(string name); void AddStation(Station station); Task ImportStationFrom7D(string path); - void AddTrainNumber(string number); + void AddTrainNumber(TrainNumber trainNumber); void DeleteStation(Station station); void DeleteTrainNumber(TrainNumber trainNumber); void UpdateStation(string stationName,Station station); diff --git a/CRSim.Core/Abstractions/IHasTimeService.cs b/CRSim.Core/Abstractions/IHasTimeService.cs new file mode 100644 index 0000000..8742198 --- /dev/null +++ b/CRSim.Core/Abstractions/IHasTimeService.cs @@ -0,0 +1,7 @@ +namespace CRSim.Core.Abstractions +{ + public interface IHasTimeService + { + ITimeService TimeService { get; set; } + } +} diff --git a/CRSim.Core/Abstractions/INetworkService.cs b/CRSim.Core/Abstractions/INetworkService.cs new file mode 100644 index 0000000..13975b1 --- /dev/null +++ b/CRSim.Core/Abstractions/INetworkService.cs @@ -0,0 +1,13 @@ +using CRSim.Core.Models; +using CRSim.Core.Models.Plugin; + +namespace CRSim.Core.Abstractions +{ + public interface INetworkService + { + Task?> GetTimeTableAsync(string number); + Task?> GetTrainNumbersAsync(string name); + List? GetOnlinePlugins(string uri); + Task GetUpdateAsync(string uri); + } +} diff --git a/CRSim.Core/Abstractions/IPluginService.cs b/CRSim.Core/Abstractions/IPluginService.cs new file mode 100644 index 0000000..60296d6 --- /dev/null +++ b/CRSim.Core/Abstractions/IPluginService.cs @@ -0,0 +1,31 @@ +using CRSim.Core.Models.Plugin; +using System.Collections.ObjectModel; + +namespace CRSim.Core.Abstractions; + +/// +/// 插件服务。用于管理应用各插件的加载和设置。 +/// +public interface IPluginService +{ + /// + /// 插件包文件扩展名。 + /// + public static readonly string PluginPackageExtension = ".crsp"; + + internal static List LoadedPluginsInternal { get; } = []; + + /// + /// 已加载的插件信息列表。 + /// + public static List LoadedPlugins => LoadedPluginsInternal; + + internal static List OnlinePluginsInternal { get; } = []; + + public static List OnlinePlugins => OnlinePluginsInternal; + + Task InstallPluginOnlineAsync(PluginInfo plugin); + Task InstallPluginLocalAsync(string filePath); + Task PackPluginAsync(PluginInfo plugin, string filePath); + Task LoadOnlinePluginsAsync(); +} \ No newline at end of file diff --git a/CRSim.Core/Services/ISettingsService.cs b/CRSim.Core/Abstractions/ISettingsService.cs similarity index 80% rename from CRSim.Core/Services/ISettingsService.cs rename to CRSim.Core/Abstractions/ISettingsService.cs index da71bde..173c1ef 100644 --- a/CRSim.Core/Services/ISettingsService.cs +++ b/CRSim.Core/Abstractions/ISettingsService.cs @@ -5,10 +5,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace CRSim.Core.Services +namespace CRSim.Core.Abstractions { public interface ISettingsService { + void LoadSettings(); void SaveSettings(); Settings GetSettings(); } diff --git a/CRSim.Core/Abstractions/ITimeService.cs b/CRSim.Core/Abstractions/ITimeService.cs new file mode 100644 index 0000000..6e52db5 --- /dev/null +++ b/CRSim.Core/Abstractions/ITimeService.cs @@ -0,0 +1,17 @@ +namespace CRSim.Core.Abstractions +{ + public interface ITimeService + { + event EventHandler OneSecondElapsed; + + event EventHandler RefreshSecondsElapsed; + + void Start(); + + DateTime GetDateTimeNow(); + + DateTime SimulateTime { get; set; } // 初始模拟时间 + + double Speed { get; set; } // 模拟倍速 + } +} \ No newline at end of file diff --git a/CRSim.Core/Abstractions/PluginBase.cs b/CRSim.Core/Abstractions/PluginBase.cs new file mode 100644 index 0000000..0056c3d --- /dev/null +++ b/CRSim.Core/Abstractions/PluginBase.cs @@ -0,0 +1,21 @@ +using CRSim.Core.Models.Plugin; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace CRSim.Core.Abstractions; +public abstract class PluginBase +{ + /// + /// 当前插件的信息 + /// + public PluginInfo Info { get; internal set; } = null!; + + public abstract Type ViewModel { get; } + public abstract Type View { get; } + /// + /// 初始化插件。一般在这个方法中完成插件的各项服务的注册。 + /// + /// + /// + public abstract void Initialize(HostBuilderContext context, IServiceCollection services); +} \ No newline at end of file diff --git a/CRSim.Core/Attributes/PluginEntrance.cs b/CRSim.Core/Attributes/PluginEntrance.cs new file mode 100644 index 0000000..b09365b --- /dev/null +++ b/CRSim.Core/Attributes/PluginEntrance.cs @@ -0,0 +1,3 @@ +namespace CRSim.Core.Attributes; +[AttributeUsage(AttributeTargets.Class)] +public class PluginEntrance : Attribute; \ No newline at end of file diff --git a/CRSim.Core/CRSim.Core.csproj b/CRSim.Core/CRSim.Core.csproj index 42cb3d9..c982424 100644 --- a/CRSim.Core/CRSim.Core.csproj +++ b/CRSim.Core/CRSim.Core.csproj @@ -1,10 +1,20 @@  - net9.0 - enable - enable - 13.0 + net9.0-windows10.0.19041.0 + 10.0.19041.0 + enable + enable + AnyCPU;x64 + CRSim.Core + True + + + + + + + diff --git a/CRSim.Core/Enums/PluginLoadStatus.cs b/CRSim.Core/Enums/PluginLoadStatus.cs new file mode 100644 index 0000000..47e2c44 --- /dev/null +++ b/CRSim.Core/Enums/PluginLoadStatus.cs @@ -0,0 +1,24 @@ +namespace CRSim.Core.Enums; + +/// +/// 表示插件加载状态。 +/// +public enum PluginLoadStatus +{ + /// + /// 插件未加载 + /// + NotLoaded, + /// + /// 插件成功加载。 + /// + Loaded, + /// + /// 插件已禁用。 + /// + Disabled, + /// + /// 插件加载时出现错误。 + /// + Error, +} \ No newline at end of file diff --git a/CRSim.Core/Models/CommandLineOptions.cs b/CRSim.Core/Models/CommandLineOptions.cs new file mode 100644 index 0000000..12a3dc4 --- /dev/null +++ b/CRSim.Core/Models/CommandLineOptions.cs @@ -0,0 +1,8 @@ +namespace CRSim.Core.Models +{ + public class CommandLineOptions + { + public string? ExternalPluginPath { get; set; } + public bool Debug { get; set; } + } +} diff --git a/CRSim.Core/Models/JsonSerializationContext.cs b/CRSim.Core/Models/JsonSerializationContext.cs new file mode 100644 index 0000000..640adbf --- /dev/null +++ b/CRSim.Core/Models/JsonSerializationContext.cs @@ -0,0 +1,20 @@ +using CRSim.Core.Models.Plugin; +using System.Text.Json.Serialization; + +namespace CRSim.Core.Models +{ + [JsonSerializable(typeof(Json))] + [JsonSerializable(typeof(UpdateInfo))] + [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] + public partial class JsonContext : JsonSerializerContext + { + } + + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(StyleInfo))] + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] + public partial class JsonContextWithCamelCase : JsonSerializerContext + { + } +} + diff --git a/CRSim.Core/Models/Plugin/PluginInfo.cs b/CRSim.Core/Models/Plugin/PluginInfo.cs new file mode 100644 index 0000000..62c8fc8 --- /dev/null +++ b/CRSim.Core/Models/Plugin/PluginInfo.cs @@ -0,0 +1,86 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CRSim.Core.Abstractions; +using CRSim.Core.Enums; + +namespace CRSim.Core.Models.Plugin; +public partial class PluginInfo : ObservableRecipient +{ + [ObservableProperty] + private int _downloadProgress = 0; + + public bool IsAvailableOnMarket => IPluginService.OnlinePlugins.Any(x => x.Manifest.Id == Manifest.Id); + + public bool IsUpdateAvailable + { + get + { + if (IsAvailableOnMarket) + { + if(IPluginService.OnlinePlugins.Where(x => x.Manifest.Id == Manifest.Id).FirstOrDefault() is PluginInfo onlinePlugin) + { + return onlinePlugin.Manifest.Version != Manifest.Version; + } + } + return false; + } + } + + [ObservableProperty] + private PluginManifest _manifest = new(); + + [ObservableProperty] + private bool _restartRequired = false; + + public PluginLoadStatus LoadStatus { get; internal set; } = PluginLoadStatus.NotLoaded; + + public bool IsEnabled + { + get + { + return !File.Exists(Path.Combine(PluginFolderPath, ".disabled")); + } + set + { + var path = Path.Combine(PluginFolderPath, ".disabled"); + RestartRequired = true; + if (value) + { + File.Delete(path); + } + else + { + File.WriteAllText(path, ""); + } + OnPropertyChanged(); + } + } + + public string PluginFolderPath { get; internal set; } = ""; + public string RealIconPath { get; set; } = ""; + public bool IsUninstalling + { + get + { + return File.Exists(Path.Combine(PluginFolderPath, ".uninstall")); + } + set + { + var path = Path.Combine(PluginFolderPath, ".uninstall"); + if (value) + { + RestartRequired = true; + File.WriteAllText(path, ""); + } + else + { + RestartRequired = false; + File.Delete(path); + } + OnPropertyChanged(); + } + } + public Exception? Exception { get; internal set; } + + [ObservableProperty] + private StyleInfo? styleInfo; +} \ No newline at end of file diff --git a/CRSim.Core/Models/Plugin/PluginManifest.cs b/CRSim.Core/Models/Plugin/PluginManifest.cs new file mode 100644 index 0000000..e0679fa --- /dev/null +++ b/CRSim.Core/Models/Plugin/PluginManifest.cs @@ -0,0 +1,53 @@ +namespace CRSim.Core.Models.Plugin; + +/// +/// 插件元数据 +/// +public class PluginManifest +{ + /// + /// 入口程序集。加载插件时,将在此入口程序集中搜索插件类。 + /// + /// MyPlugin.dll + public string EntranceAssembly { get; set; } = ""; + + /// + /// 插件显示名称。 + /// + public string Name { get; set; } = ""; + + /// + /// 插件ID。 + /// + public string Id { get; set; } = ""; + + /// + /// 插件自述。 + /// + public string Description { get; set; } = ""; + + /// + /// 项目 Url + /// + public string Url { get; set; } = ""; + + /// + /// 插件版本 + /// + public string Version { get; set; } = ""; + + /// + /// 插件目标 CRSim 版本 + /// + public string ApiVersion { get; set; } = ""; + + /// + /// 插件作者 + /// + public string Author { get; set; } = ""; + + /// + /// 插件类型 + /// + public string Type { get; set; } = ""; +} \ No newline at end of file diff --git a/CRSim.Core/Models/Settings.cs b/CRSim.Core/Models/Settings.cs index d79e3c1..4b4b8ef 100644 --- a/CRSim.Core/Models/Settings.cs +++ b/CRSim.Core/Models/Settings.cs @@ -5,8 +5,6 @@ namespace CRSim.Core.Models { public class Settings { - public TimeSpan TimeOffset { get; set; } = TimeSpan.Zero; - public TimeSpan DepartureCheckInAdvanceDuration { get; set; } = TimeSpan.FromMinutes(20); public TimeSpan PassingCheckInAdvanceDuration { get; set; } = TimeSpan.FromMinutes(10); @@ -21,7 +19,7 @@ namespace CRSim.Core.Models /// public TimeSpan StopDisplayFromArrivalDuration { get; set; } = TimeSpan.FromMinutes(10); public TimeSpan StopCheckInAdvanceDuration { get; set; } = TimeSpan.FromMinutes(2); - + public string ApiUri { get; set; } = "http://47.122.74.193:25565"; public int MaxPages { get; set; } = 3; public int SwitchPageSeconds { get; set; } = 20; public string UserKey { get; set; } = ""; diff --git a/CRSim.Core/Models/Platform.cs b/CRSim.Core/Models/Simulator/Platform.cs similarity index 100% rename from CRSim.Core/Models/Platform.cs rename to CRSim.Core/Models/Simulator/Platform.cs diff --git a/CRSim.Core/Models/Section.cs b/CRSim.Core/Models/Simulator/Section.cs similarity index 100% rename from CRSim.Core/Models/Section.cs rename to CRSim.Core/Models/Simulator/Section.cs diff --git a/CRSim.Core/Models/Station.cs b/CRSim.Core/Models/Simulator/Station.cs similarity index 100% rename from CRSim.Core/Models/Station.cs rename to CRSim.Core/Models/Simulator/Station.cs diff --git a/CRSim.Core/Models/StationType.cs b/CRSim.Core/Models/Simulator/StationType.cs similarity index 100% rename from CRSim.Core/Models/StationType.cs rename to CRSim.Core/Models/Simulator/StationType.cs diff --git a/CRSim.Core/Models/Ticket.cs b/CRSim.Core/Models/Simulator/Ticket.cs similarity index 100% rename from CRSim.Core/Models/Ticket.cs rename to CRSim.Core/Models/Simulator/Ticket.cs diff --git a/CRSim.Core/Models/Simulator/TicketCheck.cs b/CRSim.Core/Models/Simulator/TicketCheck.cs new file mode 100644 index 0000000..c6591d0 --- /dev/null +++ b/CRSim.Core/Models/Simulator/TicketCheck.cs @@ -0,0 +1,9 @@ +namespace CRSim.Core.Models +{ + public class TicketCheck + { + 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/Tickets.cs b/CRSim.Core/Models/Simulator/Tickets.cs similarity index 100% rename from CRSim.Core/Models/Tickets.cs rename to CRSim.Core/Models/Simulator/Tickets.cs diff --git a/CRSim.Core/Models/TrainNumber.cs b/CRSim.Core/Models/Simulator/TrainNumber.cs similarity index 100% rename from CRSim.Core/Models/TrainNumber.cs rename to CRSim.Core/Models/Simulator/TrainNumber.cs diff --git a/CRSim.Core/Models/TrainStop.cs b/CRSim.Core/Models/Simulator/TrainStop.cs similarity index 86% rename from CRSim.Core/Models/TrainStop.cs rename to CRSim.Core/Models/Simulator/TrainStop.cs index 9893649..087d376 100644 --- a/CRSim.Core/Models/TrainStop.cs +++ b/CRSim.Core/Models/Simulator/TrainStop.cs @@ -15,9 +15,10 @@ namespace CRSim.Core.Models [JsonConverter(typeof(TimeSpanJsonConverter))] public TimeSpan? DepartureTime { get; set; } - public List? TicketChecks { get; set; } + [JsonConverter(typeof(TimeSpanJsonConverter))] + public TimeSpan? Status { get; set; } = TimeSpan.Zero; - 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 new file mode 100644 index 0000000..0b96f71 --- /dev/null +++ b/CRSim.Core/Models/Simulator/WaitingArea.cs @@ -0,0 +1,10 @@ +using System.Collections.ObjectModel; + +namespace CRSim.Core.Models +{ + public class WaitingArea + { + public string Name { get; set; } + public ObservableCollection TicketChecks { get; set; } = []; + } +} diff --git a/CRSim.Core/Models/StyleInfo.cs b/CRSim.Core/Models/StyleInfo.cs new file mode 100644 index 0000000..cee4ce3 --- /dev/null +++ b/CRSim.Core/Models/StyleInfo.cs @@ -0,0 +1,11 @@ +using CRSim.Core.Models.Plugin; + +namespace CRSim.Core.Models +{ + public class StyleInfo + { + public string[] Parameters { get; set; } = []; + public string Type { get; set; } = ""; + public string Region { get; set; } = ""; + } +} diff --git a/CRSim.Core/Models/UpdateInfo.cs b/CRSim.Core/Models/UpdateInfo.cs new file mode 100644 index 0000000..280a9b8 --- /dev/null +++ b/CRSim.Core/Models/UpdateInfo.cs @@ -0,0 +1,170 @@ +using System.Text.Json.Serialization; + +namespace CRSim.Core.Models +{ + public class UpdateInfo + { + [JsonPropertyName("url")] + public string? Url { get; set; } + [JsonPropertyName("assets_url")] + public string? AssetsUrl { get; set; } + [JsonPropertyName("upload_url")] + public string? UploadUrl { get; set; } + [JsonPropertyName("html_url")] + public string? HtmlUrl { get; set; } + [JsonPropertyName("id")] + public long? Id { get; set; } + [JsonPropertyName("author")] + public AuthorEntity? Author { get; set; } + [JsonPropertyName("node_id")] + public string? NodeId { get; set; } + [JsonPropertyName("tag_name")] + public string? TagName { get; set; } + [JsonPropertyName("target_commitish")] + public string? TargetCommitish { get; set; } + [JsonPropertyName("name")] + public string? Name { get; set; } + [JsonPropertyName("draft")] + public bool? Draft { get; set; } + [JsonPropertyName("immutable")] + public bool? Immutable { get; set; } + [JsonPropertyName("prerelease")] + public bool? Prerelease { get; set; } + [JsonPropertyName("created_at")] + public string? CreatedAt { get; set; } + [JsonPropertyName("updated_at")] + public string? UpdatedAt { get; set; } + [JsonPropertyName("published_at")] + public string? PublishedAt { get; set; } + [JsonPropertyName("assets")] + public List? Assets { get; set; } + [JsonPropertyName("tarball_url")] + public string? TarballUrl { get; set; } + [JsonPropertyName("zipball_url")] + public string? ZipballUrl { get; set; } + [JsonPropertyName("body")] + public string? Body { get; set; } + [JsonPropertyName("mentions_count")] + public long? MentionsCount { get; set; } + + } + + public class AuthorEntity + { + [JsonPropertyName("login")] + public string? Login { get; set; } + [JsonPropertyName("id")] + public long? Id { get; set; } + [JsonPropertyName("node_id")] + public string? NodeId { get; set; } + [JsonPropertyName("avatar_url")] + public string? AvatarUrl { get; set; } + [JsonPropertyName("gravatar_id")] + public string? GravatarId { get; set; } + [JsonPropertyName("url")] + public string? Url { get; set; } + [JsonPropertyName("html_url")] + public string? HtmlUrl { get; set; } + [JsonPropertyName("followers_url")] + public string? FollowersUrl { get; set; } + [JsonPropertyName("following_url")] + public string? FollowingUrl { get; set; } + [JsonPropertyName("gists_url")] + public string? GistsUrl { get; set; } + [JsonPropertyName("starred_url")] + public string? StarredUrl { get; set; } + [JsonPropertyName("subscriptions_url")] + public string? SubscriptionsUrl { get; set; } + [JsonPropertyName("organizations_url")] + public string? OrganizationsUrl { get; set; } + [JsonPropertyName("repos_url")] + public string? ReposUrl { get; set; } + [JsonPropertyName("events_url")] + public string? EventsUrl { get; set; } + [JsonPropertyName("received_events_url")] + public string? ReceivedEventsUrl { get; set; } + [JsonPropertyName("type")] + public string? Type { get; set; } + [JsonPropertyName("user_view_type")] + public string? UserViewType { get; set; } + [JsonPropertyName("site_admin")] + public bool? SiteAdmin { get; set; } + + } + + public class AssetsEntity + { + [JsonPropertyName("url")] + public string? Url { get; set; } + [JsonPropertyName("id")] + public long? Id { get; set; } + [JsonPropertyName("node_id")] + public string? NodeId { get; set; } + [JsonPropertyName("name")] + public string? Name { get; set; } + [JsonPropertyName("label")] + public string? Label { get; set; } + [JsonPropertyName("uploader")] + public UploaderEntity? Uploader { get; set; } + [JsonPropertyName("content_type")] + public string? ContentType { get; set; } + [JsonPropertyName("state")] + public string? State { get; set; } + [JsonPropertyName("size")] + public long? Size { get; set; } + [JsonPropertyName("digest")] + public string? Digest { get; set; } + [JsonPropertyName("download_count")] + public long? DownloadCount { get; set; } + [JsonPropertyName("created_at")] + public string? CreatedAt { get; set; } + [JsonPropertyName("updated_at")] + public string? UpdatedAt { get; set; } + [JsonPropertyName("browser_download_url")] + public string? BrowserDownloadUrl { get; set; } + + } + + public class UploaderEntity + { + [JsonPropertyName("login")] + public string? Login { get; set; } + [JsonPropertyName("id")] + public long? Id { get; set; } + [JsonPropertyName("node_id")] + public string? NodeId { get; set; } + [JsonPropertyName("avatar_url")] + public string? AvatarUrl { get; set; } + [JsonPropertyName("gravatar_id")] + public string? GravatarId { get; set; } + [JsonPropertyName("url")] + public string? Url { get; set; } + [JsonPropertyName("html_url")] + public string? HtmlUrl { get; set; } + [JsonPropertyName("followers_url")] + public string? FollowersUrl { get; set; } + [JsonPropertyName("following_url")] + public string? FollowingUrl { get; set; } + [JsonPropertyName("gists_url")] + public string? GistsUrl { get; set; } + [JsonPropertyName("starred_url")] + public string? StarredUrl { get; set; } + [JsonPropertyName("subscriptions_url")] + public string? SubscriptionsUrl { get; set; } + [JsonPropertyName("organizations_url")] + public string? OrganizationsUrl { get; set; } + [JsonPropertyName("repos_url")] + public string? ReposUrl { get; set; } + [JsonPropertyName("events_url")] + public string? EventsUrl { get; set; } + [JsonPropertyName("received_events_url")] + public string? ReceivedEventsUrl { get; set; } + [JsonPropertyName("type")] + public string? Type { get; set; } + [JsonPropertyName("user_view_type")] + public string? UserViewType { get; set; } + [JsonPropertyName("site_admin")] + public bool? SiteAdmin { get; set; } + + } +} diff --git a/CRSim.Core/Models/WaitingArea.cs b/CRSim.Core/Models/WaitingArea.cs deleted file mode 100644 index 440166f..0000000 --- a/CRSim.Core/Models/WaitingArea.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CRSim.Core.Models -{ - public class WaitingArea - { - public string Name { get; set; } - public List TicketChecks { get; set; } = []; - } -} diff --git a/CRSim.Core/Services/DatabaseService.cs b/CRSim.Core/Services/DatabaseService.cs index 908ad0c..5e9b199 100644 --- a/CRSim.Core/Services/DatabaseService.cs +++ b/CRSim.Core/Services/DatabaseService.cs @@ -1,4 +1,6 @@ -using CRSim.Core.Models; +using CRSim.Core.Abstractions; +using CRSim.Core.Models; +using CRSim.Core.Utils; using System.Text.Json; using System.Text.Json.Serialization; @@ -6,23 +8,9 @@ namespace CRSim.Core.Services { public class DatabaseService : IDatabaseService { - private readonly string _jsonFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CRSim"); private List _stations; private List _trainNumbers; - private readonly JsonSerializerOptions options = new() - { - WriteIndented = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - }; - - public DatabaseService() - { - if (!Directory.Exists(_jsonFilePath)) Directory.CreateDirectory(_jsonFilePath); - _jsonFilePath += "\\data.json"; - ImportData(_jsonFilePath); - } - public List GetAllStations() { return _stations; @@ -32,21 +20,10 @@ namespace CRSim.Core.Services { try { - var json = File.ReadAllText(jsonFilePath).Replace("StationStop", "TrainStop");// 修复旧版本的数据 - var data = JsonSerializer.Deserialize(json); + 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.WaitingArea = station.WaitingAreas.Where(x => x.TicketChecks.Contains(trainStop.TicketChecks[0])).FirstOrDefault().Name; - } - } - }// 修复旧版本的数据 } catch { @@ -109,8 +86,8 @@ namespace CRSim.Core.Services { Stations = _stations, TrainNumbers = _trainNumbers - }, options); - await File.WriteAllTextAsync(_jsonFilePath, json); + },JsonContext.Default.Json); + await File.WriteAllTextAsync(AppPaths.ConfigFilePath, json); } public async Task ExportData(string p) @@ -119,7 +96,7 @@ namespace CRSim.Core.Services { Stations = _stations, TrainNumbers = _trainNumbers - }, options); + }, JsonContext.Default.Json); await File.WriteAllTextAsync(p, json); } @@ -128,12 +105,9 @@ namespace CRSim.Core.Services return _trainNumbers; } - public void AddTrainNumber(string number) + public void AddTrainNumber(TrainNumber trainNumber) { - _trainNumbers.Add(new TrainNumber() - { - Number = number - }); + _trainNumbers.Add(trainNumber); } public async Task ImportStationFrom7D(string path) @@ -169,9 +143,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 }); } } @@ -199,10 +173,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.Core/Services/INetworkService.cs b/CRSim.Core/Services/INetworkService.cs deleted file mode 100644 index f756bdd..0000000 --- a/CRSim.Core/Services/INetworkService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CRSim.Core.Services -{ - public interface INetworkService - { - Task?> GetTimeTableAsync(string number); - Task> GetTrainNumbersAsync(string name); - } -} diff --git a/CRSim.Core/Services/ITimeService.cs b/CRSim.Core/Services/ITimeService.cs deleted file mode 100644 index 158a549..0000000 --- a/CRSim.Core/Services/ITimeService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Timers; - -namespace CRSim.Core.Services -{ - public interface ITimeService - { - event EventHandler OneSecondElapsed; // 1秒触发的事件 - event EventHandler RefreshSecondsElapsed; // 20秒触发的事件 - - void Start(); - void Stop(); - - DateTime GetDateTimeNow(); - } -} \ No newline at end of file diff --git a/CRSim.Core/Services/NetworkService.cs b/CRSim.Core/Services/NetworkService.cs index 727caca..9a91b78 100644 --- a/CRSim.Core/Services/NetworkService.cs +++ b/CRSim.Core/Services/NetworkService.cs @@ -1,13 +1,13 @@ -using CRSim.Core.Models; +using CRSim.Core.Abstractions; +using CRSim.Core.Models; +using CRSim.Core.Models.Plugin; using System.Net; using System.Text.Json; namespace CRSim.Core.Services { - public class NetworkService(IDatabaseService databaseService) : INetworkService + public class NetworkService() : INetworkService { - private readonly IDatabaseService _databaseService = databaseService; - public async Task?> GetTimeTableAsync(string number) { try @@ -61,10 +61,10 @@ namespace CRSim.Core.Services } catch { - return null; } return null; } + private static TimeSpan? ParseTime(string timeStr) { timeStr = string.Concat(timeStr.AsSpan(0, 2), ":", timeStr.AsSpan(2, 2)); @@ -77,58 +77,94 @@ namespace CRSim.Core.Services return null; } - public async Task> GetTrainNumbersAsync(string name) + public async Task?> GetTrainNumbersAsync(string name) { - var client = new HttpClient(); - var stations = (await client.GetStringAsync("https://kyfw.12306.cn/otn/resources/js/framework/station_name.js")).Split("|||"); - string tel = ""; - foreach (string station in stations) + try { - if (station.StartsWith("';")) continue; - if (station.Split("|")[1] == name) + var client = new HttpClient(); + var stations = (await client.GetStringAsync("https://kyfw.12306.cn/otn/resources/js/framework/station_name.js")).Split("|||"); + string tel = ""; + foreach (string station in stations) { - tel = station.Split("|")[2]; + if (station.StartsWith("';")) continue; + if (station.Split("|")[1] == name) + { + tel = station.Split("|")[2]; + } } - } - if (tel == "") - { - return []; - } - var baseAddress = new Uri("https://mobile.12306.cn/wxxcx/wechat/bigScreen/queryTrainByStation"); - var cookieContainer = new CookieContainer(); - cookieContainer.Add(baseAddress, new Cookie("BIGipServerweixin_xiaochengxu", "2028077578.5670.0000")); - var handler = new HttpClientHandler() { CookieContainer = cookieContainer, UseCookies = true }; + if (tel == "") + { + return []; + } + var baseAddress = new Uri("https://mobile.12306.cn/wxxcx/wechat/bigScreen/queryTrainByStation"); + var cookieContainer = new CookieContainer(); + cookieContainer.Add(baseAddress, new Cookie("BIGipServerweixin_xiaochengxu", "2028077578.5670.0000")); + var handler = new HttpClientHandler() { CookieContainer = cookieContainer, UseCookies = true }; - HttpContent content = new FormUrlEncodedContent(new Dictionary() - { - {"train_station_code",tel }, - {"train_start_date",DateTime.Now.ToString("yyyyMMdd")} - }); - client = new HttpClient(handler) { BaseAddress = baseAddress }; - client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c11)XWEB/11581"); - var response = await client.PostAsync(baseAddress, content); - var json = await response.Content.ReadAsStringAsync(); - if (json.Contains("操作失败,请稍后重试")) return []; - var list = JsonDocument.Parse(json).RootElement.GetProperty("data").EnumerateArray(); - if (!list.Any()) return []; - List trainStops = []; - foreach (var item in list) - { - var arriveTimeStr = item.GetProperty("arrive_time").GetString(); - var startTimeStr = item.GetProperty("start_time").GetString(); - TimeSpan? arriveTime = null; - TimeSpan? startTime = null; - if (arriveTimeStr != "----") + HttpContent content = new FormUrlEncodedContent(new Dictionary() { - arriveTime = TimeSpan.Parse(arriveTimeStr); - } - if (startTimeStr != arriveTimeStr) + {"train_station_code",tel }, + {"train_start_date",DateTime.Now.ToString("yyyyMMdd")} + }); + client = new HttpClient(handler) { BaseAddress = baseAddress }; + client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090c11)XWEB/11581"); + var response = await client.PostAsync(baseAddress, content); + var json = await response.Content.ReadAsStringAsync(); + if (json.Contains("操作失败,请稍后重试")) return []; + var list = JsonDocument.Parse(json).RootElement.GetProperty("data").EnumerateArray(); + if (!list.Any()) return []; + List trainStops = []; + foreach (var item in list) { - startTime = TimeSpan.Parse(startTimeStr); + var arriveTimeStr = item.GetProperty("arrive_time").GetString(); + var startTimeStr = item.GetProperty("start_time").GetString(); + TimeSpan? arriveTime = null; + TimeSpan? startTime = null; + if (arriveTimeStr != "----") + { + arriveTime = TimeSpan.Parse(arriveTimeStr); + } + if (startTimeStr != arriveTimeStr) + { + startTime = TimeSpan.Parse(startTimeStr); + } + trainStops.Add(new TrainStop() { Number = item.GetProperty("station_train_code").ToString(), Terminal = item.GetProperty("end_station_name").ToString(), Origin = item.GetProperty("start_station_name").ToString(), ArrivalTime = arriveTime, DepartureTime = startTime }); } - trainStops.Add(new TrainStop() { Number = item.GetProperty("station_train_code").ToString(), Terminal = item.GetProperty("end_station_name").ToString(), Origin = item.GetProperty("start_station_name").ToString(), ArrivalTime = arriveTime, DepartureTime = startTime }); + return trainStops; } - return trainStops; + catch + { + } + return null; + } + + public List? GetOnlinePlugins(string url) + { + try + { + var client = new HttpClient(); + var response = client.GetStringAsync(url).Result; + return JsonSerializer.Deserialize(response, JsonContextWithCamelCase.Default.ListPluginManifest); + } + catch + { + } + return null; + } + + public async Task GetUpdateAsync(string uri) + { + try + { + var client = new HttpClient(); + client.DefaultRequestHeaders.UserAgent.ParseAdd("CRSim"); + var response = await client.GetStringAsync(uri); + return JsonSerializer.Deserialize(response, JsonContext.Default.UpdateInfo); + } + catch + { + } + return null; } } } diff --git a/CRSim.Core/Services/PluginService.cs b/CRSim.Core/Services/PluginService.cs new file mode 100644 index 0000000..6cd850b --- /dev/null +++ b/CRSim.Core/Services/PluginService.cs @@ -0,0 +1,267 @@ +using CRSim.Core.Abstractions; +using CRSim.Core.Attributes; +using CRSim.Core.Enums; +using CRSim.Core.Models; +using CRSim.Core.Models.Plugin; +using CRSim.Core.Utils; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Reflection; +using System.Text.Json; +using Windows.System; +using Downloader; + +namespace CRSim.Core.Services; + +public class PluginService : IPluginService +{ + public static readonly string PluginManifestFileName = "manifest.json"; + public static readonly string StyleInfoFileName = "style.json"; + + private string IndexUrl => $"{_settings.ApiUri}/GetFile?fileName=plugins.json"; + + public static void InitializePlugins(HostBuilderContext context, IServiceCollection services,string externalPluginPath) + { + Console.WriteLine("ʼز"); + if (!Directory.Exists(AppPaths.PluginsRootPath)) + { + Directory.CreateDirectory(AppPaths.PluginsRootPath); + } + + var pluginDirs = Directory.EnumerateDirectories(AppPaths.PluginsRootPath); + if(!string.IsNullOrEmpty(externalPluginPath)) + { + pluginDirs = pluginDirs.Append(externalPluginPath); + } + + foreach (var pluginDir in pluginDirs) + { + Console.WriteLine($"ǰѰĿ¼: {pluginDir}"); + if (string.IsNullOrWhiteSpace(pluginDir)) + continue; + var manifestPath = Path.Combine(pluginDir, PluginManifestFileName); + if (!File.Exists(manifestPath)) + { + continue; + } + + var manifestYaml = File.ReadAllText(manifestPath); + PluginManifest manifest = new(); + try + { + manifest = JsonSerializer.Deserialize(manifestYaml,JsonContextWithCamelCase.Default.PluginManifest); + Console.WriteLine("ɹJSONļ: " + manifestPath); + } + catch (Exception ex) + { + Console.WriteLine("JSONʧ"); + Console.WriteLine(ex.ToString()); + continue; + } + Console.WriteLine("ҵ: " + manifest.Id); + var info = new PluginInfo + { + Manifest = manifest, + PluginFolderPath = Path.GetFullPath(pluginDir), + RealIconPath = Path.Combine(Path.GetFullPath(pluginDir), "icon.png"), + StyleInfo = manifest.Type == "ScreenStyle" ? JsonSerializer.Deserialize(File.ReadAllText(Path.Combine(Path.GetFullPath(pluginDir), StyleInfoFileName)),JsonContextWithCamelCase.Default.StyleInfo) : null, + }; + if (info.IsUninstalling) + { + Directory.Delete(pluginDir, true); + continue; + } + if (!info.IsEnabled) + { + info.LoadStatus = PluginLoadStatus.Disabled; + } + IPluginService.LoadedPluginsInternal.Add(info); + } + + foreach (var info in IPluginService.LoadedPluginsInternal) + { + if(info.LoadStatus == PluginLoadStatus.Disabled) + { + continue; + } + var manifest = info.Manifest; + var pluginDir = info.PluginFolderPath; + try + { + Console.WriteLine("Լز: " + manifest.Id); + var fullPath = Path.GetFullPath(Path.Combine(pluginDir, manifest.EntranceAssembly)); + var loadContext = new PluginLoadContext(fullPath); + var assembly = loadContext.LoadFromAssemblyPath(fullPath); + var entrance = assembly.ExportedTypes.FirstOrDefault(x => + x.BaseType == typeof(PluginBase) || + x.GetCustomAttributes().FirstOrDefault(a => a.GetType() == typeof(PluginEntrance)) != null); + + if (entrance == null) + { + continue; + } + + if (Activator.CreateInstance(entrance) is not PluginBase entranceObj) + { + continue; + } + + entranceObj.Info = info; + entranceObj.Initialize(context, services); + services.AddSingleton(typeof(PluginBase), entranceObj); + services.AddSingleton(entrance, entranceObj); + info.LoadStatus = PluginLoadStatus.Loaded; + Console.WriteLine($"زɹ: {pluginDir} ({manifest.Version})"); + } + catch (Exception ex) + { + Console.WriteLine($"زʧ: {ex}"); + info.Exception = ex; + info.LoadStatus = PluginLoadStatus.Error; + } + } + } + private readonly Models.Settings _settings; + private readonly INetworkService _networkService; + public PluginService(INetworkService networkService,ISettingsService settingsService) + { + _networkService = networkService; + _settings = settingsService.GetSettings(); + } + public async Task LoadOnlinePluginsAsync() + { + var pluginManifests = _networkService.GetOnlinePlugins(IndexUrl); + IPluginService.OnlinePluginsInternal.Clear(); + foreach (var Manifest in pluginManifests ?? []) + { + var localInfo = IPluginService.LoadedPluginsInternal.FirstOrDefault(x => x.Manifest.Id == Manifest.Id); + var info = new PluginInfo + { + Manifest = Manifest, + RealIconPath = $"{_settings.ApiUri}/GetFile?fileName=icons/{Manifest.Id}.png", + LoadStatus = localInfo?.LoadStatus ?? PluginLoadStatus.NotLoaded, + PluginFolderPath = localInfo?.PluginFolderPath ?? string.Empty, + }; + IPluginService.OnlinePluginsInternal.Add(info); + } + await Task.CompletedTask; + } + + public async Task InstallPluginOnlineAsync(PluginInfo plugin) + { + var id = plugin.Manifest.Id; + var packageUrl = $"{_settings.ApiUri}/GetFile?fileName=plugins/{id}{IPluginService.PluginPackageExtension}"; + var tempDir = Path.Combine(AppPaths.TempPath, "Plugins", Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + var packagePath = Path.Combine(tempDir, $"{id}{IPluginService.PluginPackageExtension}"); + DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + var downloader = new DownloadService(); + plugin.DownloadProgress = 0; + try + { + downloader.DownloadProgressChanged += (s, e) => + { + dispatcherQueue.TryEnqueue(() => + { + plugin.DownloadProgress = (int)e.ProgressPercentage; + Console.WriteLine(e.ProgressPercentage); + }); + }; + await downloader.DownloadFileTaskAsync(packageUrl, packagePath); + } + catch (Exception e) + { + Console.WriteLine(e); + plugin.DownloadProgress = 0; + return; + } + + // ѹĿ¼ + var destDir = Path.Combine(AppPaths.PluginsRootPath, id); + if (Directory.Exists(destDir)) + Directory.Delete(destDir, true); + Directory.CreateDirectory(destDir); + System.IO.Compression.ZipFile.ExtractToDirectory(packagePath, destDir); + + // ʱļ + try { Directory.Delete(tempDir, true); } catch { } + + plugin.DownloadProgress = 0; + plugin.RestartRequired = true; + } + + public async Task InstallPluginLocalAsync(string filePath) + { + var tempDir = Path.Combine(AppPaths.TempPath, "Plugins", Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + System.IO.Compression.ZipFile.ExtractToDirectory(filePath, tempDir); + + var manifestPath = Path.Combine(tempDir, PluginManifestFileName); + if (!File.Exists(manifestPath)) + { + throw new FileNotFoundException("ȱ manifest.json ļ"); + } + var manifestYaml = File.ReadAllText(manifestPath); + if( JsonSerializer.Deserialize(manifestYaml, JsonContextWithCamelCase.Default.PluginManifest) is not PluginManifest manifest || manifest.Id == "") + { + throw new InvalidDataException("е manifest.json ļʽȷ"); + } + + // ѹĿ¼ + var destDir = Path.Combine(AppPaths.PluginsRootPath, manifest.Id); + if (Directory.Exists(destDir)) + Directory.Delete(destDir, true); + System.IO.Compression.ZipFile.ExtractToDirectory(filePath, destDir); + + var info = new PluginInfo + { + Manifest = manifest, + PluginFolderPath = destDir, + RealIconPath = Path.Combine(destDir, "icon.png"), + StyleInfo = manifest.Type == "ScreenStyle" ? JsonSerializer.Deserialize(File.ReadAllText(Path.Combine(destDir, StyleInfoFileName)), JsonContextWithCamelCase.Default.StyleInfo) : null, + RestartRequired = true + }; + // ʱļ + try { Directory.Delete(tempDir, true); } catch { } + IPluginService.LoadedPluginsInternal.Add(info); + await Task.CompletedTask; + } + + public async Task PackPluginAsync(PluginInfo plugin, string filePath) + { + var pluginDir = plugin.PluginFolderPath; + if (string.IsNullOrWhiteSpace(pluginDir) || !Directory.Exists(pluginDir)) + throw new DirectoryNotFoundException("Ŀ¼ڡ"); + + // ʱĿ¼ڴ + var tempDir = Path.Combine(AppPaths.TempPath, "Pack", Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + + // ļʱĿ¼ųָdll + foreach (var file in Directory.EnumerateFiles(pluginDir, "*", SearchOption.AllDirectories)) + { + var fileName = Path.GetFileName(file); + if (fileName.Equals("Microsoft.Windows.SDK.NET.dll", StringComparison.OrdinalIgnoreCase) || + fileName.Equals("WinRT.Runtime.dll", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var relativePath = Path.GetRelativePath(pluginDir, file); + var destPath = Path.Combine(tempDir, relativePath); + Directory.CreateDirectory(Path.GetDirectoryName(destPath)!); + File.Copy(file, destPath, true); + } + + // ĿļѴڣɾ + if (File.Exists(filePath)) + File.Delete(filePath); + + // Ϊzip + System.IO.Compression.ZipFile.CreateFromDirectory(tempDir, filePath); + + // ʱĿ¼ + try { Directory.Delete(tempDir, true); } catch { } + + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/CRSim.Core/Services/SettingsService.cs b/CRSim.Core/Services/SettingsService.cs index accff27..385ff77 100644 --- a/CRSim.Core/Services/SettingsService.cs +++ b/CRSim.Core/Services/SettingsService.cs @@ -1,4 +1,5 @@ -using CRSim.Core.Models; +using CRSim.Core.Abstractions; +using CRSim.Core.Models; using Microsoft.Win32; using System.Text.Json; @@ -8,14 +9,10 @@ namespace CRSim.Core.Services { private Settings _settings; private RegistryKey _key = Registry.CurrentUser.OpenSubKey(@"Software\CRSim\Settings",true); - public SettingsService() - { - LoadSettings(); - } public void SaveSettings() { - _key.SetValue("TimeOffset", (int)_settings.TimeOffset.TotalMinutes); _key.SetValue("SwitchPageSeconds", _settings.SwitchPageSeconds); + _key.SetValue("ApiUri", _settings.ApiUri); _key.SetValue("MaxPages", _settings.MaxPages); _key.SetValue("StopCheckInAdvanceDuration", (int)_settings.StopCheckInAdvanceDuration.TotalMinutes); _key.SetValue("StopDisplayUntilDepartureDuration", (int)_settings.StopDisplayUntilDepartureDuration.TotalMinutes); @@ -26,7 +23,7 @@ namespace CRSim.Core.Services _key.SetValue("LoadTodayOnly", _settings.LoadTodayOnly); _key.SetValue("ReopenUnclosedScreensOnLoad", _settings.ReopenUnclosedScreensOnLoad); } - private void LoadSettings() + public void LoadSettings() { if (_key==null) { @@ -38,8 +35,8 @@ namespace CRSim.Core.Services else { _settings = new Settings(); - if (_key.GetValue("TimeOffset") != null) _settings.TimeOffset = TimeSpan.FromMinutes((int)_key.GetValue("TimeOffset")); if (_key.GetValue("SwitchPageSeconds") != null) _settings.SwitchPageSeconds = (int)_key.GetValue("SwitchPageSeconds"); + if (_key.GetValue("ApiUri") != null) _settings.ApiUri = (string)_key.GetValue("ApiUri"); if (_key.GetValue("MaxPages") != null) _settings.MaxPages = (int)_key.GetValue("MaxPages"); if (_key.GetValue("StopCheckInAdvanceDuration") != null) _settings.StopCheckInAdvanceDuration = TimeSpan.FromMinutes((int)_key.GetValue("StopCheckInAdvanceDuration")); if (_key.GetValue("StopDisplayUntilDepartureDuration") != null) _settings.StopDisplayUntilDepartureDuration = TimeSpan.FromMinutes((int)_key.GetValue("StopDisplayUntilDepartureDuration")); diff --git a/CRSim.Core/Services/TimeService.cs b/CRSim.Core/Services/TimeService.cs index 0ea2b7d..a693f00 100644 --- a/CRSim.Core/Services/TimeService.cs +++ b/CRSim.Core/Services/TimeService.cs @@ -1,4 +1,4 @@ - +using CRSim.Core.Abstractions; using CRSim.Core.Models; using System.Timers; @@ -6,49 +6,76 @@ namespace CRSim.Core.Services { public class TimeService : ITimeService { - private readonly Settings _settings; - private readonly System.Timers.Timer _oneSecondTimer; - private readonly System.Timers.Timer _twentySecondsTimer; + public DateTime SimulateTime { get; set; } - public event EventHandler OneSecondElapsed; - public event EventHandler RefreshSecondsElapsed; + private double _speed = 1.0; + public double Speed + { + get => _speed; + set + { + UpdateSimulatedOffset(); // 切换前先结算旧速率下的时间 + _speed = value; + _lastSpeedChangeTime = DateTime.Now; + } + } + + private DateTime _lastSpeedChangeTime; // 上一次倍速切换时的真实时间 + private TimeSpan _simulatedOffset = TimeSpan.Zero; // 累计模拟时间偏移(倍速放大之后的) + + private readonly System.Timers.Timer _oneSecondTimer; + private readonly System.Timers.Timer _twentySecondsTimer; + + public event EventHandler OneSecondElapsed; + public event EventHandler RefreshSecondsElapsed; + + private Settings _settings; public TimeService(ISettingsService settingsService) { _settings = settingsService.GetSettings(); - _oneSecondTimer = new(1000); + _oneSecondTimer = new(1000); _oneSecondTimer.Elapsed += OnOneSecondElapsed; - _twentySecondsTimer = new(_settings.SwitchPageSeconds*1000); + _twentySecondsTimer = new(_settings.SwitchPageSeconds * 1000); _twentySecondsTimer.Elapsed += OnTwentySecondsElapsed; + + _lastSpeedChangeTime = DateTime.Now; } public void Start() { - _oneSecondTimer.Start(); - _twentySecondsTimer.Start(); - } - - public void Stop() - { - _oneSecondTimer.Stop(); - _twentySecondsTimer.Stop(); + _oneSecondTimer.Start(); + _twentySecondsTimer.Start(); + _lastSpeedChangeTime = DateTime.Now; // 启动时刷新时间 } private void OnOneSecondElapsed(object sender, ElapsedEventArgs e) { - OneSecondElapsed?.Invoke(this, EventArgs.Empty); + OneSecondElapsed?.Invoke(this, EventArgs.Empty); } private void OnTwentySecondsElapsed(object sender, ElapsedEventArgs e) { - RefreshSecondsElapsed?.Invoke(this, EventArgs.Empty); + RefreshSecondsElapsed?.Invoke(this, EventArgs.Empty); } public DateTime GetDateTimeNow() { - return DateTime.Now+ _settings.TimeOffset; + var now = DateTime.Now; + var elapsed = now - _lastSpeedChangeTime; + return SimulateTime + _simulatedOffset + TimeSpan.FromTicks((long)(elapsed.Ticks * Speed)); + } + + /// + /// 当倍速改变时,先结算之前段的累计偏移 + /// + private void UpdateSimulatedOffset() + { + var now = DateTime.Now; + var elapsed = now - _lastSpeedChangeTime; + _simulatedOffset += TimeSpan.FromTicks((long)(elapsed.Ticks * Speed)); } } } \ No newline at end of file diff --git a/CRSim.Core/Utils/AppPaths.cs b/CRSim.Core/Utils/AppPaths.cs new file mode 100644 index 0000000..1ff84c0 --- /dev/null +++ b/CRSim.Core/Utils/AppPaths.cs @@ -0,0 +1,16 @@ +namespace CRSim.Core.Utils +{ + public static class AppPaths + { + public static string AppDataPath => + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "CRSim"); + public static string TempPath => + Path.Combine(Path.GetTempPath(), "CRSim"); + + public static string ConfigFilePath => + Path.Combine(AppDataPath, "data.json"); + + public static string PluginsRootPath => + Path.Combine(AppDataPath, "Plugins"); + } +} diff --git a/CRSim.Core/Utils/CommandLineParser.cs b/CRSim.Core/Utils/CommandLineParser.cs new file mode 100644 index 0000000..118b10b --- /dev/null +++ b/CRSim.Core/Utils/CommandLineParser.cs @@ -0,0 +1,44 @@ +using CRSim.Core.Models; +namespace CRSim.Core.Utils +{ + public static class CommandLineParser + { + public static CommandLineOptions Parse(string[] args) + { + var options = new CommandLineOptions(); + + for (int i = 1; i < args.Length; i++) + { + var arg = args[i]; + + if (!arg.StartsWith("-")) + continue; + + string key = arg.TrimStart('-', '/').ToLower(); + + // 是否为最后一个参数 或 下一个参数也是一个key(说明当前是布尔/无值参数) + bool isLastArg = (i + 1 >= args.Length); + bool nextIsFlag = !isLastArg && args[i + 1].StartsWith("-"); + + string? value = null; + if (!isLastArg && !nextIsFlag) + { + value = args[i + 1]; + i++; // skip value + } + + switch (key) + { + case "epp": + options.ExternalPluginPath = value; + break; + case "debug": + options.Debug = true; + break; + } + } + + return options; + } + } +} diff --git a/CRSim.Core/Utils/FileHashHelper.cs b/CRSim.Core/Utils/FileHashHelper.cs new file mode 100644 index 0000000..278ea79 --- /dev/null +++ b/CRSim.Core/Utils/FileHashHelper.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace CRSim.Core.Utils +{ + public class FileHashHelper + { + /// + /// 计算文件 SHA256 值 + /// + public static string ComputeSHA256(string filePath) + { + using var sha256 = SHA256.Create(); + using var stream = File.OpenRead(filePath); + var hashBytes = sha256.ComputeHash(stream); + return Convert.ToHexStringLower(hashBytes); + } + + /// + /// 验证文件 SHA256 + /// + public static bool VerifySHA256(string filePath, string expectedHash) + { + var fileHash = ComputeSHA256(filePath); + return string.Equals(fileHash, expectedHash, StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/CRSim.Core/Utils/PluginLoadContext.cs b/CRSim.Core/Utils/PluginLoadContext.cs new file mode 100644 index 0000000..8ce9894 --- /dev/null +++ b/CRSim.Core/Utils/PluginLoadContext.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using System.Runtime.Loader; + +namespace CRSim.Core.Utils +{ + internal class PluginLoadContext(string pluginPath) : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver _resolver = new(pluginPath); + + protected override Assembly Load(AssemblyName assemblyName) + { + string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath != null) + { + return LoadFromAssemblyPath(assemblyPath); + } + + return null; + } + } +} diff --git a/CRSim.ScreenSimulator/Abstractions/IScreenViewModel.cs b/CRSim.ScreenSimulator/Abstractions/IScreenViewModel.cs new file mode 100644 index 0000000..7a961af --- /dev/null +++ b/CRSim.ScreenSimulator/Abstractions/IScreenViewModel.cs @@ -0,0 +1,16 @@ +using CRSim.Core.Abstractions; +using CRSim.Core.Models; +using System.Windows.Threading; + +namespace CRSim.ScreenSimulator.Abstractions +{ + public interface IScreenViewModel + { + ITimeService TimeService { get; } + Dispatcher UIDispatcher { get; set; } + 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/Assets/Ankang/ExitScreen.png b/CRSim.ScreenSimulator/Assets/Ankang/ExitScreen.png deleted file mode 100644 index 3b9035d..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Ankang/ExitScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Beijing/ArrivalScreen.png b/CRSim.ScreenSimulator/Assets/Beijing/ArrivalScreen.png deleted file mode 100644 index 1e24dd9..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Beijing/ArrivalScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/BeijingNan/PrimaryPlatformScreen.png b/CRSim.ScreenSimulator/Assets/BeijingNan/PrimaryPlatformScreen.png deleted file mode 100644 index 9c9fb41..0000000 Binary files a/CRSim.ScreenSimulator/Assets/BeijingNan/PrimaryPlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/BeijingNan/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/BeijingNan/PrimaryScreen.png deleted file mode 100644 index 287bf34..0000000 Binary files a/CRSim.ScreenSimulator/Assets/BeijingNan/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/BeijingNan/SecondaryPlatformScreen.png b/CRSim.ScreenSimulator/Assets/BeijingNan/SecondaryPlatformScreen.png deleted file mode 100644 index 86a2862..0000000 Binary files a/CRSim.ScreenSimulator/Assets/BeijingNan/SecondaryPlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/BeijingNan/TicketCheckScreen.png b/CRSim.ScreenSimulator/Assets/BeijingNan/TicketCheckScreen.png deleted file mode 100644 index d270a40..0000000 Binary files a/CRSim.ScreenSimulator/Assets/BeijingNan/TicketCheckScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/BeijingXi/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/BeijingXi/PrimaryScreen.png deleted file mode 100644 index 0ba64bd..0000000 Binary files a/CRSim.ScreenSimulator/Assets/BeijingXi/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/CRSimIcon.png b/CRSim.ScreenSimulator/Assets/CRSimIcon.png new file mode 100644 index 0000000..d12d2c6 Binary files /dev/null and b/CRSim.ScreenSimulator/Assets/CRSimIcon.png differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduDong/PillarTicketCheckScreen.png b/CRSim.ScreenSimulator/Assets/ChengduDong/PillarTicketCheckScreen.png deleted file mode 100644 index e6a8b97..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduDong/PillarTicketCheckScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduDong/PrimaryTicketCheckScreen.png b/CRSim.ScreenSimulator/Assets/ChengduDong/PrimaryTicketCheckScreen.png deleted file mode 100644 index 2212bfa..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduDong/PrimaryTicketCheckScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduDong/SecondaryScreen.png b/CRSim.ScreenSimulator/Assets/ChengduDong/SecondaryScreen.png deleted file mode 100644 index 1b8e729..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduDong/SecondaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduDong/TicketCheck.png b/CRSim.ScreenSimulator/Assets/ChengduDong/TicketCheck.png deleted file mode 100644 index f029b30..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduDong/TicketCheck.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduDong/cdcz.png b/CRSim.ScreenSimulator/Assets/ChengduDong/cdcz.png deleted file mode 100644 index 81107b6..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduDong/cdcz.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduMetro/ChengduMetro-Icon.png b/CRSim.ScreenSimulator/Assets/ChengduMetro/ChengduMetro-Icon.png deleted file mode 100644 index af0da64..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduMetro/ChengduMetro-Icon.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduMetro/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/ChengduMetro/PlatformScreen.png deleted file mode 100644 index 630c7fd..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduMetro/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/SecondaryScreen.png b/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/SecondaryScreen.png deleted file mode 100644 index 795032a..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/SecondaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/planetakingoff.png b/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/planetakingoff.png deleted file mode 100644 index 0c82085..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/planetakingoff.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/DaqingDong/ConcourseBridgeScreen.png b/CRSim.ScreenSimulator/Assets/DaqingDong/ConcourseBridgeScreen.png deleted file mode 100644 index 0436f63..0000000 Binary files a/CRSim.ScreenSimulator/Assets/DaqingDong/ConcourseBridgeScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/DaqingDong/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/DaqingDong/PlatformScreen.png deleted file mode 100644 index b4b20ca..0000000 Binary files a/CRSim.ScreenSimulator/Assets/DaqingDong/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/DezhouDong/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/DezhouDong/PrimaryScreen.png deleted file mode 100644 index d7da84b..0000000 Binary files a/CRSim.ScreenSimulator/Assets/DezhouDong/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/DezhouDong/TicketCheckScreen.png b/CRSim.ScreenSimulator/Assets/DezhouDong/TicketCheckScreen.png deleted file mode 100644 index 8a5a7ab..0000000 Binary files a/CRSim.ScreenSimulator/Assets/DezhouDong/TicketCheckScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Fuzhou/TicketCheckScreen.png b/CRSim.ScreenSimulator/Assets/Fuzhou/TicketCheckScreen.png deleted file mode 100644 index 35a3994..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Fuzhou/TicketCheckScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/FuzhouNan/TicketCheckScreen.png b/CRSim.ScreenSimulator/Assets/FuzhouNan/TicketCheckScreen.png deleted file mode 100644 index 44f7cef..0000000 Binary files a/CRSim.ScreenSimulator/Assets/FuzhouNan/TicketCheckScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/GuangdongIntercity/SecondaryScreen.png b/CRSim.ScreenSimulator/Assets/GuangdongIntercity/SecondaryScreen.png deleted file mode 100644 index eeaef68..0000000 Binary files a/CRSim.ScreenSimulator/Assets/GuangdongIntercity/SecondaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Guangyuan/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/Guangyuan/PrimaryScreen.png deleted file mode 100644 index eecd34e..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Guangyuan/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Hanzhong/Image.png b/CRSim.ScreenSimulator/Assets/Hanzhong/Image.png deleted file mode 100644 index aa3dc70..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Hanzhong/Image.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Hanzhong/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/Hanzhong/PlatformScreen.png deleted file mode 100644 index f9ae53d..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Hanzhong/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Harbin/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/Harbin/PrimaryScreen.png deleted file mode 100644 index c9cfcb4..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Harbin/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/HarbinXi/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/HarbinXi/PlatformScreen.png deleted file mode 100644 index 6ff203c..0000000 Binary files a/CRSim.ScreenSimulator/Assets/HarbinXi/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Jinanxi/SmallScreen.png b/CRSim.ScreenSimulator/Assets/Jinanxi/SmallScreen.png deleted file mode 100644 index c43e197..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Jinanxi/SmallScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/KunmingChangshuiAirport/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/KunmingChangshuiAirport/PrimaryScreen.png deleted file mode 100644 index a3c51df..0000000 Binary files a/CRSim.ScreenSimulator/Assets/KunmingChangshuiAirport/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Mianyang/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/Mianyang/PlatformScreen.png deleted file mode 100644 index f07d004..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Mianyang/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Niuzhou/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/Niuzhou/PrimaryScreen.png deleted file mode 100644 index 10c01fb..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Niuzhou/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Shanghai/OutsideScreen.png b/CRSim.ScreenSimulator/Assets/Shanghai/OutsideScreen.png deleted file mode 100644 index 5f91de6..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Shanghai/OutsideScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ShenyangBei/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/ShenyangBei/PlatformScreen.png deleted file mode 100644 index 8939c98..0000000 Binary files a/CRSim.ScreenSimulator/Assets/ShenyangBei/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Tianjin/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/Tianjin/PlatformScreen.png deleted file mode 100644 index 3d59501..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Tianjin/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Tianjin/image.png b/CRSim.ScreenSimulator/Assets/Tianjin/image.png deleted file mode 100644 index a7bf60e..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Tianjin/image.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/TianjinMetro/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/TianjinMetro/PlatformScreen.png deleted file mode 100644 index cfde666..0000000 Binary files a/CRSim.ScreenSimulator/Assets/TianjinMetro/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/TianjinMetro/TianjinMetro-Icon.png b/CRSim.ScreenSimulator/Assets/TianjinMetro/TianjinMetro-Icon.png deleted file mode 100644 index 22dd6a3..0000000 Binary files a/CRSim.ScreenSimulator/Assets/TianjinMetro/TianjinMetro-Icon.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/XuzhouMetro/Background.jpg b/CRSim.ScreenSimulator/Assets/XuzhouMetro/Background.jpg deleted file mode 100644 index 77b9d4d..0000000 Binary files a/CRSim.ScreenSimulator/Assets/XuzhouMetro/Background.jpg and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/XuzhouMetro/Metro.png b/CRSim.ScreenSimulator/Assets/XuzhouMetro/Metro.png deleted file mode 100644 index 5c47b77..0000000 Binary files a/CRSim.ScreenSimulator/Assets/XuzhouMetro/Metro.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/XuzhouMetro/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/XuzhouMetro/PlatformScreen.png deleted file mode 100644 index 3f12412..0000000 Binary files a/CRSim.ScreenSimulator/Assets/XuzhouMetro/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/XuzhouMetro/XuzhouMetro-Icon.png b/CRSim.ScreenSimulator/Assets/XuzhouMetro/XuzhouMetro-Icon.png deleted file mode 100644 index ee8306d..0000000 Binary files a/CRSim.ScreenSimulator/Assets/XuzhouMetro/XuzhouMetro-Icon.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Zibo/ConcourseBridgeScreen.png b/CRSim.ScreenSimulator/Assets/Zibo/ConcourseBridgeScreen.png deleted file mode 100644 index ec48b54..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Zibo/ConcourseBridgeScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Zibo/ExitScreen.png b/CRSim.ScreenSimulator/Assets/Zibo/ExitScreen.png deleted file mode 100644 index 034bd39..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Zibo/ExitScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Zibo/PlatformScreen.png b/CRSim.ScreenSimulator/Assets/Zibo/PlatformScreen.png deleted file mode 100644 index de53c91..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Zibo/PlatformScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Zibo/PrimaryScreen.png b/CRSim.ScreenSimulator/Assets/Zibo/PrimaryScreen.png deleted file mode 100644 index 1460e58..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Zibo/PrimaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Zibo/SecondaryScreen.png b/CRSim.ScreenSimulator/Assets/Zibo/SecondaryScreen.png deleted file mode 100644 index 05b8850..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Zibo/SecondaryScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/Zibo/TicketCheckScreen.png b/CRSim.ScreenSimulator/Assets/Zibo/TicketCheckScreen.png deleted file mode 100644 index e0e2090..0000000 Binary files a/CRSim.ScreenSimulator/Assets/Zibo/TicketCheckScreen.png and /dev/null differ diff --git a/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/logo-CR.png b/CRSim.ScreenSimulator/Assets/logo-CR.png similarity index 100% rename from CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/logo-CR.png rename to CRSim.ScreenSimulator/Assets/logo-CR.png diff --git a/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/logo-CRH.png b/CRSim.ScreenSimulator/Assets/logo-CRH.png similarity index 100% rename from CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/logo-CRH.png rename to CRSim.ScreenSimulator/Assets/logo-CRH.png diff --git a/CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/logo-GTJT.png b/CRSim.ScreenSimulator/Assets/logo-GTJT.png similarity index 100% rename from CRSim.ScreenSimulator/Assets/ChengduShuangliuAirport/logo-GTJT.png rename to CRSim.ScreenSimulator/Assets/logo-GTJT.png diff --git a/CRSim.Shared/Converters/DateTimeToStringConverter.cs b/CRSim.ScreenSimulator/Converters/DateTimeToStringConverter.cs similarity index 88% rename from CRSim.Shared/Converters/DateTimeToStringConverter.cs rename to CRSim.ScreenSimulator/Converters/DateTimeToStringConverter.cs index 10a9d5e..440de84 100644 --- a/CRSim.Shared/Converters/DateTimeToStringConverter.cs +++ b/CRSim.ScreenSimulator/Converters/DateTimeToStringConverter.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Windows.Data; -namespace CRSim.Shared.Converters +namespace CRSim.ScreenSimulator.Converters { public class DateTimeToStringConverter : IValueConverter { @@ -13,7 +13,7 @@ namespace CRSim.Shared.Converters if (value is DateTime dateTime && dateTime != new DateTime()) { var CultureInfo = new CultureInfo(Culture); - return dateTime.ToString(Format,CultureInfo); + return dateTime.ToString(Format, CultureInfo); } return "--:--"; } diff --git a/CRSim.Shared/Converters/EmptyToVisibilityConverter.cs b/CRSim.ScreenSimulator/Converters/EmptyToVisibilityConverter.cs similarity index 91% rename from CRSim.Shared/Converters/EmptyToVisibilityConverter.cs rename to CRSim.ScreenSimulator/Converters/EmptyToVisibilityConverter.cs index 0257913..325e113 100644 --- a/CRSim.Shared/Converters/EmptyToVisibilityConverter.cs +++ b/CRSim.ScreenSimulator/Converters/EmptyToVisibilityConverter.cs @@ -2,7 +2,7 @@ using System.Windows; using System.Windows.Data; -namespace CRSim.Shared.Converters +namespace CRSim.ScreenSimulator.Converters { public class EmptyToVisibilityConverter : IValueConverter { @@ -12,7 +12,7 @@ namespace CRSim.Shared.Converters { return string.IsNullOrWhiteSpace(str) ? Visibility.Collapsed : Visibility.Visible; } - if(value is int i) + if (value is int i) { return i == 0 ? Visibility.Collapsed : Visibility.Visible; } diff --git a/CRSim.Shared/Converters/ListToStringConverter.cs b/CRSim.ScreenSimulator/Converters/ListToStringConverter.cs similarity index 94% rename from CRSim.Shared/Converters/ListToStringConverter.cs rename to CRSim.ScreenSimulator/Converters/ListToStringConverter.cs index 9500f67..fceef3d 100644 --- a/CRSim.Shared/Converters/ListToStringConverter.cs +++ b/CRSim.ScreenSimulator/Converters/ListToStringConverter.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Windows.Data; -namespace CRSim.Shared.Converters +namespace CRSim.ScreenSimulator.Converters { public class ListToStringConverter : IValueConverter { diff --git a/CRSim.ScreenSimulator/Converters/MetroDateTimeToStringConverter.cs b/CRSim.ScreenSimulator/Converters/MetroDateTimeToStringConverter.cs index 4349e1a..2adc329 100644 --- a/CRSim.ScreenSimulator/Converters/MetroDateTimeToStringConverter.cs +++ b/CRSim.ScreenSimulator/Converters/MetroDateTimeToStringConverter.cs @@ -1,29 +1,26 @@ -using CRSim.Core.Services; +using CRSim.Core.Abstractions; using CRSim.ScreenSimulator.Models; -using Microsoft.Extensions.DependencyInjection; using System.Globalization; using System.Windows; using System.Windows.Data; namespace CRSim.ScreenSimulator.Converters { - public class MetroDateTimeToStringConverter : IValueConverter + public class MetroDateTimeToStringConverter : IValueConverter, IHasTimeService { - private ITimeService _timeService; + public ITimeService TimeService { get; set; } public string WaitingText { get; set; } = "请耐心等待"; public string ComingText { get; set; } = "即将进站"; public string ArrivedText { get; set; } = "列车进站"; public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - var serviceProvider = (IServiceProvider)Application.Current.Resources["ServiceProvider"]; - _timeService = serviceProvider.GetRequiredService(); if (value is TrainInfo trainInfo && parameter is string para) { if(trainInfo.TrainNumber == null) { return WaitingText; } - var timeDifference = (trainInfo.ArrivalTime ?? DateTime.MinValue) - _timeService.GetDateTimeNow(); + var timeDifference = (trainInfo.ArrivalTime ?? DateTime.MinValue) - TimeService.GetDateTimeNow(); bool isArrivalTimeNull = trainInfo.ArrivalTime == null; var checkInAdvanceDuration = TimeSpan.FromMinutes(1); if (timeDifference < checkInAdvanceDuration) @@ -32,7 +29,7 @@ namespace CRSim.ScreenSimulator.Converters { if (!isArrivalTimeNull) { - if(trainInfo.ArrivalTime - _timeService.GetDateTimeNow() > TimeSpan.Zero) + if(trainInfo.ArrivalTime - TimeService.GetDateTimeNow() > TimeSpan.Zero) { return ComingText; } @@ -43,7 +40,7 @@ namespace CRSim.ScreenSimulator.Converters else { if (para == "StackPanel") - return Math.Round((trainInfo.ArrivalTime - _timeService.GetDateTimeNow()).Value.TotalMinutes).ToString(); + return Math.Round((trainInfo.ArrivalTime - TimeService.GetDateTimeNow()).Value.TotalMinutes).ToString(); } } return string.Empty; diff --git a/CRSim.ScreenSimulator/Converters/MetroDateTimeToVisibilityConverter.cs b/CRSim.ScreenSimulator/Converters/MetroDateTimeToVisibilityConverter.cs index bad6db9..bde7bc0 100644 --- a/CRSim.ScreenSimulator/Converters/MetroDateTimeToVisibilityConverter.cs +++ b/CRSim.ScreenSimulator/Converters/MetroDateTimeToVisibilityConverter.cs @@ -1,4 +1,4 @@ -using CRSim.Core.Services; +using CRSim.Core.Abstractions; using CRSim.ScreenSimulator.Models; using Microsoft.Extensions.DependencyInjection; using System.Globalization; @@ -7,16 +7,14 @@ using System.Windows.Data; namespace CRSim.ScreenSimulator.Converters { - public class MetroDateTimeToVisibilityConverter : IValueConverter + public class MetroDateTimeToVisibilityConverter : IValueConverter, IHasTimeService { - private ITimeService _timeService; + public ITimeService TimeService { get; set; } public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - var serviceProvider = (IServiceProvider)Application.Current.Resources["ServiceProvider"]; - _timeService = serviceProvider.GetRequiredService(); if (value is TrainInfo trainInfo && parameter is string para) { - var timeDifference = (trainInfo.ArrivalTime ?? DateTime.MinValue) - _timeService.GetDateTimeNow(); + var timeDifference = (trainInfo.ArrivalTime ?? DateTime.MinValue) - TimeService.GetDateTimeNow(); var checkInAdvanceDuration = TimeSpan.FromMinutes(1); if (timeDifference < checkInAdvanceDuration) { diff --git a/CRSim.ScreenSimulator/Converters/NumberToImageConverter.cs b/CRSim.ScreenSimulator/Converters/NumberToImageConverter.cs index 60f7d02..993d874 100644 --- a/CRSim.ScreenSimulator/Converters/NumberToImageConverter.cs +++ b/CRSim.ScreenSimulator/Converters/NumberToImageConverter.cs @@ -10,17 +10,17 @@ namespace CRSim.ScreenSimulator.Converters if (value == null) return null; string trainNumber = value.ToString(); - string imageName = "pack://application:,,,/CRSim.ScreenSimulator;component/Assets/ChengduShuangliuAirport/logo-GTJT.png"; + string imageName = "pack://application:,,,/CRSim.ScreenSimulator;component/Assets/logo-GTJT.png"; if (!string.IsNullOrEmpty(trainNumber) && (trainNumber.StartsWith('G') || trainNumber.StartsWith('C') || trainNumber.StartsWith('D'))) { int probability = trainNumber.GetHashCode() % 2; if (probability == 1) { - imageName = "pack://application:,,,/CRSim.ScreenSimulator;component/Assets/ChengduShuangliuAirport/logo-CR.png"; + imageName = "pack://application:,,,/CRSim.ScreenSimulator;component/Assets/logo-CR.png"; } else { - imageName = "pack://application:,,,/CRSim.ScreenSimulator;component/Assets/ChengduShuangliuAirport/logo-CRH.png"; + imageName = "pack://application:,,,/CRSim.ScreenSimulator;component/Assets/logo-CRH.png"; } } var image = new System.Windows.Media.Imaging.BitmapImage(); diff --git a/CRSim.ScreenSimulator/Converters/TrainInfoToTicketCheckTimeConverter.cs b/CRSim.ScreenSimulator/Converters/TrainInfoToTicketCheckTimeConverter.cs index e50885e..66f6cae 100644 --- a/CRSim.ScreenSimulator/Converters/TrainInfoToTicketCheckTimeConverter.cs +++ b/CRSim.ScreenSimulator/Converters/TrainInfoToTicketCheckTimeConverter.cs @@ -1,23 +1,20 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; +using CRSim.Core.Abstractions; +using CRSim.Core.Models; using CRSim.ScreenSimulator.Models; using Microsoft.Extensions.DependencyInjection; using System.Globalization; -using System.Windows; using System.Windows.Data; namespace CRSim.ScreenSimulator.Converters { public class TrainInfoToTicketCheckTimeConverter : IValueConverter { - private ITimeService _timeService; private Settings _settings; public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is TrainInfo trainInfo && trainInfo.DepartureTime!=null) { - var serviceProvider = (IServiceProvider)Application.Current.Resources["ServiceProvider"]; - _timeService = serviceProvider.GetRequiredService(); + var serviceProvider = StyleManager.ServiceProvider; _settings = serviceProvider.GetRequiredService().GetSettings(); if (trainInfo.ArrivalTime == null) { diff --git a/CRSim.ScreenSimulator/Converters/TrainStateColorConverter.cs b/CRSim.ScreenSimulator/Converters/TrainStateColorConverter.cs index 1e8c7d6..702888d 100644 --- a/CRSim.ScreenSimulator/Converters/TrainStateColorConverter.cs +++ b/CRSim.ScreenSimulator/Converters/TrainStateColorConverter.cs @@ -1,85 +1,88 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; +using CRSim.Core.Abstractions; +using CRSim.Core.Models; using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; using System.Globalization; -using System.Windows; using System.Windows.Data; using System.Windows.Media; + namespace CRSim.ScreenSimulator.Converters { - public class TrainStateColorConverter : IMultiValueConverter + public class TrainStateColorConverter : IMultiValueConverter, IHasTimeService { - private ITimeService _timeService; + public ITimeService TimeService { get; set; } private Settings _settings; + public string DisplayMode { get; set; } = "Normal"; - /* - Normal: 标准显示。 - Alternating_Row_Colors: 候车状态隔行异色显示(第4个参数控制行号)。 - Arrive: 到达屏显示 - */ - - public List WaitingColorList { get; set; } = []; + public List WaitingColorList { get; set; } = new(); public SolidColorBrush ArrivedText { get; set; } = new(Colors.LightGreen); public SolidColorBrush ArrivingText { get; set; } = new(Colors.White); public SolidColorBrush WaitingColor { get; set; } = new(Colors.White); public SolidColorBrush CheckInColor { get; set; } = new(Colors.LightGreen); public SolidColorBrush StopCheckInColor { get; set; } = new(Colors.Red); - object IMultiValueConverter.Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - var serviceProvider = (IServiceProvider)Application.Current.Resources["ServiceProvider"]; - _timeService = serviceProvider.GetRequiredService(); - _settings = serviceProvider.GetRequiredService().GetSettings(); - if (DisplayMode == "Arrive" || ( values.Length > 1 && values[1] == null)) + _settings ??= StyleManager.ServiceProvider + .GetRequiredService() + .GetSettings(); + + var now = TimeService.GetDateTimeNow(); + + // 到达模式或缺少关键数据 + if (DisplayMode == "Arrive" || (values.Length > 1 && values[1] == null)) { if (values[0] is DateTime arriveTime) - { - return _timeService.GetDateTimeNow() >= arriveTime ? ArrivedText : ArrivingText; - } - return string.Empty; + return now >= arriveTime ? ArrivedText : ArrivingText; + return new SolidColorBrush(Colors.Transparent); } - if (values[0] != null && values[1] == null) + + // 列车停运 + if (values[2] is null) + return StopCheckInColor; + + // 正点/晚点判断 + if (values[0] != null && values[1] == null && values[2] is TimeSpan status) + return GetStatusColor(status); + + // 有出发时间 + if (values[1] is DateTime departureTime && departureTime != DateTime.MinValue) { - return WaitingColor; - } - if (values[1] is DateTime departureTime && departureTime !=new DateTime()) - { - if (values[0] is DateTime) - { - //过路站 - if (_timeService.GetDateTimeNow() > departureTime.Subtract(_settings.PassingCheckInAdvanceDuration) && _timeService.GetDateTimeNow() < departureTime.Subtract(_settings.StopCheckInAdvanceDuration)) - { - return CheckInColor; - } - } - else - { - //始发站 - if (_timeService.GetDateTimeNow() > departureTime.Subtract(_settings.DepartureCheckInAdvanceDuration) && _timeService.GetDateTimeNow() < departureTime.Subtract(_settings.StopCheckInAdvanceDuration)) - { - return CheckInColor; - } - } - if (_timeService.GetDateTimeNow() > departureTime.Subtract(_settings.StopCheckInAdvanceDuration)) - { + bool isPassingStation = values[0] is DateTime; + var checkInDuration = isPassingStation ? _settings.PassingCheckInAdvanceDuration : _settings.DepartureCheckInAdvanceDuration; + + // 候车中/检票中 + if (now > departureTime - checkInDuration && now < departureTime - _settings.StopCheckInAdvanceDuration) + return CheckInColor; + + // 停止检票 + if (now >= departureTime - _settings.StopCheckInAdvanceDuration) return StopCheckInColor; - } - if (values[2] is TimeSpan state && state.TotalMinutes > 0) - { - return StopCheckInColor; - } - if (DisplayMode == "Alternating_Row_Colors" && values.Length > 3 && values[3] is int rowNumber){ - return WaitingColorList[rowNumber % WaitingColorList.Count]; - } - return WaitingColor; + + // 正点/晚点 + if (values[2] is TimeSpan status2) + return GetStatusColor(status2, values.Length > 3 && values[3] is int row ? row : -1); } + return new SolidColorBrush(Colors.Transparent); } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + private SolidColorBrush GetStatusColor(TimeSpan status, int rowNumber = -1) { - // 通常不需要实现 ConvertBack - return null; + if (status == TimeSpan.Zero) + { + if (DisplayMode == "Alternating_Row_Colors" && rowNumber >= 0 && WaitingColorList.Count > 0) + return WaitingColorList[rowNumber % WaitingColorList.Count]; + return WaitingColor; // 正点 + } + else + { + return StopCheckInColor; // 晚点 + } } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => null; } } diff --git a/CRSim.ScreenSimulator/Converters/TrainStateConverter.cs b/CRSim.ScreenSimulator/Converters/TrainStateConverter.cs index 43c3ddf..a2bb742 100644 --- a/CRSim.ScreenSimulator/Converters/TrainStateConverter.cs +++ b/CRSim.ScreenSimulator/Converters/TrainStateConverter.cs @@ -1,15 +1,14 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; +using CRSim.Core.Abstractions; +using CRSim.Core.Models; using Microsoft.Extensions.DependencyInjection; using System.Globalization; -using System.Windows; using System.Windows.Data; namespace CRSim.ScreenSimulator.Converters { - public class TrainStateConverter : IMultiValueConverter + public class TrainStateConverter : IMultiValueConverter, IHasTimeService { - private ITimeService _timeService; + public ITimeService TimeService { get; set; } private Settings _settings; public string DisplayMode { get; set; } = "Normal"; public string ArrivedText { get; set; } = "列车已到达"; @@ -17,49 +16,65 @@ namespace CRSim.ScreenSimulator.Converters public string WaitingText { get; set; } = "候车"; public string CheckInText { get; set; } = "正在检票"; public string StopCheckInText { get; set; } = "停止检票"; - object IMultiValueConverter.Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) + public string SuspendText { get; set; } = "列车停运"; + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - var serviceProvider = (IServiceProvider)Application.Current.Resources["ServiceProvider"]; - _timeService = serviceProvider.GetRequiredService(); - _settings = serviceProvider.GetRequiredService().GetSettings(); - if (DisplayMode == "Arrive" || ( values.Length > 1 && values[1] == null)) + _settings ??= StyleManager.ServiceProvider + .GetRequiredService() + .GetSettings(); + + var now = TimeService.GetDateTimeNow(); + + // 到达模式或关键数据缺失 + if (DisplayMode == "Arrive" || (values.Length > 1 && values[1] == null)) { if (values[0] is DateTime arriveTime) - { - return _timeService.GetDateTimeNow() >= arriveTime ? ArrivedText : ArrivingText; - } + return now >= arriveTime ? ArrivedText : ArrivingText; return string.Empty; } - if (values[1] is DateTime departureTime && departureTime != new DateTime()) + + // 停运列车 + if (values[2] is null) + return SuspendText; + + // 有出发时间 + if (values[1] is DateTime departureTime && departureTime != DateTime.MinValue) { - if (values[0] is DateTime) - { - //过路站 - if (_timeService.GetDateTimeNow() > departureTime.Subtract(_settings.PassingCheckInAdvanceDuration) && _timeService.GetDateTimeNow() < departureTime.Subtract(_settings.StopCheckInAdvanceDuration)) - { - return CheckInText; - } - } - else - { - //始发站 - if (_timeService.GetDateTimeNow() > departureTime.Subtract(_settings.DepartureCheckInAdvanceDuration) && _timeService.GetDateTimeNow() < departureTime.Subtract(_settings.StopCheckInAdvanceDuration)) - { - return CheckInText; - } - } - if (_timeService.GetDateTimeNow() > departureTime.Subtract(_settings.StopCheckInAdvanceDuration)) - { + bool isPassingStation = values[0] is DateTime; + var checkInDuration = isPassingStation ? _settings.PassingCheckInAdvanceDuration : _settings.DepartureCheckInAdvanceDuration; + + // 检票中 + if (now > departureTime - checkInDuration && now < departureTime - _settings.StopCheckInAdvanceDuration) + return CheckInText; + + // 停止检票 + if (now >= departureTime - _settings.StopCheckInAdvanceDuration) return StopCheckInText; - } - if (values[2] is TimeSpan state && state.TotalMinutes > 0) + + // 晚点 / 正点 + if (values[2] is TimeSpan state) { - return $"预计开点{departureTime.Add(state):HH:mm}";//晚点列车 + if (state.TotalMinutes > 0) + return $"晚点约{ToHourMinuteString(state)}"; // 晚点 + return WaitingText; // 正点 } - return WaitingText; } + return string.Empty; } + public static string ToHourMinuteString(TimeSpan ts) + { + int totalHours = (int)ts.TotalHours; // 可以超过24 + int minutes = ts.Minutes; + + if (totalHours > 0 && minutes > 0) + return $"{totalHours}小时{minutes}分钟"; + if (totalHours > 0) + return $"{totalHours}小时"; + if (minutes > 0) + return $"{minutes}分钟"; + return "0分钟"; // 特殊情况,完全为0 + } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { diff --git a/CRSim.ScreenSimulator/Models/ScreenInfo.cs b/CRSim.ScreenSimulator/Models/ScreenInfo.cs deleted file mode 100644 index c8ee4ec..0000000 --- a/CRSim.ScreenSimulator/Models/ScreenInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace CRSim.ScreenSimulator.Models -{ - public class ScreenInfo - { - public required Guid SessionID { get; set; } - public required string SelectedStyle { get; set; } - public string? Text { get; set; } - public string? Video { get; set; } - public string? SelectedStationName { get; set; } - public string? SelectedTicketCheck { get; set; } - public string? SelectedPlatformName { get; set; } - public int? SelectedLoaction { get; set; } - } -} diff --git a/CRSim.ScreenSimulator/Models/Session.cs b/CRSim.ScreenSimulator/Models/Session.cs new file mode 100644 index 0000000..6665abd --- /dev/null +++ b/CRSim.ScreenSimulator/Models/Session.cs @@ -0,0 +1,17 @@ +using CRSim.Core.Models; + +namespace CRSim.ScreenSimulator.Models +{ + public class Session + { + public required Guid ID { get; set; } + public required string StyleName { get; set; } + public required DateTime SimulateTime { get; set; } + public string? Text { get; set; } + public Uri? Video { get; set; } + public Station? Station { get; set; } + public TicketCheck? TicketCheck { get; set; } + public string? PlatformName { get; set; } + public int? Loaction { get; set; } + } +} diff --git a/CRSim.ScreenSimulator/Models/StylesInfoDataSource.cs b/CRSim.ScreenSimulator/Models/StylesInfoDataSource.cs deleted file mode 100644 index a26cd04..0000000 --- a/CRSim.ScreenSimulator/Models/StylesInfoDataSource.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Reflection; -using System.Text.Json; - -namespace CRSim.ScreenSimulator.Models -{ - public class StylesInfoDataItem - { - public string UniqueId { get; set; } - public string Title { get; set; } - public string Region { get; set; } - public string Author { get; set; } - public string Type { get; set; } - public Type ViewType - { - get - { - string type = $"CRSim.ScreenSimulator.Views.{UniqueId}View"; - return Assembly.Load("CRSim.ScreenSimulator").GetType(type); - } - } - - public Uri ImageSource - { - get - { - return new Uri($"pack://application:,,,/CRSim.ScreenSimulator;component/Assets/{UniqueId.Replace('.', '/')}.png"); - } - } - - public List Parameters { get; set; } = []; - - public override string ToString() - { - return Title; - } - } - - public sealed class StylesInfoDataSource - { - - #region Singleton - - private static readonly StylesInfoDataSource _instance; - - public static StylesInfoDataSource Instance - { - get - { - return _instance; - } - } - - static StylesInfoDataSource() - { - _instance = new StylesInfoDataSource(); - } - - private StylesInfoDataSource() - { - var jsonText = ReadStylesData(); - StylesInfo = JsonSerializer.Deserialize>(jsonText); - } - - #endregion - - public ICollection StylesInfo { get; } - - private static string ReadStylesData() - { - var assembly = typeof(StylesInfoDataSource).Assembly; - var resourceName = "CRSim.ScreenSimulator.Models.StylesInfoData.json"; - - using var stream = assembly.GetManifestResourceStream(resourceName); - using var reader = new System.IO.StreamReader(stream); - return reader.ReadToEnd(); - } - - public ICollection GetParametersInfo(Type type) - { - return StylesInfo.FirstOrDefault(x => x.ViewType == type)?.Parameters; - } - - public string GetStyleName(Type type) - { - return StylesInfo.FirstOrDefault(x => x.ViewType == type)?.Title; - } - } -} diff --git a/CRSim.ScreenSimulator/Models/TrainInfo.cs b/CRSim.ScreenSimulator/Models/TrainInfo.cs index 1d01f87..6283130 100644 --- a/CRSim.ScreenSimulator/Models/TrainInfo.cs +++ b/CRSim.ScreenSimulator/Models/TrainInfo.cs @@ -5,12 +5,34 @@ public string TrainNumber { get; set; } public string Origin { get; set; } public string Terminal { get; set; } - public DateTime? ArrivalTime { get; set; } - public DateTime? DepartureTime { get; set; } + private DateTime? arrivalTime { get; set; } + private DateTime? departureTime { get; set; } + public DateTime? ArrivalTime + { + get + { + return State is null ? arrivalTime : arrivalTime + State; + } + set + { + arrivalTime = value; + } + } + public DateTime? DepartureTime + { + get + { + return State is null ? departureTime : departureTime + State; + } + set + { + departureTime = value; + } + } public List TicketChecks { get; set; } public string WaitingArea { get; set; } public string Platform { get; set; } - public TimeSpan State { get; set; } + public TimeSpan? State { get; set; } public string? Landmark { get; set; } public int Length { get; set; } } diff --git a/CRSim.ScreenSimulator/Services/ServiceCollectionExtensions.cs b/CRSim.ScreenSimulator/Services/ServiceCollectionExtensions.cs deleted file mode 100644 index 48fe334..0000000 --- a/CRSim.ScreenSimulator/Services/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using CRSim.ScreenSimulator.Models; -using Microsoft.Extensions.DependencyInjection; - -namespace CRSim.ScreenSimulator.Services -{ - public static class ServiceCollectionExtensions - { - public static IServiceCollection AddScreenSimulatorServices(this IServiceCollection services) - { - foreach (var uniqueId in StylesInfoDataSource.Instance.StylesInfo.Select(x => x.UniqueId)) - { - var viewModelType = Type.GetType($"CRSim.ScreenSimulator.ViewModels.{uniqueId}ViewModel"); - if (viewModelType != null) - { - services.AddTransient(viewModelType); - } - - var viewType = Type.GetType($"CRSim.ScreenSimulator.Views.{uniqueId}View"); - if (viewType != null) - { - services.AddTransient(viewType); - } - } - return services; - } - } -} diff --git a/CRSim.ScreenSimulator/StyleManager.cs b/CRSim.ScreenSimulator/StyleManager.cs new file mode 100644 index 0000000..a20c05e --- /dev/null +++ b/CRSim.ScreenSimulator/StyleManager.cs @@ -0,0 +1,68 @@ +using CRSim.Core.Abstractions; +using CRSim.Core.Enums; +using CRSim.Core.Models.Plugin; +using CRSim.ScreenSimulator.Abstractions; +using CRSim.ScreenSimulator.Models; +using CRSim.ScreenSimulator.Views; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Windows.Controls; +using Windows.System; + +namespace CRSim.ScreenSimulator +{ + public class StyleManager + { + public List StyleInfos => [.. IPluginService.LoadedPlugins.Where(x => x.Manifest.Type == "ScreenStyle" && x.LoadStatus == PluginLoadStatus.Loaded)]; + + public static IServiceProvider ServiceProvider { get; set; } + + public ObservableCollection ActiveWindows { get; set; } = []; + + public DispatcherQueue DispatcherQueue { get; set; } = DispatcherQueue.GetForCurrentThread(); + + public StyleManager(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + public void ShowWindow(Type page, Session session) + { + Thread thread = new(() => + { + Page viewInstance = (Page)ServiceProvider.GetService(page); + if (viewInstance.DataContext is IScreenViewModel viewModel) + { + var timeService = viewModel.TimeService; + timeService.SimulateTime = session.SimulateTime; + timeService.Start(); + + viewModel.UIDispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; + if (session.Text != null) + { + viewModel.Text = session.Text; + } + if (session.Loaction != null && session.Loaction != 0) + { + viewModel.Location = session.Loaction.Value; + } + if (session.Video != null) + { + viewModel.Video = session.Video; + } + viewModel.LoadData(session.Station, session.TicketCheck, session.PlatformName); + var win = new SimulatorWindow(viewInstance, timeService, this, session); + DispatcherQueue.TryEnqueue(() => + { + ActiveWindows.Add(win); + }); + win.Show(); + System.Windows.Threading.Dispatcher.Run(); + } + }); + + thread.SetApartmentState(ApartmentState.STA); + thread.IsBackground = true; + thread.Start(); + } + } +} diff --git a/CRSim.ScreenSimulator/ViewModels/Ankang/ExitScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Ankang/ExitScreenViewModel.cs deleted file mode 100644 index 7aa688b..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Ankang/ExitScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Services; -using CRSim.Core.Models; -namespace CRSim.ScreenSimulator.ViewModels.Ankang -{ - public class ExitScreenViewModel : ScreenViewModel - { - public ExitScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"西安局集团公司推出西铁行APP站车服务产品,欢迎体验使用。"; - ItemsPerPage = 7; - ScreenCount = 1; - StationType = StationType.Arrival; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/ScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/BaseScreenViewModel.cs similarity index 80% rename from CRSim.ScreenSimulator/ViewModels/ScreenViewModel.cs rename to CRSim.ScreenSimulator/ViewModels/BaseScreenViewModel.cs index 7365baf..abc3803 100644 --- a/CRSim.ScreenSimulator/ViewModels/ScreenViewModel.cs +++ b/CRSim.ScreenSimulator/ViewModels/BaseScreenViewModel.cs @@ -1,15 +1,17 @@ using CommunityToolkit.Mvvm.ComponentModel; -using CRSim.Core.Services; +using CRSim.Core.Abstractions; using CRSim.Core.Models; +using CRSim.ScreenSimulator.Abstractions; using CRSim.ScreenSimulator.Models; -using System.Windows; using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; namespace CRSim.ScreenSimulator.ViewModels { - public partial class ScreenViewModel : ObservableObject + public partial class BaseScreenViewModel : ObservableObject,IScreenViewModel { - public readonly ITimeService _timeService; + public ITimeService TimeService { get; set; } public readonly Settings _settings; public readonly TaskCompletionSource DataLoaded = new(); [ObservableProperty] @@ -24,23 +26,26 @@ namespace CRSim.ScreenSimulator.ViewModels private string _thisPlatform; [ObservableProperty] private string _thisTicketCheck; + [ObservableProperty] + private Uri _video; public ObservableCollection ScreenA { get; private set; } = []; - public ObservableCollection ScreenB { get; private set; } = []; + public ObservableCollection ScreenB { get; private set; } = []; + public System.Windows.Threading.Dispatcher UIDispatcher { get; set; } public List TrainInfo { get; set; } = []; public StationType StationType = StationType.Both; - protected ScreenViewModel(ITimeService timeService, ISettingsService settingsService) + protected BaseScreenViewModel(ITimeService timeService, ISettingsService settingsService) { - _timeService = timeService; + TimeService = timeService; _settings = settingsService.GetSettings(); - _timeService.OneSecondElapsed += OnTimeElapsed; + TimeService.OneSecondElapsed += OnTimeElapsed; - _timeService.RefreshSecondsElapsed += RefreshData; - _timeService.RefreshSecondsElapsed += RefreshDisplay; + TimeService.RefreshSecondsElapsed += RefreshData; + TimeService.RefreshSecondsElapsed += RefreshDisplay; Initialize(); } [ObservableProperty] @@ -73,14 +78,14 @@ namespace CRSim.ScreenSimulator.ViewModels { if (trainInfo.DepartureTime == null) { - if (trainInfo.ArrivalTime.Value.Add(_settings.StopDisplayFromArrivalDuration) < _timeService.GetDateTimeNow()) + if (trainInfo.ArrivalTime.Value.Add(_settings.StopDisplayFromArrivalDuration) < TimeService.GetDateTimeNow()) { itemsToRemove.Add(trainInfo); } } else { - if ((StationType == StationType.Arrival || StationType == StationType.Both ? trainInfo.DepartureTime.Value : trainInfo.DepartureTime.Value.Subtract(_settings.StopDisplayUntilDepartureDuration)) < _timeService.GetDateTimeNow()) + if ((StationType == StationType.Arrival || StationType == StationType.Both ? trainInfo.DepartureTime.Value : trainInfo.DepartureTime.Value.Subtract(_settings.StopDisplayUntilDepartureDuration)) < TimeService.GetDateTimeNow()) { itemsToRemove.Add(trainInfo); } @@ -95,14 +100,16 @@ namespace CRSim.ScreenSimulator.ViewModels private void OnTimeElapsed(object? sender, EventArgs e) { - CurrentTime = _timeService.GetDateTimeNow(); + CurrentTime = TimeService.GetDateTimeNow(); + OnPropertyChanged(nameof(ScreenA)); + 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) { @@ -111,10 +118,10 @@ 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(); + var now = TimeService.GetDateTimeNow(); var today = now.Date; if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime??trainNumber.ArrivalTime)!.Value) < now) @@ -132,12 +139,15 @@ 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, - State = TimeSpan.Zero + State = trainNumber.Status }); } @@ -155,7 +165,7 @@ namespace CRSim.ScreenSimulator.ViewModels } public virtual void RefreshDisplay(object? sender, EventArgs e) { - Application.Current.Dispatcher.Invoke(() => + UIDispatcher.Invoke(() => { if (ScreenCount == null) { @@ -163,7 +173,7 @@ namespace CRSim.ScreenSimulator.ViewModels { ScreenA.Add(TrainInfo.Count > i ? TrainInfo[i] : new()); } - if(ScreenA.Count > ItemsPerPage) + if (ScreenA.Count > ItemsPerPage) { for (int i = 0; i < ItemsPerPage; i++) { diff --git a/CRSim.ScreenSimulator/ViewModels/Beijing/ArrivalScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Beijing/ArrivalScreenViewModel.cs deleted file mode 100644 index 6f84316..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Beijing/ArrivalScreenViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Beijing -{ - public class ArrivalScreenViewModel : ScreenViewModel - { - public ArrivalScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 2; - StationType = StationType.Arrival; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/BeijingNan/PrimaryPlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/BeijingNan/PrimaryPlatformScreenViewModel.cs deleted file mode 100644 index dde1e42..0000000 --- a/CRSim.ScreenSimulator/ViewModels/BeijingNan/PrimaryPlatformScreenViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.BeijingNan -{ - public class PrimaryPlatformScreenViewModel : ScreenViewModel - { - public PrimaryPlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/BeijingNan/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/BeijingNan/PrimaryScreenViewModel.cs deleted file mode 100644 index a657111..0000000 --- a/CRSim.ScreenSimulator/ViewModels/BeijingNan/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.BeijingNan -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 10; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/BeijingNan/SecondaryPlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/BeijingNan/SecondaryPlatformScreenViewModel.cs deleted file mode 100644 index 24f2638..0000000 --- a/CRSim.ScreenSimulator/ViewModels/BeijingNan/SecondaryPlatformScreenViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.BeijingNan -{ - public class SecondaryPlatformScreenViewModel : ScreenViewModel - { - public SecondaryPlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/BeijingNan/TicketCheckScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/BeijingNan/TicketCheckScreenViewModel.cs deleted file mode 100644 index db134d7..0000000 --- a/CRSim.ScreenSimulator/ViewModels/BeijingNan/TicketCheckScreenViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.BeijingNan -{ - public class TicketCheckScreenViewModel : ScreenViewModel - { - public TicketCheckScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 2; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/BeijingXi/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/BeijingXi/PrimaryScreenViewModel.cs deleted file mode 100644 index b9523e4..0000000 --- a/CRSim.ScreenSimulator/ViewModels/BeijingXi/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.BeijingXi -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 13; - ScreenCount = 2; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/ChengduDong/PillarTicketCheckScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/ChengduDong/PillarTicketCheckScreenViewModel.cs deleted file mode 100644 index 811f9d0..0000000 --- a/CRSim.ScreenSimulator/ViewModels/ChengduDong/PillarTicketCheckScreenViewModel.cs +++ /dev/null @@ -1,59 +0,0 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CRSim.Core.Models; -using CRSim.Core.Services; -using CRSim.ScreenSimulator.Models; -using System.Windows; -namespace CRSim.ScreenSimulator.ViewModels.ChengduDong -{ - public partial class PillarTicketCheckScreenViewModel : ScreenViewModel - { - [ObservableProperty] - private TrainInfo _firstTrain = new(); - [ObservableProperty] - private TrainInfo _secondTrain = new(); - [ObservableProperty] - private TrainInfo _checkingTrain = new(); - public PillarTicketCheckScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - StationType = StationType.Departure; - } - public override void RefreshDisplay(object? sender, EventArgs e) - { - Application.Current.Dispatcher.Invoke(() => - { - if (TrainInfo.Count == 0) - { - return; - } - var now = _timeService.GetDateTimeNow(); - var departureTime = TrainInfo[0].DepartureTime!.Value; - - TimeSpan checkInStartOffset = TrainInfo[0].ArrivalTime is DateTime - ? _settings.PassingCheckInAdvanceDuration // 过路站 - : _settings.DepartureCheckInAdvanceDuration; // 始发站 - - bool checking = now > departureTime - checkInStartOffset && now < departureTime - _settings.StopCheckInAdvanceDuration; - - if (checking) - { - CheckingTrain = TrainInfo[0]; - FirstTrain = TrainInfo.ElementAtOrDefault(1); - SecondTrain = TrainInfo.ElementAtOrDefault(2); - } - else - { - CheckingTrain = null; - if(now < departureTime && now > departureTime - _settings.StopCheckInAdvanceDuration) - { - FirstTrain = TrainInfo.ElementAtOrDefault(1); - SecondTrain = TrainInfo.ElementAtOrDefault(2); - return; - } - FirstTrain = TrainInfo.ElementAtOrDefault(0); - SecondTrain = TrainInfo.ElementAtOrDefault(1); - } - }); - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/ChengduDong/PrimaryTicketCheckScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/ChengduDong/PrimaryTicketCheckScreenViewModel.cs deleted file mode 100644 index 3dd477f..0000000 --- a/CRSim.ScreenSimulator/ViewModels/ChengduDong/PrimaryTicketCheckScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.ChengduDong -{ - public class PrimaryTicketCheckScreenViewModel : ScreenViewModel - { - public PrimaryTicketCheckScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = "持户口簿、护照、临时身份证的旅客,请在非居民身份证通道排队。请主动礼让老、弱、病、残、孕的旅客。"; - ItemsPerPage = 2; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/ChengduDong/SecondaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/ChengduDong/SecondaryScreenViewModel.cs deleted file mode 100644 index 9e04ecc..0000000 --- a/CRSim.ScreenSimulator/ViewModels/ChengduDong/SecondaryScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.ChengduDong -{ - public class SecondaryScreenViewModel : ScreenViewModel - { - public SecondaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = " 检票口信息请以车站显示屏信息为准。公共场所请妥善保管个人贵重物品。"; - ItemsPerPage = 12; - ScreenCount = 2; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/ChengduMetro/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/ChengduMetro/PlatformScreenViewModel.cs deleted file mode 100644 index d598589..0000000 --- a/CRSim.ScreenSimulator/ViewModels/ChengduMetro/PlatformScreenViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.ChengduMetro -{ - public class PlatformScreenViewModel : MetroPlatformScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService,settingsService) - { - Text = "不吃火锅,就吃烤匠。 建设成渝地区双城经济圈,共同唱好新时代西部“双城记”。 2018年2月8日,复兴号列车首次担当G89次列车,正式开进四川,标志着“复兴号”高速列车正式走上蜀道。然而,在行驶至西安北站时,发生了突发设备故障,车轴出现过热报警,导致车号为CR400BF-5033的复兴号列车被迫停运并进行检修。相关工作人员迅速展开故障排查,并确保乘客安全无虞。"; - Video = new("Assets\\Advertisement-1.mp4", UriKind.Relative); - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/ChengduShuangliuAirport/SecondaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/ChengduShuangliuAirport/SecondaryScreenViewModel.cs deleted file mode 100644 index 91a545c..0000000 --- a/CRSim.ScreenSimulator/ViewModels/ChengduShuangliuAirport/SecondaryScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.ChengduShuangliuAirport -{ - public class SecondaryScreenViewModel : ScreenViewModel - { - public SecondaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"国内航班截载时间为起飞前{settingsService.GetSettings().StopCheckInAdvanceDuration.TotalMinutes}分钟。"; - ItemsPerPage = 7; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/DaqingDong/ConcourseBridgeScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/DaqingDong/ConcourseBridgeScreenViewModel.cs deleted file mode 100644 index d3ee144..0000000 --- a/CRSim.ScreenSimulator/ViewModels/DaqingDong/ConcourseBridgeScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.DaqingDong -{ - public class ConcourseBridgeScreenViewModel : ScreenViewModel - { - public ConcourseBridgeScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 3; - StationType = StationType.Departure; - timeService.RefreshSecondsElapsed += RefreshDisplay; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/DaqingDong/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/DaqingDong/PlatformScreenViewModel.cs deleted file mode 100644 index 1e7fb8a..0000000 --- a/CRSim.ScreenSimulator/ViewModels/DaqingDong/PlatformScreenViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.DaqingDong -{ - public class PlatformScreenViewModel : ScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 1; - Text = $"请站在白色安全线以内排队候车"; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/DezhouDong/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/DezhouDong/PrimaryScreenViewModel.cs deleted file mode 100644 index 76f216f..0000000 --- a/CRSim.ScreenSimulator/ViewModels/DezhouDong/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.DezhouDong -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 10; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/DezhouDong/TicketCheckScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/DezhouDong/TicketCheckScreenViewModel.cs deleted file mode 100644 index e5c5e12..0000000 --- a/CRSim.ScreenSimulator/ViewModels/DezhouDong/TicketCheckScreenViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.Core.Services; -using CRSim.Core.Models; -namespace CRSim.ScreenSimulator.ViewModels.DezhouDong -{ - public class TicketCheckScreenViewModel : ScreenViewModel - { - public TicketCheckScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 3; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Fuzhou/TicketCheckScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Fuzhou/TicketCheckScreenViewModel.cs deleted file mode 100644 index 94cf586..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Fuzhou/TicketCheckScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Services; -using CRSim.Core.Models; -namespace CRSim.ScreenSimulator.ViewModels.Fuzhou -{ - public class TicketCheckScreenViewModel : ScreenViewModel - { - public TicketCheckScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"开车前{settingsService.GetSettings().StopCheckInAdvanceDuration.TotalMinutes}分钟停止检票"; - ItemsPerPage = 4; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/FuzhouNan/TicketCheckScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/FuzhouNan/TicketCheckScreenViewModel.cs deleted file mode 100644 index 7e8e53c..0000000 --- a/CRSim.ScreenSimulator/ViewModels/FuzhouNan/TicketCheckScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Services; -using CRSim.Core.Models; -namespace CRSim.ScreenSimulator.ViewModels.FuzhouNan -{ - public class TicketCheckScreenViewModel : ScreenViewModel - { - public TicketCheckScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"开车前{settingsService.GetSettings().StopCheckInAdvanceDuration.TotalMinutes}分钟停止检票"; - ItemsPerPage = 6; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/GuangdongIntercity/SecondaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/GuangdongIntercity/SecondaryScreenViewModel.cs deleted file mode 100644 index a974a70..0000000 --- a/CRSim.ScreenSimulator/ViewModels/GuangdongIntercity/SecondaryScreenViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.GuangdongIntercity -{ - public class SecondaryScreenViewModel : ScreenViewModel - { - public SecondaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"列 车 到 发 信 息"; - ItemsPerPage = 8; - ScreenCount = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Guangyuan/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Guangyuan/PrimaryScreenViewModel.cs deleted file mode 100644 index a55d7e3..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Guangyuan/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Services; -using CRSim.Core.Models; -namespace CRSim.ScreenSimulator.ViewModels.Guangyuan -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = "成都铁路客服电话:028-12306"; - ItemsPerPage = 12; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Hanzhong/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Hanzhong/PlatformScreenViewModel.cs deleted file mode 100644 index d925ae3..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Hanzhong/PlatformScreenViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Hanzhong -{ - public class PlatformScreenViewModel : ScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 1; - Text = "请注意列车与站台之间的间隙,请勿携带危险品上车"; - } - } -} \ No newline at end of file diff --git a/CRSim.ScreenSimulator/ViewModels/Harbin/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Harbin/PrimaryScreenViewModel.cs deleted file mode 100644 index 3c057ec..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Harbin/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Harbin -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"开车前{settingsService.GetSettings().StopCheckInAdvanceDuration.TotalMinutes}分钟停止检票"; - ItemsPerPage = 6; - ScreenCount = 2; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/HarbinXi/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/HarbinXi/PlatformScreenViewModel.cs deleted file mode 100644 index 2c4b38d..0000000 --- a/CRSim.ScreenSimulator/ViewModels/HarbinXi/PlatformScreenViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.HarbinXi -{ - public class PlatformScreenViewModel : ScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"请在白线内行走,乘降列车时注意脚下站台与列车之间的空隙,看护好老人和儿童。"; - ItemsPerPage = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Jinanxi/SmallScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Jinanxi/SmallScreenViewModel.cs deleted file mode 100644 index 7d91ad3..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Jinanxi/SmallScreenViewModel.cs +++ /dev/null @@ -1,21 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Jinanxi -{ - public class SmallScreenViewModel : ScreenViewModel - { - public SmallScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 9; - ScreenCount = 1; - StationType = StationType.Departure; - SetText(); - } - private async Task SetText() - { - await DataLoaded.Task; - Text = $"{string.Join(" ", ThisStation.Name.ToCharArray())} 站 欢 迎 您"; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/KunmingChangshuiAirport/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/KunmingChangshuiAirport/PrimaryScreenViewModel.cs deleted file mode 100644 index 733e82e..0000000 --- a/CRSim.ScreenSimulator/ViewModels/KunmingChangshuiAirport/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.KunmingChangshuiAirport -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"如需要任何帮助,或有相关建议,请拨打96566。"; - ItemsPerPage = 20; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs index ce67b07..d420efd 100644 --- a/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs +++ b/CRSim.ScreenSimulator/ViewModels/MetroPlatformScreenViewModel.cs @@ -1,13 +1,14 @@ using CommunityToolkit.Mvvm.ComponentModel; +using CRSim.Core.Abstractions; using CRSim.Core.Models; -using CRSim.Core.Services; +using CRSim.ScreenSimulator.Abstractions; using CRSim.ScreenSimulator.Models; namespace CRSim.ScreenSimulator.ViewModels { - public partial class MetroPlatformScreenViewModel : ObservableObject + public partial class MetroPlatformScreenViewModel : ObservableObject, IScreenViewModel { - public readonly ITimeService _timeService; + public ITimeService TimeService { get; set; } public readonly Settings _settings; [ObservableProperty] private DateTime _currentTime = new(); @@ -19,17 +20,20 @@ namespace CRSim.ScreenSimulator.ViewModels private string _text; [ObservableProperty] private Uri _video; + [ObservableProperty] + public int _location; + public System.Windows.Threading.Dispatcher UIDispatcher { get; set; } public List TrainInfos { get; set; } = []; public MetroPlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) { - _timeService = timeService; + TimeService = timeService; _settings = settingsService.GetSettings(); timeService.OneSecondElapsed += RefreshDisplay; } private void RefreshDisplay(object? sender, EventArgs e) { - CurrentTime = _timeService.GetDateTimeNow(); - List itemsToRemove = [.. TrainInfos.Where(info => info.DepartureTime < _timeService.GetDateTimeNow())]; + CurrentTime = TimeService.GetDateTimeNow(); + List itemsToRemove = [.. TrainInfos.Where(info => info.DepartureTime < TimeService.GetDateTimeNow())]; foreach (var item in itemsToRemove) { TrainInfos.Remove(item); @@ -40,14 +44,14 @@ 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) { if (trainNumber != null && trainNumber.DepartureTime != null && trainNumber.Platform == platform) { - var now = _timeService.GetDateTimeNow(); + var now = TimeService.GetDateTimeNow(); var today = now.Date; if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime ?? trainNumber.ArrivalTime)!.Value) < now) { @@ -62,7 +66,7 @@ namespace CRSim.ScreenSimulator.ViewModels Terminal = trainNumber.Terminal, ArrivalTime = AdjustTime(trainNumber.ArrivalTime), DepartureTime = AdjustTime(trainNumber.DepartureTime), - State = TimeSpan.Zero + State = trainNumber.Status }); } } diff --git a/CRSim.ScreenSimulator/ViewModels/Mianyang/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Mianyang/PlatformScreenViewModel.cs deleted file mode 100644 index bda7b53..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Mianyang/PlatformScreenViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Mianyang -{ - public class PlatformScreenViewModel : ScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Niuzhou/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Niuzhou/PrimaryScreenViewModel.cs deleted file mode 100644 index 98cb3f5..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Niuzhou/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Niuzhou -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"开车前{settingsService.GetSettings().PassingCheckInAdvanceDuration.TotalMinutes}分钟开始检票,开车前{settingsService.GetSettings().StopCheckInAdvanceDuration.TotalMinutes}分钟停止检票。"; - ItemsPerPage = 10; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Shanghai/OutsideScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Shanghai/OutsideScreenViewModel.cs deleted file mode 100644 index 04f353b..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Shanghai/OutsideScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Services; -using CRSim.Core.Models; -namespace CRSim.ScreenSimulator.ViewModels.Shanghai -{ - public class OutsideScreenViewModel : ScreenViewModel - { - public OutsideScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 9; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/ShenyangBei/PrimaryPlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/ShenyangBei/PrimaryPlatformScreenViewModel.cs deleted file mode 100644 index 9ca45b5..0000000 --- a/CRSim.ScreenSimulator/ViewModels/ShenyangBei/PrimaryPlatformScreenViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.ShenyangBei -{ - public class PlatformScreenViewModel : ScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Tianjin/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Tianjin/PlatformScreenViewModel.cs deleted file mode 100644 index a9c1aff..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Tianjin/PlatformScreenViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Tianjin -{ - public class PlatformScreenViewModel : ScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/TianjinMetro/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/TianjinMetro/PlatformScreenViewModel.cs deleted file mode 100644 index 5c6964c..0000000 --- a/CRSim.ScreenSimulator/ViewModels/TianjinMetro/PlatformScreenViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.TianjinMetro -{ - public class PlatformScreenViewModel : MetroPlatformScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = "海河明珠闪耀津门,古今交融绘就华章。五大道镌刻百年风华,滨海新区涌动创新浪潮。狗不理飘香,相声茶馆笑声扬。意式风情街浪漫流淌,古文化街非遗绽放。生态宜居城,开放包容乡。天津卫,邀您共谱新时代乐章!"; - Video = new("Assets\\Advertisement-3.mp4", UriKind.Relative); - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/XuzhouMetro/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/XuzhouMetro/PlatformScreenViewModel.cs deleted file mode 100644 index 87ce405..0000000 --- a/CRSim.ScreenSimulator/ViewModels/XuzhouMetro/PlatformScreenViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.XuzhouMetro -{ - public class PlatformScreenViewModel : MetroPlatformScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = "徐州,一座镌刻华夏文明印记的英雄之城。这里是大汉王朝的摇篮,楚风汉韵流淌在云龙山水间,龟山汉墓诉说着千年的传奇,汉画像石馆定格了古人的智慧光影。九里山前古战场,戏马台边秋风烈,历史的豪情从未褪色。作为五省通衢的枢纽,徐州以开放的胸怀连接南北,高铁飞驰、运河如练,现代脉动与古朴底蕴在此交融。登云龙山俯瞰一城青山半城湖,泛舟云龙湖感受水墨诗意;户部山的街巷藏着老徐州的烟火气,回龙窝的灯火点亮了夜色温柔。从地锅鸡的鲜香到饣它汤的醇厚,从酥脆的烙馍到清甜的蜜三刀,舌尖上的彭城满是热情滋味。这里是淮海之芯,用智造之光照亮未来,用楚汉气魄续写新章——徐州,等你共赴一场跨越时空的约定!"; - Video = new("Assets\\Advertisement-2.mp4", UriKind.Relative); - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Zibo/ConcourseBridgeScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Zibo/ConcourseBridgeScreenViewModel.cs deleted file mode 100644 index 809dd8d..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Zibo/ConcourseBridgeScreenViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Zibo -{ - public class ConcourseBridgeScreenViewModel : ScreenViewModel - { - public ConcourseBridgeScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - StationType = StationType.Departure; - ItemsPerPage = 2; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Zibo/ExitScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Zibo/ExitScreenViewModel.cs deleted file mode 100644 index a600924..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Zibo/ExitScreenViewModel.cs +++ /dev/null @@ -1,48 +0,0 @@ -using CRSim.Core.Services; -using CRSim.Core.Models; -using CRSim.ScreenSimulator.Models; -using System.Windows; -namespace CRSim.ScreenSimulator.ViewModels.Zibo -{ - public class ExitScreenViewModel : ScreenViewModel - { - public ExitScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"温馨提示:请接站的旅客在出站口外耐心等候"; - ItemsPerPage = 6; - ScreenCount = 2; - StationType = StationType.Arrival; - } - public override void RefreshDisplay(object? sender, EventArgs? e) - { - Application.Current.Dispatcher.Invoke(() => - { - int pageCount = (int)Math.Ceiling((double)TrainInfo.Count / (ItemsPerPage * ScreenCount.Value)); - ScreenA.Clear(); - ScreenB.Clear(); - int startIndex = CurrentPageIndex * ItemsPerPage * ScreenCount.Value; - var ItemsToShow = TrainInfo.Skip(startIndex).Take(ItemsPerPage * ScreenCount.Value).ToList(); - var leftItems = ItemsToShow.Where((item, index) => index % 2 == 0).ToList(); - var rightItems = ItemsToShow.Where((item, index) => index % 2 == 1).ToList(); - while (leftItems.Count < ItemsPerPage) - { - leftItems.Add(new TrainInfo()); - } - while (rightItems.Count < ItemsPerPage) - { - rightItems.Add(new TrainInfo()); - } - foreach (var item in leftItems) - { - ScreenA.Add(item); - } - foreach (var item in rightItems) - { - ScreenB.Add(item); - } - CurrentPageIndex = CurrentPageIndex + 1 >= Math.Min(_settings.MaxPages, pageCount) ? 0 : CurrentPageIndex + 1; - }); - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Zibo/PlatformScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Zibo/PlatformScreenViewModel.cs deleted file mode 100644 index 49b910b..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Zibo/PlatformScreenViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Zibo -{ - public class PlatformScreenViewModel : ScreenViewModel - { - public PlatformScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"请站在白色安全线以内排队候车"; - ItemsPerPage = 1; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Zibo/PrimaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Zibo/PrimaryScreenViewModel.cs deleted file mode 100644 index 69a5dd9..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Zibo/PrimaryScreenViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Zibo -{ - public class PrimaryScreenViewModel : ScreenViewModel - { - public PrimaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - ItemsPerPage = 8; - ScreenCount = 2; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Zibo/SecondaryScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Zibo/SecondaryScreenViewModel.cs deleted file mode 100644 index 93aa835..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Zibo/SecondaryScreenViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -namespace CRSim.ScreenSimulator.ViewModels.Zibo -{ - public class SecondaryScreenViewModel : ScreenViewModel - { - public SecondaryScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $"开车前{settingsService.GetSettings().StopCheckInAdvanceDuration.TotalMinutes}分钟停止检票"; - ItemsPerPage = 18; - ScreenCount = 1; - StationType = StationType.Departure; - } - } -} diff --git a/CRSim.ScreenSimulator/ViewModels/Zibo/TicketCheckScreenViewModel.cs b/CRSim.ScreenSimulator/ViewModels/Zibo/TicketCheckScreenViewModel.cs deleted file mode 100644 index ea35ab9..0000000 --- a/CRSim.ScreenSimulator/ViewModels/Zibo/TicketCheckScreenViewModel.cs +++ /dev/null @@ -1,48 +0,0 @@ -using CRSim.Core.Models; -using CRSim.Core.Services; -using CRSim.ScreenSimulator.Models; -using System.Windows; -namespace CRSim.ScreenSimulator.ViewModels.Zibo -{ - public class TicketCheckScreenViewModel : ScreenViewModel - { - public TicketCheckScreenViewModel(ITimeService timeService, ISettingsService settingsService) - : base(timeService, settingsService) - { - Text = $" 开车前{settingsService.GetSettings().StopCheckInAdvanceDuration.TotalMinutes}分钟停止检票"; - StationType = StationType.Departure; - ItemsPerPage = 5; - ScreenCount = 2; - } - public override void RefreshDisplay(object? sender, EventArgs? e) - { - Application.Current.Dispatcher.Invoke(() => - { - int pageCount = (int)Math.Ceiling((double)TrainInfo.Count / (ItemsPerPage * ScreenCount.Value)); - ScreenA.Clear(); - ScreenB.Clear(); - int startIndex = CurrentPageIndex * ItemsPerPage * ScreenCount.Value; - var ItemsToShow = TrainInfo.Skip(startIndex).Take(ItemsPerPage * ScreenCount.Value).ToList(); - var leftItems = ItemsToShow.Where((item, index) => index % 2 == 0).ToList(); - var rightItems = ItemsToShow.Where((item, index) => index % 2 == 1).ToList(); - while (leftItems.Count < ItemsPerPage) - { - leftItems.Add(new TrainInfo()); - } - while (rightItems.Count < ItemsPerPage) - { - rightItems.Add(new TrainInfo()); - } - foreach (var item in leftItems) - { - ScreenA.Add(item); - } - foreach (var item in rightItems) - { - ScreenB.Add(item); - } - CurrentPageIndex = CurrentPageIndex + 1 >= Math.Min(_settings.MaxPages, pageCount) ? 0 : CurrentPageIndex + 1; - }); - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Ankang/ExitScreenView.xaml b/CRSim.ScreenSimulator/Views/Ankang/ExitScreenView.xaml deleted file mode 100644 index 43fe68b..0000000 --- a/CRSim.ScreenSimulator/Views/Ankang/ExitScreenView.xaml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Ankang/ExitScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Ankang/ExitScreenView.xaml.cs deleted file mode 100644 index efb3592..0000000 --- a/CRSim.ScreenSimulator/Views/Ankang/ExitScreenView.xaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Ankang; -using System.Windows; -using System.Windows.Input; -using System.Windows.Media; - -namespace CRSim.ScreenSimulator.Views.Ankang -{ - public partial class ExitScreenView : BaseScreenView - { - public ExitScreenViewModel ViewModel { get; } - public ExitScreenView(ExitScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/BaseScreenView.cs b/CRSim.ScreenSimulator/Views/BaseScreenView.cs deleted file mode 100644 index 084d50f..0000000 --- a/CRSim.ScreenSimulator/Views/BaseScreenView.cs +++ /dev/null @@ -1,60 +0,0 @@ -using CRSim.ScreenSimulator.Models; -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Windows; -using System.Windows.Input; -using System.Windows.Media; - -namespace CRSim.ScreenSimulator.Views -{ - public abstract class BaseScreenView : Window - { - public Guid SessionID; - protected BaseScreenView() - { - Title = "引导屏"; - Background = Brushes.Transparent; - RenderOptions.SetEdgeMode(this, EdgeMode.Aliased); - MouseDown += Window_MouseDown; - KeyDown += Window_KeyDown; - Unloaded += Window_Unloaded; - SizeToContent = SizeToContent.WidthAndHeight; - ResizeMode = ResizeMode.NoResize; - WindowStyle = WindowStyle.None; - AllowsTransparency = true; - } - - private async void Window_Unloaded(object sender, RoutedEventArgs e) - { - await DeleteScreenByGuidAsync(); - } - - private void Window_MouseDown(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) DragMove(); - } - - private void Window_KeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.Escape) Close(); - } - private static readonly JsonSerializerOptions CachedJsonSerializerOptions = new() - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - private async Task DeleteScreenByGuidAsync() - { - if (File.Exists("Screen.json")) - { - var fileContent = await File.ReadAllTextAsync("Screen.json"); - var screenInfos = JsonSerializer.Deserialize>(fileContent) ?? []; - - screenInfos.RemoveAll(screen => screen.SessionID == SessionID); - - var serializedContent = JsonSerializer.Serialize(screenInfos, CachedJsonSerializerOptions); - await File.WriteAllTextAsync("Screen.json", serializedContent); - } - } - } -} \ No newline at end of file diff --git a/CRSim.ScreenSimulator/Views/Beijing/ArrivalScreenView.xaml b/CRSim.ScreenSimulator/Views/Beijing/ArrivalScreenView.xaml deleted file mode 100644 index fc95107..0000000 --- a/CRSim.ScreenSimulator/Views/Beijing/ArrivalScreenView.xaml +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Beijing/ArrivalScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Beijing/ArrivalScreenView.xaml.cs deleted file mode 100644 index a64ad58..0000000 --- a/CRSim.ScreenSimulator/Views/Beijing/ArrivalScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Beijing; -namespace CRSim.ScreenSimulator.Views.Beijing -{ - public partial class ArrivalScreenView : BaseScreenView - { - public ArrivalScreenViewModel ViewModel { get; } - public ArrivalScreenView(ArrivalScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} \ No newline at end of file diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryPlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryPlatformScreenView.xaml deleted file mode 100644 index 88d9c84..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryPlatformScreenView.xaml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryPlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryPlatformScreenView.xaml.cs deleted file mode 100644 index c5994cd..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryPlatformScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.BeijingNan; - -namespace CRSim.ScreenSimulator.Views.BeijingNan -{ - public partial class PrimaryPlatformScreenView : BaseScreenView - { - public PrimaryPlatformScreenViewModel ViewModel { get; } - public PrimaryPlatformScreenView(PrimaryPlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryScreenView.xaml b/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryScreenView.xaml deleted file mode 100644 index 8c3c963..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryScreenView.xaml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryScreenView.xaml.cs deleted file mode 100644 index 2144160..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/PrimaryScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.BeijingNan; -namespace CRSim.ScreenSimulator.Views.BeijingNan -{ - public partial class PrimaryScreenView : BaseScreenView - { - public PrimaryScreenViewModel ViewModel { get; } - public PrimaryScreenView(PrimaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/SecondaryPlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/BeijingNan/SecondaryPlatformScreenView.xaml deleted file mode 100644 index 9472b89..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/SecondaryPlatformScreenView.xaml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/SecondaryPlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/BeijingNan/SecondaryPlatformScreenView.xaml.cs deleted file mode 100644 index 7b2a48c..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/SecondaryPlatformScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.BeijingNan; - -namespace CRSim.ScreenSimulator.Views.BeijingNan -{ - public partial class SecondaryPlatformScreenView : BaseScreenView - { - public SecondaryPlatformScreenViewModel ViewModel { get; } - public SecondaryPlatformScreenView(SecondaryPlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/TicketCheckScreenView.xaml b/CRSim.ScreenSimulator/Views/BeijingNan/TicketCheckScreenView.xaml deleted file mode 100644 index b11797e..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/TicketCheckScreenView.xaml +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/BeijingNan/TicketCheckScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/BeijingNan/TicketCheckScreenView.xaml.cs deleted file mode 100644 index 4e5f6d0..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingNan/TicketCheckScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.BeijingNan; -namespace CRSim.ScreenSimulator.Views.BeijingNan -{ - public partial class TicketCheckScreenView : BaseScreenView - { - public TicketCheckScreenViewModel ViewModel { get; } - public TicketCheckScreenView(TicketCheckScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/BeijingXi/PrimaryScreenView.xaml b/CRSim.ScreenSimulator/Views/BeijingXi/PrimaryScreenView.xaml deleted file mode 100644 index 3f44de3..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingXi/PrimaryScreenView.xaml +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/BeijingXi/PrimaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/BeijingXi/PrimaryScreenView.xaml.cs deleted file mode 100644 index aff1da0..0000000 --- a/CRSim.ScreenSimulator/Views/BeijingXi/PrimaryScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.BeijingXi; -namespace CRSim.ScreenSimulator.Views.BeijingXi -{ - public partial class PrimaryScreenView : BaseScreenView - { - public PrimaryScreenViewModel ViewModel { get; } - public PrimaryScreenView(PrimaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/ChengduDong/PillarTicketCheckScreenView.xaml b/CRSim.ScreenSimulator/Views/ChengduDong/PillarTicketCheckScreenView.xaml deleted file mode 100644 index 8a5f73f..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduDong/PillarTicketCheckScreenView.xaml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/ChengduDong/PillarTicketCheckScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/ChengduDong/PillarTicketCheckScreenView.xaml.cs deleted file mode 100644 index 06a38c3..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduDong/PillarTicketCheckScreenView.xaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.ChengduDong; - -namespace CRSim.ScreenSimulator.Views.ChengduDong -{ - /// - /// SecondaryScreen.xaml 的交互逻辑 - /// - public partial class PillarTicketCheckScreenView : BaseScreenView - { - public PillarTicketCheckScreenViewModel ViewModel { get; } - public PillarTicketCheckScreenView(PillarTicketCheckScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/ChengduDong/PrimaryTicketCheckScreenView.xaml b/CRSim.ScreenSimulator/Views/ChengduDong/PrimaryTicketCheckScreenView.xaml deleted file mode 100644 index f680144..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduDong/PrimaryTicketCheckScreenView.xaml +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/ChengduDong/PrimaryTicketCheckScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/ChengduDong/PrimaryTicketCheckScreenView.xaml.cs deleted file mode 100644 index ccb5d9e..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduDong/PrimaryTicketCheckScreenView.xaml.cs +++ /dev/null @@ -1,20 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.ChengduDong; -using System.Windows; -using System.Windows.Input; -using System.Windows.Media; -namespace CRSim.ScreenSimulator.Views.ChengduDong -{ - /// - /// SecondaryScreen.xaml 的交互逻辑 - /// - public partial class PrimaryTicketCheckScreenView : BaseScreenView - { - public PrimaryTicketCheckScreenViewModel ViewModel { get; } - public PrimaryTicketCheckScreenView(PrimaryTicketCheckScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/ChengduDong/SecondaryScreenView.xaml b/CRSim.ScreenSimulator/Views/ChengduDong/SecondaryScreenView.xaml deleted file mode 100644 index 197be74..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduDong/SecondaryScreenView.xaml +++ /dev/null @@ -1,412 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/ChengduDong/SecondaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/ChengduDong/SecondaryScreenView.xaml.cs deleted file mode 100644 index fca44b7..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduDong/SecondaryScreenView.xaml.cs +++ /dev/null @@ -1,21 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.ChengduDong; -using System.Windows; -using System.Windows.Input; -using System.Windows.Media; - -namespace CRSim.ScreenSimulator.Views.ChengduDong -{ - /// - /// SecondaryScreen.xaml 的交互逻辑 - /// - public partial class SecondaryScreenView : BaseScreenView - { - public SecondaryScreenViewModel ViewModel { get; } - public SecondaryScreenView(SecondaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/ChengduMetro/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/ChengduMetro/PlatformScreenView.xaml deleted file mode 100644 index 225ff67..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduMetro/PlatformScreenView.xaml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/ChengduMetro/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/ChengduMetro/PlatformScreenView.xaml.cs deleted file mode 100644 index 29bb432..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduMetro/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,25 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.ChengduMetro; -using System.Windows; - -namespace CRSim.ScreenSimulator.Views.ChengduMetro -{ - /// - /// SecondaryScreen.xaml 的交互逻辑 - /// - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - player.Play(); - } - private void MediaElement_MediaEnded(object sender, RoutedEventArgs e) - { - player.Position = TimeSpan.Zero; - player.Play(); - } - } -} diff --git a/CRSim.ScreenSimulator/Views/ChengduShuangliuAirport/SecondaryScreenView.xaml b/CRSim.ScreenSimulator/Views/ChengduShuangliuAirport/SecondaryScreenView.xaml deleted file mode 100644 index 02b026e..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduShuangliuAirport/SecondaryScreenView.xaml +++ /dev/null @@ -1,300 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/ChengduShuangliuAirport/SecondaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/ChengduShuangliuAirport/SecondaryScreenView.xaml.cs deleted file mode 100644 index 63e12da..0000000 --- a/CRSim.ScreenSimulator/Views/ChengduShuangliuAirport/SecondaryScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.ChengduShuangliuAirport; - -namespace CRSim.ScreenSimulator.Views.ChengduShuangliuAirport -{ - public partial class SecondaryScreenView : BaseScreenView - { - public SecondaryScreenViewModel ViewModel { get; } - public SecondaryScreenView(SecondaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/DaqingDong/ConcourseBridgeScreenView.xaml b/CRSim.ScreenSimulator/Views/DaqingDong/ConcourseBridgeScreenView.xaml deleted file mode 100644 index cf6b9d3..0000000 --- a/CRSim.ScreenSimulator/Views/DaqingDong/ConcourseBridgeScreenView.xaml +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/DaqingDong/ConcourseBridgeScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/DaqingDong/ConcourseBridgeScreenView.xaml.cs deleted file mode 100644 index 9d0a5c5..0000000 --- a/CRSim.ScreenSimulator/Views/DaqingDong/ConcourseBridgeScreenView.xaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.DaqingDong; -namespace CRSim.ScreenSimulator.Views.DaqingDong -{ - /// - /// SecondaryScreen.xaml 的交互逻辑 - /// - public partial class ConcourseBridgeScreenView : BaseScreenView - { - public ConcourseBridgeScreenViewModel ViewModel { get; } - public ConcourseBridgeScreenView(ConcourseBridgeScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/DaqingDong/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/DaqingDong/PlatformScreenView.xaml deleted file mode 100644 index 292b59e..0000000 --- a/CRSim.ScreenSimulator/Views/DaqingDong/PlatformScreenView.xaml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/DaqingDong/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/DaqingDong/PlatformScreenView.xaml.cs deleted file mode 100644 index 53c86d2..0000000 --- a/CRSim.ScreenSimulator/Views/DaqingDong/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.DaqingDong; - -namespace CRSim.ScreenSimulator.Views.DaqingDong -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/DezhouDong/PrimaryScreenView.xaml b/CRSim.ScreenSimulator/Views/DezhouDong/PrimaryScreenView.xaml deleted file mode 100644 index 682ced4..0000000 --- a/CRSim.ScreenSimulator/Views/DezhouDong/PrimaryScreenView.xaml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/DezhouDong/PrimaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/DezhouDong/PrimaryScreenView.xaml.cs deleted file mode 100644 index 0853676..0000000 --- a/CRSim.ScreenSimulator/Views/DezhouDong/PrimaryScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.DezhouDong; -namespace CRSim.ScreenSimulator.Views.DezhouDong -{ - public partial class PrimaryScreenView : BaseScreenView - { - public PrimaryScreenViewModel ViewModel { get; } - public PrimaryScreenView(PrimaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/DezhouDong/TicketCheckScreenView.xaml b/CRSim.ScreenSimulator/Views/DezhouDong/TicketCheckScreenView.xaml deleted file mode 100644 index cb37bf6..0000000 --- a/CRSim.ScreenSimulator/Views/DezhouDong/TicketCheckScreenView.xaml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/DezhouDong/TicketCheckScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/DezhouDong/TicketCheckScreenView.xaml.cs deleted file mode 100644 index 723996d..0000000 --- a/CRSim.ScreenSimulator/Views/DezhouDong/TicketCheckScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.DezhouDong; - -namespace CRSim.ScreenSimulator.Views.DezhouDong -{ - public partial class TicketCheckScreenView : BaseScreenView - { - public TicketCheckScreenViewModel ViewModel { get; } - public TicketCheckScreenView(TicketCheckScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Fuzhou/TicketCheckScreenView.xaml b/CRSim.ScreenSimulator/Views/Fuzhou/TicketCheckScreenView.xaml deleted file mode 100644 index 5f09254..0000000 --- a/CRSim.ScreenSimulator/Views/Fuzhou/TicketCheckScreenView.xaml +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Fuzhou/TicketCheckScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Fuzhou/TicketCheckScreenView.xaml.cs deleted file mode 100644 index 4629cdc..0000000 --- a/CRSim.ScreenSimulator/Views/Fuzhou/TicketCheckScreenView.xaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Fuzhou; - -namespace CRSim.ScreenSimulator.Views.Fuzhou -{ - /// - /// SecondaryScreen.xaml 的交互逻辑 - /// - public partial class TicketCheckScreenView : BaseScreenView - { - public TicketCheckScreenViewModel ViewModel { get; } - public TicketCheckScreenView(TicketCheckScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/FuzhouNan/TicketCheckScreenView.xaml b/CRSim.ScreenSimulator/Views/FuzhouNan/TicketCheckScreenView.xaml deleted file mode 100644 index ffda2af..0000000 --- a/CRSim.ScreenSimulator/Views/FuzhouNan/TicketCheckScreenView.xaml +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/FuzhouNan/TicketCheckScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/FuzhouNan/TicketCheckScreenView.xaml.cs deleted file mode 100644 index 9459ea8..0000000 --- a/CRSim.ScreenSimulator/Views/FuzhouNan/TicketCheckScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.FuzhouNan; - -namespace CRSim.ScreenSimulator.Views.FuzhouNan -{ - public partial class TicketCheckScreenView : BaseScreenView - { - public TicketCheckScreenViewModel ViewModel { get; } - public TicketCheckScreenView(TicketCheckScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/GuangdongIntercity/SecondaryScreenView.xaml b/CRSim.ScreenSimulator/Views/GuangdongIntercity/SecondaryScreenView.xaml deleted file mode 100644 index 43dbbdf..0000000 --- a/CRSim.ScreenSimulator/Views/GuangdongIntercity/SecondaryScreenView.xaml +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/GuangdongIntercity/SecondaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/GuangdongIntercity/SecondaryScreenView.xaml.cs deleted file mode 100644 index bb480bf..0000000 --- a/CRSim.ScreenSimulator/Views/GuangdongIntercity/SecondaryScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.GuangdongIntercity; - -namespace CRSim.ScreenSimulator.Views.GuangdongIntercity -{ - public partial class SecondaryScreenView : BaseScreenView - { - public SecondaryScreenViewModel ViewModel { get; } - public SecondaryScreenView(SecondaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Guangyuan/PrimaryScreenView.xaml b/CRSim.ScreenSimulator/Views/Guangyuan/PrimaryScreenView.xaml deleted file mode 100644 index 2e1bc80..0000000 --- a/CRSim.ScreenSimulator/Views/Guangyuan/PrimaryScreenView.xaml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Guangyuan/PrimaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Guangyuan/PrimaryScreenView.xaml.cs deleted file mode 100644 index 71fd35a..0000000 --- a/CRSim.ScreenSimulator/Views/Guangyuan/PrimaryScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Guangyuan; -namespace CRSim.ScreenSimulator.Views.Guangyuan -{ - public partial class PrimaryScreenView : BaseScreenView - { - public PrimaryScreenViewModel ViewModel { get; } - public PrimaryScreenView(PrimaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Hanzhong/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/Hanzhong/PlatformScreenView.xaml deleted file mode 100644 index d469126..0000000 --- a/CRSim.ScreenSimulator/Views/Hanzhong/PlatformScreenView.xaml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Hanzhong/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Hanzhong/PlatformScreenView.xaml.cs deleted file mode 100644 index a66ef29..0000000 --- a/CRSim.ScreenSimulator/Views/Hanzhong/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Hanzhong; - -namespace CRSim.ScreenSimulator.Views.Hanzhong -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Harbin/PrimaryScreenView.xaml b/CRSim.ScreenSimulator/Views/Harbin/PrimaryScreenView.xaml deleted file mode 100644 index 8f37539..0000000 --- a/CRSim.ScreenSimulator/Views/Harbin/PrimaryScreenView.xaml +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Harbin/PrimaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Harbin/PrimaryScreenView.xaml.cs deleted file mode 100644 index b88b19f..0000000 --- a/CRSim.ScreenSimulator/Views/Harbin/PrimaryScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Harbin; - -namespace CRSim.ScreenSimulator.Views.Harbin -{ - public partial class PrimaryScreenView : BaseScreenView - { - public PrimaryScreenViewModel ViewModel { get; } - public PrimaryScreenView(PrimaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/HarbinXi/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/HarbinXi/PlatformScreenView.xaml deleted file mode 100644 index df506b5..0000000 --- a/CRSim.ScreenSimulator/Views/HarbinXi/PlatformScreenView.xaml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/HarbinXi/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/HarbinXi/PlatformScreenView.xaml.cs deleted file mode 100644 index f61390c..0000000 --- a/CRSim.ScreenSimulator/Views/HarbinXi/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.HarbinXi; - -namespace CRSim.ScreenSimulator.Views.HarbinXi -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Jinanxi/SmallScreenView.xaml b/CRSim.ScreenSimulator/Views/Jinanxi/SmallScreenView.xaml deleted file mode 100644 index 2321bdb..0000000 --- a/CRSim.ScreenSimulator/Views/Jinanxi/SmallScreenView.xaml +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Jinanxi/SmallScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Jinanxi/SmallScreenView.xaml.cs deleted file mode 100644 index 6d59b07..0000000 --- a/CRSim.ScreenSimulator/Views/Jinanxi/SmallScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Jinanxi; - -namespace CRSim.ScreenSimulator.Views.Jinanxi -{ - public partial class SmallScreenView : BaseScreenView - { - public SmallScreenViewModel ViewModel { get; } - public SmallScreenView(SmallScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/KunmingChangshuiAirport/PrimaryScreenView.xaml b/CRSim.ScreenSimulator/Views/KunmingChangshuiAirport/PrimaryScreenView.xaml deleted file mode 100644 index 92d7d17..0000000 --- a/CRSim.ScreenSimulator/Views/KunmingChangshuiAirport/PrimaryScreenView.xaml +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Niuzhou/PrimaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Niuzhou/PrimaryScreenView.xaml.cs deleted file mode 100644 index 72b62e2..0000000 --- a/CRSim.ScreenSimulator/Views/Niuzhou/PrimaryScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Niuzhou; - -namespace CRSim.ScreenSimulator.Views.Niuzhou -{ - public partial class PrimaryScreenView : BaseScreenView - { - public PrimaryScreenViewModel ViewModel { get; } - public PrimaryScreenView(PrimaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Shanghai/OutsideScreenView.xaml b/CRSim.ScreenSimulator/Views/Shanghai/OutsideScreenView.xaml deleted file mode 100644 index 15d9329..0000000 --- a/CRSim.ScreenSimulator/Views/Shanghai/OutsideScreenView.xaml +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Shanghai/OutsideScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Shanghai/OutsideScreenView.xaml.cs deleted file mode 100644 index 283ca22..0000000 --- a/CRSim.ScreenSimulator/Views/Shanghai/OutsideScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Shanghai; - -namespace CRSim.ScreenSimulator.Views.Shanghai -{ - public partial class OutsideScreenView : BaseScreenView - { - public OutsideScreenViewModel ViewModel { get; } - public OutsideScreenView(OutsideScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/ShenyangBei/PrimaryPlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/ShenyangBei/PrimaryPlatformScreenView.xaml deleted file mode 100644 index ca72247..0000000 --- a/CRSim.ScreenSimulator/Views/ShenyangBei/PrimaryPlatformScreenView.xaml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/ShenyangBei/PrimaryPlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/ShenyangBei/PrimaryPlatformScreenView.xaml.cs deleted file mode 100644 index ab5923d..0000000 --- a/CRSim.ScreenSimulator/Views/ShenyangBei/PrimaryPlatformScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.ShenyangBei; - -namespace CRSim.ScreenSimulator.Views.ShenyangBei -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/SimulatorWindow.xaml b/CRSim.ScreenSimulator/Views/SimulatorWindow.xaml new file mode 100644 index 0000000..1f6170b --- /dev/null +++ b/CRSim.ScreenSimulator/Views/SimulatorWindow.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CRSim.ScreenSimulator/Views/SimulatorWindow.xaml.cs b/CRSim.ScreenSimulator/Views/SimulatorWindow.xaml.cs new file mode 100644 index 0000000..d817961 --- /dev/null +++ b/CRSim.ScreenSimulator/Views/SimulatorWindow.xaml.cs @@ -0,0 +1,129 @@ +using CRSim.Core.Abstractions; +using CRSim.ScreenSimulator.Models; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace CRSim.ScreenSimulator.Views +{ + public partial class SimulatorWindow : Window + { + private readonly ITimeService _timeService; + private readonly StyleManager _styleManager; + public Session Session { get; } + public SimulatorWindow(Page page,ITimeService timeService,StyleManager styleManager,Session session) + { + InitializeComponent(); + RenderOptions.SetEdgeMode(this, EdgeMode.Aliased); + Session = session; + contentFrame.Content = page; + _timeService = timeService; + _styleManager = styleManager; + + foreach (var resource in page.Resources.Values) + { + if (resource is IHasTimeService needsTime) + { + needsTime.TimeService = timeService; + } + } + _timeService.OneSecondElapsed += UpdateTime; + } + + private void UpdateTime(object? sender, EventArgs e) + { + Dispatcher.Invoke(() => + { + TimeText.Text = _timeService.GetDateTimeNow().ToString("HH:mm:ss"); + }); + } + + private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left) DragMove(); + } + + private void DecreaseSpeed(object sender, MouseButtonEventArgs e) + { + if (_timeService.Speed > 0.25 && _timeService.Speed <= 1024) + { + _timeService.Speed /= 2; + SpeedText.Text = _timeService.Speed.ToString() + "x"; + if (_timeService.Speed == 1) + { + RestoreSpeedIcon.Visibility = Visibility.Hidden; + } + else + { + RestoreSpeedIcon.Visibility = Visibility.Visible; + } + } + } + private void IncreaseSpeed(object sender, MouseButtonEventArgs e) + { + if (_timeService.Speed >= 0.25 && _timeService.Speed < 1024) + { + _timeService.Speed *= 2; + SpeedText.Text = _timeService.Speed.ToString() + "x"; + if (_timeService.Speed == 1) + { + RestoreSpeedIcon.Visibility = Visibility.Hidden; + } + else + { + RestoreSpeedIcon.Visibility = Visibility.Visible; + } + } + } + + private void RestoreSpeed(object sender, MouseButtonEventArgs e) + { + _timeService.Speed = 1; + SpeedText.Text = "1x"; + RestoreSpeedIcon.Visibility = Visibility.Hidden; + } + + private void Window_Closed(object sender, EventArgs e) + { + _styleManager.DispatcherQueue.TryEnqueue(() => + { + _styleManager.ActiveWindows.Remove(this); + }); + } + + public void FoucsWindow(object o,object s) + { + Dispatcher.Invoke(() => + { + WindowState = WindowState.Normal; + Activate(); + Topmost = true; + Topmost = false; + Focus(); + }); + } + public void CloseWindow(object o, object s) + { + Dispatcher.Invoke(() => + { + Close(); + }); + } + + private void FullScreen(object sender, MouseButtonEventArgs e) + { + Left = 0; + viewbox.Stretch = Stretch.Uniform; + FullScreenIcon.Visibility = Visibility.Collapsed; + RestoreWindowIcon.Visibility = Visibility.Visible; + } + + private void RestoreWindow(object sender, MouseButtonEventArgs e) + { + viewbox.Stretch = Stretch.None; + FullScreenIcon.Visibility = Visibility.Visible; + RestoreWindowIcon.Visibility = Visibility.Collapsed; + } + } +} diff --git a/CRSim.ScreenSimulator/Views/Tianjin/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/Tianjin/PlatformScreenView.xaml deleted file mode 100644 index 24a07b2..0000000 --- a/CRSim.ScreenSimulator/Views/Tianjin/PlatformScreenView.xaml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Tianjin/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Tianjin/PlatformScreenView.xaml.cs deleted file mode 100644 index 98a0447..0000000 --- a/CRSim.ScreenSimulator/Views/Tianjin/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Tianjin; - -namespace CRSim.ScreenSimulator.Views.Tianjin -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/TianjinMetro/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/TianjinMetro/PlatformScreenView.xaml deleted file mode 100644 index 5bee86f..0000000 --- a/CRSim.ScreenSimulator/Views/TianjinMetro/PlatformScreenView.xaml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/TianjinMetro/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/TianjinMetro/PlatformScreenView.xaml.cs deleted file mode 100644 index 769951a..0000000 --- a/CRSim.ScreenSimulator/Views/TianjinMetro/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,24 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.TianjinMetro; -using System.Windows; - -namespace CRSim.ScreenSimulator.Views.TianjinMetro -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - player.Play(); - } - - private void MediaElement_MediaEnded(object sender, RoutedEventArgs e) - { - player.Position = TimeSpan.Zero; - player.Play(); - } - - } -} diff --git a/CRSim.ScreenSimulator/Views/XuzhouMetro/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/XuzhouMetro/PlatformScreenView.xaml deleted file mode 100644 index 6f5dfd4..0000000 --- a/CRSim.ScreenSimulator/Views/XuzhouMetro/PlatformScreenView.xaml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/XuzhouMetro/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/XuzhouMetro/PlatformScreenView.xaml.cs deleted file mode 100644 index 40f9ac9..0000000 --- a/CRSim.ScreenSimulator/Views/XuzhouMetro/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,24 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.XuzhouMetro; -using System.Windows; - -namespace CRSim.ScreenSimulator.Views.XuzhouMetro -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - player.Play(); - } - - private void MediaElement_MediaEnded(object sender, RoutedEventArgs e) - { - player.Position = TimeSpan.Zero; - player.Play(); - } - - } -} diff --git a/CRSim.ScreenSimulator/Views/Zibo/ConcourseBridgeScreenView.xaml b/CRSim.ScreenSimulator/Views/Zibo/ConcourseBridgeScreenView.xaml deleted file mode 100644 index c9a195c..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/ConcourseBridgeScreenView.xaml +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Zibo/ConcourseBridgeScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Zibo/ConcourseBridgeScreenView.xaml.cs deleted file mode 100644 index ebd9ecc..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/ConcourseBridgeScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Zibo; -namespace CRSim.ScreenSimulator.Views.Zibo -{ - public partial class ConcourseBridgeScreenView : BaseScreenView - { - public ConcourseBridgeScreenViewModel ViewModel { get; } - public ConcourseBridgeScreenView(ConcourseBridgeScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Zibo/ExitScreenView.xaml b/CRSim.ScreenSimulator/Views/Zibo/ExitScreenView.xaml deleted file mode 100644 index e7dfff1..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/ExitScreenView.xaml +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Zibo/ExitScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Zibo/ExitScreenView.xaml.cs deleted file mode 100644 index e0140b9..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/ExitScreenView.xaml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Zibo; -using System.Windows; -using System.Windows.Input; -using System.Windows.Media; - -namespace CRSim.ScreenSimulator.Views.Zibo -{ - public partial class ExitScreenView : BaseScreenView - { - public ExitScreenViewModel ViewModel { get; } - public ExitScreenView(ExitScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Zibo/PlatformScreenView.xaml b/CRSim.ScreenSimulator/Views/Zibo/PlatformScreenView.xaml deleted file mode 100644 index e6dcdde..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/PlatformScreenView.xaml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Zibo/PlatformScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Zibo/PlatformScreenView.xaml.cs deleted file mode 100644 index f561208..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/PlatformScreenView.xaml.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Zibo; - -namespace CRSim.ScreenSimulator.Views.Zibo -{ - public partial class PlatformScreenView : BaseScreenView - { - public PlatformScreenViewModel ViewModel { get; } - public PlatformScreenView(PlatformScreenViewModel viewModel) - { - - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Zibo/PrimaryScreenView.xaml b/CRSim.ScreenSimulator/Views/Zibo/PrimaryScreenView.xaml deleted file mode 100644 index 516386f..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/PrimaryScreenView.xaml +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Zibo/PrimaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Zibo/PrimaryScreenView.xaml.cs deleted file mode 100644 index 43b8c3b..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/PrimaryScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Zibo; - -namespace CRSim.ScreenSimulator.Views.Zibo -{ - public partial class PrimaryScreenView : BaseScreenView - { - public PrimaryScreenViewModel ViewModel { get; } - public PrimaryScreenView(PrimaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Zibo/SecondaryScreenView.xaml b/CRSim.ScreenSimulator/Views/Zibo/SecondaryScreenView.xaml deleted file mode 100644 index 8d65ff8..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/SecondaryScreenView.xaml +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Zibo/SecondaryScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Zibo/SecondaryScreenView.xaml.cs deleted file mode 100644 index 076c779..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/SecondaryScreenView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Zibo; - -namespace CRSim.ScreenSimulator.Views.Zibo -{ - public partial class SecondaryScreenView : BaseScreenView - { - public SecondaryScreenViewModel ViewModel { get; } - public SecondaryScreenView(SecondaryScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.ScreenSimulator/Views/Zibo/TicketCheckScreenView.xaml b/CRSim.ScreenSimulator/Views/Zibo/TicketCheckScreenView.xaml deleted file mode 100644 index d817288..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/TicketCheckScreenView.xaml +++ /dev/null @@ -1,388 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim.ScreenSimulator/Views/Zibo/TicketCheckScreenView.xaml.cs b/CRSim.ScreenSimulator/Views/Zibo/TicketCheckScreenView.xaml.cs deleted file mode 100644 index ebe1a13..0000000 --- a/CRSim.ScreenSimulator/Views/Zibo/TicketCheckScreenView.xaml.cs +++ /dev/null @@ -1,14 +0,0 @@ -using CRSim.ScreenSimulator.ViewModels.Zibo; -namespace CRSim.ScreenSimulator.Views.Zibo -{ - public partial class TicketCheckScreenView : BaseScreenView - { - public TicketCheckScreenViewModel ViewModel { get; } - public TicketCheckScreenView(TicketCheckScreenViewModel viewModel) - { - InitializeComponent(); - ViewModel = viewModel; - DataContext = viewModel; - } - } -} diff --git a/CRSim.Shared/CRSim.Shared.csproj b/CRSim.Shared/CRSim.Shared.csproj deleted file mode 100644 index ac07c5b..0000000 --- a/CRSim.Shared/CRSim.Shared.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - net9.0-windows - enable - true - enable - - - diff --git a/CRSim.Shared/Converters/TimeSpanToStringConverter.cs b/CRSim.Shared/Converters/TimeSpanToStringConverter.cs deleted file mode 100644 index 1144291..0000000 --- a/CRSim.Shared/Converters/TimeSpanToStringConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Globalization; -using System.Windows.Data; - -namespace CRSim.Shared.Converters -{ - public class TimeSpanToStringConverter : IValueConverter - { - - public string Format { get; set; } = @"hh\:mm"; - public string Culture { get; set; } = "zh-CN"; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is TimeSpan dateTime && dateTime != new TimeSpan()) - { - var CultureInfo = new CultureInfo(Culture); - return (dateTime-dateTime.Days*TimeSpan.FromHours(24)).ToString(Format,CultureInfo); - } - return string.Empty; - } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} diff --git a/CRSim.sln b/CRSim.sln index d9edeaa..c807cdb 100644 --- a/CRSim.sln +++ b/CRSim.sln @@ -3,36 +3,32 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.12.35424.110 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CRSim", "CRSim\CRSim.csproj", "{E16F847C-9278-42F5-ACDD-9FC96B45FFCF}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CRSim.Core", "CRSim.Core\CRSim.Core.csproj", "{C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CRSim.ScreenSimulator", "CRSim.ScreenSimulator\CRSim.ScreenSimulator.csproj", "{BB0CE1B3-BE9E-42E7-A15E-EDD5AC531134}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CRSim", "CRSim\CRSim.csproj", "{9C334D96-6CE7-4137-9CA8-2969E4C67C0F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CRSim.Shared", "CRSim.Shared\CRSim.Shared.csproj", "{C7153862-390F-404C-BC75-31D4F68A57BC}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CRSim.ScreenSimulator", "CRSim.ScreenSimulator\CRSim.ScreenSimulator.csproj", "{94927020-7AD7-4F6F-A456-F34A8E0BC40A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E16F847C-9278-42F5-ACDD-9FC96B45FFCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E16F847C-9278-42F5-ACDD-9FC96B45FFCF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E16F847C-9278-42F5-ACDD-9FC96B45FFCF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E16F847C-9278-42F5-ACDD-9FC96B45FFCF}.Release|Any CPU.Build.0 = Release|Any CPU - {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Release|Any CPU.Build.0 = Release|Any CPU - {BB0CE1B3-BE9E-42E7-A15E-EDD5AC531134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BB0CE1B3-BE9E-42E7-A15E-EDD5AC531134}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BB0CE1B3-BE9E-42E7-A15E-EDD5AC531134}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BB0CE1B3-BE9E-42E7-A15E-EDD5AC531134}.Release|Any CPU.Build.0 = Release|Any CPU - {C7153862-390F-404C-BC75-31D4F68A57BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C7153862-390F-404C-BC75-31D4F68A57BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C7153862-390F-404C-BC75-31D4F68A57BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C7153862-390F-404C-BC75-31D4F68A57BC}.Release|Any CPU.Build.0 = Release|Any CPU + {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Debug|x64.ActiveCfg = Debug|x64 + {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Debug|x64.Build.0 = Debug|x64 + {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Release|x64.ActiveCfg = Release|x64 + {C72B16CC-AA3E-4E0F-A424-07D3987A9FD9}.Release|x64.Build.0 = Release|x64 + {9C334D96-6CE7-4137-9CA8-2969E4C67C0F}.Debug|x64.ActiveCfg = Debug|x64 + {9C334D96-6CE7-4137-9CA8-2969E4C67C0F}.Debug|x64.Build.0 = Debug|x64 + {9C334D96-6CE7-4137-9CA8-2969E4C67C0F}.Debug|x64.Deploy.0 = Debug|x64 + {9C334D96-6CE7-4137-9CA8-2969E4C67C0F}.Release|x64.ActiveCfg = Release|x64 + {9C334D96-6CE7-4137-9CA8-2969E4C67C0F}.Release|x64.Build.0 = Release|x64 + {9C334D96-6CE7-4137-9CA8-2969E4C67C0F}.Release|x64.Deploy.0 = Release|x64 + {94927020-7AD7-4F6F-A456-F34A8E0BC40A}.Debug|x64.ActiveCfg = Debug|x64 + {94927020-7AD7-4F6F-A456-F34A8E0BC40A}.Debug|x64.Build.0 = Debug|x64 + {94927020-7AD7-4F6F-A456-F34A8E0BC40A}.Release|x64.ActiveCfg = Release|x64 + {94927020-7AD7-4F6F-A456-F34A8E0BC40A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CRSim/App.xaml b/CRSim/App.xaml index e2192e9..65368e3 100644 --- a/CRSim/App.xaml +++ b/CRSim/App.xaml @@ -1,15 +1,18 @@ - + + - - - - + + + + + diff --git a/CRSim/AssemblyInfo.cs b/CRSim/AssemblyInfo.cs deleted file mode 100644 index a8dc727..0000000 --- a/CRSim/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Windows; - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] diff --git a/CRSim/Assets/Advertisement-1.mp4 b/CRSim/Assets/Advertisement-1.mp4 deleted file mode 100644 index f2aa196..0000000 Binary files a/CRSim/Assets/Advertisement-1.mp4 and /dev/null differ diff --git a/CRSim/Assets/Advertisement-2.mp4 b/CRSim/Assets/Advertisement-2.mp4 deleted file mode 100644 index c369f08..0000000 Binary files a/CRSim/Assets/Advertisement-2.mp4 and /dev/null differ diff --git a/CRSim/Assets/Advertisement-3.mp4 b/CRSim/Assets/Advertisement-3.mp4 deleted file mode 100644 index 0c6527a..0000000 Binary files a/CRSim/Assets/Advertisement-3.mp4 and /dev/null differ diff --git a/CRSim/Assets/Background/CRSim.wmv b/CRSim/Assets/Background/CRSim.wmv new file mode 100644 index 0000000..34dc185 Binary files /dev/null and b/CRSim/Assets/Background/CRSim.wmv differ diff --git a/CRSim/Assets/Background/SplashScreenBackground.png b/CRSim/Assets/Background/SplashScreenBackground.png new file mode 100644 index 0000000..c40ac54 Binary files /dev/null and b/CRSim/Assets/Background/SplashScreenBackground.png differ diff --git a/CRSim/Assets/CRSimIcon.png b/CRSim/Assets/CRSimIcon.png index d12d2c6..2d62627 100644 Binary files a/CRSim/Assets/CRSimIcon.png and b/CRSim/Assets/CRSimIcon.png differ diff --git a/CRSim/Assets/ControlImages/Data Management.png b/CRSim/Assets/ControlImages/Data Management.png deleted file mode 100644 index 3b105a3..0000000 Binary files a/CRSim/Assets/ControlImages/Data Management.png and /dev/null differ diff --git a/CRSim/Assets/ControlImages/Gate Simulation.png b/CRSim/Assets/ControlImages/Gate Simulation.png deleted file mode 100644 index 61e0602..0000000 Binary files a/CRSim/Assets/ControlImages/Gate Simulation.png and /dev/null differ diff --git a/CRSim/Assets/ControlImages/Screen Simulation.png b/CRSim/Assets/ControlImages/Screen Simulation.png deleted file mode 100644 index 18dc1cd..0000000 Binary files a/CRSim/Assets/ControlImages/Screen Simulation.png and /dev/null differ diff --git a/CRSim/Assets/ControlImages/Start Simulation.png b/CRSim/Assets/ControlImages/Start Simulation.png deleted file mode 100644 index d70a55c..0000000 Binary files a/CRSim/Assets/ControlImages/Start Simulation.png and /dev/null differ diff --git a/CRSim/Assets/ControlImages/Station Management.png b/CRSim/Assets/ControlImages/Station Management.png deleted file mode 100644 index 2aca4c3..0000000 Binary files a/CRSim/Assets/ControlImages/Station Management.png and /dev/null differ diff --git a/CRSim/Assets/ControlImages/TrainNumber Management.png b/CRSim/Assets/ControlImages/TrainNumber Management.png deleted file mode 100644 index 7562c4d..0000000 Binary files a/CRSim/Assets/ControlImages/TrainNumber Management.png and /dev/null differ diff --git a/CRSim/Assets/ControlImages/Website Simulation.png b/CRSim/Assets/ControlImages/Website Simulation.png deleted file mode 100644 index e062530..0000000 Binary files a/CRSim/Assets/ControlImages/Website Simulation.png and /dev/null differ diff --git a/CRSim/Assets/HomeHeaderTiles/CRSim-Icon.ico b/CRSim/Assets/HomeHeaderTiles/CRSim-Icon.ico deleted file mode 100644 index 55fe937..0000000 Binary files a/CRSim/Assets/HomeHeaderTiles/CRSim-Icon.ico and /dev/null differ diff --git a/CRSim/Assets/HomeHeaderTiles/GalleryHeaderImage.jpg b/CRSim/Assets/HomeHeaderTiles/GalleryHeaderImage.jpg new file mode 100644 index 0000000..c3e66fc Binary files /dev/null and b/CRSim/Assets/HomeHeaderTiles/GalleryHeaderImage.jpg differ diff --git a/CRSim/Assets/LockScreenLogo.scale-200.png b/CRSim/Assets/LockScreenLogo.scale-200.png new file mode 100644 index 0000000..7440f0d Binary files /dev/null and b/CRSim/Assets/LockScreenLogo.scale-200.png differ diff --git a/CRSim/Assets/SplashScreen.scale-200.png b/CRSim/Assets/SplashScreen.scale-200.png new file mode 100644 index 0000000..32f486a Binary files /dev/null and b/CRSim/Assets/SplashScreen.scale-200.png differ diff --git a/CRSim/Assets/Square150x150Logo.scale-200.png b/CRSim/Assets/Square150x150Logo.scale-200.png new file mode 100644 index 0000000..53ee377 Binary files /dev/null and b/CRSim/Assets/Square150x150Logo.scale-200.png differ diff --git a/CRSim/Assets/Square44x44Logo.scale-200.png b/CRSim/Assets/Square44x44Logo.scale-200.png new file mode 100644 index 0000000..f713bba Binary files /dev/null and b/CRSim/Assets/Square44x44Logo.scale-200.png differ diff --git a/CRSim/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/CRSim/Assets/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000..dc9f5be Binary files /dev/null and b/CRSim/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/CRSim/Assets/StoreLogo.png b/CRSim/Assets/StoreLogo.png new file mode 100644 index 0000000..a4586f2 Binary files /dev/null and b/CRSim/Assets/StoreLogo.png differ diff --git a/CRSim/Assets/Wide310x150Logo.scale-200.png b/CRSim/Assets/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000..8b4a5d0 Binary files /dev/null and b/CRSim/Assets/Wide310x150Logo.scale-200.png differ diff --git a/CRSim/Assets/win11-dashboard.dark.png b/CRSim/Assets/win11-dashboard.dark.png deleted file mode 100644 index eb649e0..0000000 Binary files a/CRSim/Assets/win11-dashboard.dark.png and /dev/null differ diff --git a/CRSim/Assets/win11-dashboard.light.png b/CRSim/Assets/win11-dashboard.light.png deleted file mode 100644 index 7d965f5..0000000 Binary files a/CRSim/Assets/win11-dashboard.light.png and /dev/null differ diff --git a/CRSim/Controls/HeaderTile.xaml b/CRSim/Controls/HeaderTile.xaml deleted file mode 100644 index eaa4240..0000000 --- a/CRSim/Controls/HeaderTile.xaml +++ /dev/null @@ -1,70 +0,0 @@ - - - - diff --git a/CRSim/Controls/HeaderTile.xaml.cs b/CRSim/Controls/HeaderTile.xaml.cs deleted file mode 100644 index 467ab69..0000000 --- a/CRSim/Controls/HeaderTile.xaml.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace CRSim.Controls -{ - /// - /// Interaction logic for HeaderTile.xaml - /// - public partial class HeaderTile : UserControl - { - public HeaderTile() - { - InitializeComponent(); - } - - public string Title - { - get { return (string)GetValue(TitleProperty); } - set { SetValue(TitleProperty, value); } - } - public static readonly DependencyProperty TitleProperty = - DependencyProperty.Register("Title", typeof(string), typeof(HeaderTile), new PropertyMetadata("")); - - public string Description - { - get { return (string)GetValue(DescriptionProperty); } - set { SetValue(DescriptionProperty, value); } - } - public static readonly DependencyProperty DescriptionProperty = - DependencyProperty.Register("ColorExplanation", typeof(string), typeof(HeaderTile), new PropertyMetadata("")); - - public string Link - { - get { return (string)GetValue(LinkProperty); } - set { SetValue(LinkProperty, value); } - } - - public static readonly DependencyProperty LinkProperty = - DependencyProperty.Register("Link", typeof(string), typeof(HeaderTile), new PropertyMetadata(null)); - - public object Source - { - get { return (object)GetValue(SourceProperty); } - set { SetValue(SourceProperty, value); } - } - - public static readonly DependencyProperty SourceProperty = - DependencyProperty.Register("Source", typeof(object), typeof(HeaderTile), new PropertyMetadata(null)); - - private void RootButton_Click(object sender, RoutedEventArgs e) - { - Process.Start(new ProcessStartInfo(Link) { UseShellExecute = true }); - } - } -} diff --git a/CRSim/Controls/HomePageHeader.xaml b/CRSim/Controls/HomePageHeader.xaml new file mode 100644 index 0000000..40d2096 --- /dev/null +++ b/CRSim/Controls/HomePageHeader.xaml @@ -0,0 +1,142 @@ + + + + + M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z + + + + + + + + 0.9 + + + + + + + + + + 0.8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CRSim/Controls/HomePageHeader.xaml.cs b/CRSim/Controls/HomePageHeader.xaml.cs new file mode 100644 index 0000000..55f9013 --- /dev/null +++ b/CRSim/Controls/HomePageHeader.xaml.cs @@ -0,0 +1,10 @@ +namespace CRSim.Controls; + +public sealed partial class HomePageHeader : UserControl +{ + + public HomePageHeader() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/CRSim/Controls/HorizontalScrollContainer.xaml b/CRSim/Controls/HorizontalScrollContainer.xaml new file mode 100644 index 0000000..4887b9e --- /dev/null +++ b/CRSim/Controls/HorizontalScrollContainer.xaml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CRSim/Controls/HorizontalScrollContainer.xaml.cs b/CRSim/Controls/HorizontalScrollContainer.xaml.cs new file mode 100644 index 0000000..c97cd63 --- /dev/null +++ b/CRSim/Controls/HorizontalScrollContainer.xaml.cs @@ -0,0 +1,72 @@ +namespace CRSim.Controls; + +public sealed partial class HorizontalScrollContainer : UserControl +{ + public HorizontalScrollContainer() + { + this.InitializeComponent(); + } + + public object Source + { + get => (object)GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } + + public static readonly DependencyProperty SourceProperty = + DependencyProperty.Register(nameof(Source), typeof(object), typeof(HorizontalScrollContainer), new PropertyMetadata(null)); + + private void Scroller_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e) + { + if (e.FinalView.HorizontalOffset < 1) + { + ScrollBackBtn.Visibility = Visibility.Collapsed; + } + else if (e.FinalView.HorizontalOffset > 1) + { + ScrollBackBtn.Visibility = Visibility.Visible; + } + + if (e.FinalView.HorizontalOffset > scroller.ScrollableWidth - 1) + { + ScrollForwardBtn.Visibility = Visibility.Collapsed; + } + else if (e.FinalView.HorizontalOffset < scroller.ScrollableWidth - 1) + { + ScrollForwardBtn.Visibility = Visibility.Visible; + } + } + + private void ScrollBackBtn_Click(object sender, RoutedEventArgs e) + { + scroller.ChangeView(scroller.HorizontalOffset - scroller.ViewportWidth, null, null); + + // Manually focus to ScrollForwardBtn since this button disappears after scrolling to the end. + ScrollForwardBtn.Focus(FocusState.Programmatic); + } + + private void ScrollForwardBtn_Click(object sender, RoutedEventArgs e) + { + scroller.ChangeView(scroller.HorizontalOffset + scroller.ViewportWidth, null, null); + + // Manually focus to ScrollBackBtn since this button disappears after scrolling to the end. + ScrollBackBtn.Focus(FocusState.Programmatic); + } + + private void Scroller_SizeChanged(object sender, SizeChangedEventArgs e) + { + UpdateScrollButtonsVisibility(); + } + + private void UpdateScrollButtonsVisibility() + { + if (scroller.ScrollableWidth > 0) + { + ScrollForwardBtn.Visibility = Visibility.Visible; + } + else + { + ScrollForwardBtn.Visibility = Visibility.Collapsed; + } + } +} \ No newline at end of file diff --git a/CRSim/Controls/OpacityMaskView.xaml b/CRSim/Controls/OpacityMaskView.xaml new file mode 100644 index 0000000..7251e4f --- /dev/null +++ b/CRSim/Controls/OpacityMaskView.xaml @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/CRSim/Controls/OpacityMaskView.xaml.cs b/CRSim/Controls/OpacityMaskView.xaml.cs new file mode 100644 index 0000000..36c338f --- /dev/null +++ b/CRSim/Controls/OpacityMaskView.xaml.cs @@ -0,0 +1,85 @@ +using Microsoft.UI.Composition; +using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Media; +using System.Numerics; + +namespace CRSim.Controls; + +[TemplatePart(Name = RootGridTemplateName, Type = typeof(Grid))] +[TemplatePart(Name = MaskContainerTemplateName, Type = typeof(Border))] +[TemplatePart(Name = ContentPresenterTemplateName, Type = typeof(ContentPresenter))] +public partial class OpacityMaskView : ContentControl +{ + + public static readonly DependencyProperty OpacityMaskProperty = + DependencyProperty.Register(nameof(OpacityMask), typeof(UIElement), typeof(OpacityMaskView), new PropertyMetadata(null, OnOpacityMaskChanged)); + + private const string ContentPresenterTemplateName = "PART_ContentPresenter"; + private const string MaskContainerTemplateName = "PART_MaskContainer"; + private const string RootGridTemplateName = "PART_RootGrid"; + + private readonly Compositor _compositor = CompositionTarget.GetCompositorForCurrentThread(); + private CompositionBrush? _mask; + private CompositionMaskBrush? _maskBrush; + + public OpacityMaskView() + { + DefaultStyleKey = typeof(OpacityMaskView); + } + + public UIElement? OpacityMask + { + get => (UIElement?)GetValue(OpacityMaskProperty); + set => SetValue(OpacityMaskProperty, value); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + Grid rootGrid = (Grid)GetTemplateChild(RootGridTemplateName); + ContentPresenter contentPresenter = (ContentPresenter)GetTemplateChild(ContentPresenterTemplateName); + Border maskContainer = (Border)GetTemplateChild(MaskContainerTemplateName); + + _maskBrush = _compositor.CreateMaskBrush(); + _maskBrush.Source = GetVisualBrush(contentPresenter); + _mask = GetVisualBrush(maskContainer); + _maskBrush.Mask = OpacityMask is null ? null : _mask; + + SpriteVisual redirectVisual = _compositor.CreateSpriteVisual(); + redirectVisual.RelativeSizeAdjustment = Vector2.One; + redirectVisual.Brush = _maskBrush; + ElementCompositionPreview.SetElementChildVisual(rootGrid, redirectVisual); + } + + private static CompositionBrush GetVisualBrush(UIElement element) + { + Visual visual = ElementCompositionPreview.GetElementVisual(element); + + Compositor compositor = visual.Compositor; + + CompositionVisualSurface visualSurface = compositor.CreateVisualSurface(); + visualSurface.SourceVisual = visual; + ExpressionAnimation sourceSizeAnimation = compositor.CreateExpressionAnimation($"{nameof(visual)}.Size"); + sourceSizeAnimation.SetReferenceParameter(nameof(visual), visual); + visualSurface.StartAnimation(nameof(visualSurface.SourceSize), sourceSizeAnimation); + + CompositionSurfaceBrush brush = compositor.CreateSurfaceBrush(visualSurface); + + visual.Opacity = 0; + + return brush; + } + + private static void OnOpacityMaskChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OpacityMaskView self = (OpacityMaskView)d; + if (self._maskBrush is not { } maskBrush) + { + return; + } + + UIElement? opacityMask = (UIElement?)e.NewValue; + maskBrush.Mask = opacityMask is null ? null : self._mask; + } +} \ No newline at end of file diff --git a/CRSim/Controls/PageHeader.xaml b/CRSim/Controls/PageHeader.xaml deleted file mode 100644 index 3fcf70e..0000000 --- a/CRSim/Controls/PageHeader.xaml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/CRSim/Controls/PageHeader.xaml.cs b/CRSim/Controls/PageHeader.xaml.cs deleted file mode 100644 index 4e742de..0000000 --- a/CRSim/Controls/PageHeader.xaml.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace CRSim.Controls; - -public class PageHeader : Control -{ - public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( - nameof(Title), - typeof(string), - typeof(PageHeader), - new PropertyMetadata(null) - ); - - public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( - nameof(Description), - typeof(string), - typeof(PageHeader), - new PropertyMetadata(null) - ); - - public static readonly DependencyProperty ShowDescriptionProperty = DependencyProperty.Register( - nameof(ShowDescription), - typeof(bool), - typeof(PageHeader), - new PropertyMetadata(true) - ); - - public string? Title - { - get => (string?)GetValue(TitleProperty); - set => SetValue(TitleProperty, value); - } - - public string? Description - { - get => (string?)GetValue(DescriptionProperty); - set => SetValue(DescriptionProperty, value); - } - - public bool ShowDescription - { - get => (bool)GetValue(ShowDescriptionProperty); - set => SetValue(ShowDescriptionProperty, value); - } -} \ No newline at end of file diff --git a/CRSim/Controls/Tile.xaml b/CRSim/Controls/Tile.xaml new file mode 100644 index 0000000..ea28410 --- /dev/null +++ b/CRSim/Controls/Tile.xaml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CRSim/Controls/Tile.xaml.cs b/CRSim/Controls/Tile.xaml.cs new file mode 100644 index 0000000..ace5801 --- /dev/null +++ b/CRSim/Controls/Tile.xaml.cs @@ -0,0 +1,49 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace CRSim.Controls; + +public sealed partial class Tile : UserControl +{ + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register(nameof(Title), typeof(string), typeof(Tile), new PropertyMetadata(null)); + + public string Description + { + get { return (string)GetValue(DescriptionProperty); } + set { SetValue(DescriptionProperty, value); } + } + + public static readonly DependencyProperty DescriptionProperty = + DependencyProperty.Register(nameof(Description), typeof(string), typeof(Tile), new PropertyMetadata(null)); + + public object Source + { + get { return (object)GetValue(SourceProperty); } + set { SetValue(SourceProperty, value); } + } + + public static readonly DependencyProperty SourceProperty = + DependencyProperty.Register(nameof(Source), typeof(object), typeof(Tile), new PropertyMetadata(null)); + + public string Link + { + get { return (string)GetValue(LinkProperty); } + set { SetValue(LinkProperty, value); } + } + + public static readonly DependencyProperty LinkProperty = + DependencyProperty.Register(nameof(Link), typeof(string), typeof(Tile), new PropertyMetadata(null)); + + + public Tile() + { + this.InitializeComponent(); + } +} \ No newline at end of file diff --git a/CRSim/Controls/TileGallery.xaml b/CRSim/Controls/TileGallery.xaml deleted file mode 100644 index 919b954..0000000 --- a/CRSim/Controls/TileGallery.xaml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim/Controls/TileGallery.xaml.cs b/CRSim/Controls/TileGallery.xaml.cs deleted file mode 100644 index 9b20fbd..0000000 --- a/CRSim/Controls/TileGallery.xaml.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace CRSim.Controls -{ - /// - /// Interaction logic for TileGallery.xaml - /// - public partial class TileGallery : UserControl - { - public TileGallery() - { - InitializeComponent(); - } - - private void ScrollBackButton_Click(object sender, RoutedEventArgs e) - { - double newOffSet = RootScrollViewer.HorizontalOffset - 210; - RootScrollViewer.ScrollToHorizontalOffset(newOffSet); - UpdateScrollButtonsVisibility(newOffSet); - } - - private void ScrollForwardButton_Click(object sender, RoutedEventArgs e) - { - double newOffSet = RootScrollViewer.HorizontalOffset + 210; - RootScrollViewer.ScrollToHorizontalOffset(newOffSet); - UpdateScrollButtonsVisibility(newOffSet); - } - - private void UpdateScrollButtonsVisibility() - { - double offset = RootScrollViewer.HorizontalOffset; - UpdateScrollButtonsVisibility(offset); - } - - private void UpdateScrollButtonsVisibility(double newOffset) - { - ScrollBackButton.Visibility = Visibility.Visible; - ScrollForwardButton.Visibility = Visibility.Visible; - - if (RootScrollViewer.ActualWidth < TilesPanel.ActualWidth) - { - if(newOffset == 0) - { - ScrollBackButton.Visibility = Visibility.Collapsed; - } - else if(newOffset >= RootScrollViewer.ScrollableWidth) - { - ScrollForwardButton.Visibility = Visibility.Collapsed; - } - } - else - { - ScrollBackButton.Visibility = Visibility.Collapsed; - ScrollForwardButton.Visibility = Visibility.Collapsed; - } - } - - private void RootScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e) - { - UpdateScrollButtonsVisibility(); - } - } -} diff --git a/CRSim/Converters/BooleanToVisibilityConverter.cs b/CRSim/Converters/BooleanToVisibilityConverter.cs deleted file mode 100644 index ae3e129..0000000 --- a/CRSim/Converters/BooleanToVisibilityConverter.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace CRSim.Converters -{ - public class BooleanToVisibilityConverter : IValueConverter - { - public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) - { - if (value is bool b) - { - return b ? Visibility.Visible : Visibility.Collapsed; - } - return null; - } - - public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} diff --git a/CRSim/Converters/IntToVisibilityConverter.cs b/CRSim/Converters/IntToVisibilityConverter.cs index f55625d..fcb212d 100644 --- a/CRSim/Converters/IntToVisibilityConverter.cs +++ b/CRSim/Converters/IntToVisibilityConverter.cs @@ -1,21 +1,17 @@ -namespace CRSim.Converters; +using Microsoft.UI.Xaml.Data; -/// -/// Converts a null value to Visibility.Collapsed -/// -internal sealed class IntToVisibilityConverter : IValueConverter +namespace CRSim.Converters { - public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + public partial class IntToVisibilityConverter : IValueConverter { - if(value is int intValue) + public object Convert(object value, Type targetType, object parameter, string language) { - return intValue == 0 ? Visibility.Collapsed : Visibility.Visible; + return (value is int num && bool.TryParse((string)parameter,out bool target) && (target ? num != 0 : num==0)) ? Visibility.Collapsed : Visibility.Visible; } - return Visibility.Collapsed; - } - public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - throw new NotImplementedException(); + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } } } diff --git a/CRSim/Converters/NullToVisibilityConverter.cs b/CRSim/Converters/NullToVisibilityConverter.cs index 42d3882..5c264a2 100644 --- a/CRSim/Converters/NullToVisibilityConverter.cs +++ b/CRSim/Converters/NullToVisibilityConverter.cs @@ -1,17 +1,19 @@ -namespace CRSim.Converters; +using Microsoft.UI.Xaml.Data; -/// -/// Converts a null value to Visibility.Collapsed -/// -internal sealed class NullToVisibilityConverter : IValueConverter +namespace CRSim.Converters { - public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + public partial class NullToVisibilityConverter : IValueConverter { - return value is null ? Visibility.Collapsed : Visibility.Visible; - } + public object Convert(object value, Type targetType, object parameter, string language) + { + bool invert = (parameter as string)?.ToLower() == "invert"; + bool isNull = value == null; + return invert ? isNull ? Visibility.Visible : Visibility.Collapsed : isNull ? Visibility.Collapsed : Visibility.Visible; + } - public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - throw new NotImplementedException(); + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } } } diff --git a/CRSim/Converters/PluginLoadStatusToVisibilityConverter.cs b/CRSim/Converters/PluginLoadStatusToVisibilityConverter.cs new file mode 100644 index 0000000..070e9d0 --- /dev/null +++ b/CRSim/Converters/PluginLoadStatusToVisibilityConverter.cs @@ -0,0 +1,34 @@ +using CRSim.Core.Enums; +using CRSim.Core.Models.Plugin; +using Microsoft.UI.Xaml.Data; + +namespace CRSim.Converters +{ + public partial class PluginLoadStatusToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if(value is PluginInfo plugin && parameter is string target) + { + PluginLoadStatus status = plugin.LoadStatus; + if ((plugin.IsUninstalling && target== "Loaded") || (plugin.RestartRequired && target == "NotLoaded")) + { + return Visibility.Collapsed; + } + if ((status == PluginLoadStatus.Loaded && target == "Loaded") || + (status == PluginLoadStatus.Error && target == "Error") || + (status == PluginLoadStatus.NotLoaded && target == "NotLoaded") || + (status == PluginLoadStatus.Disabled && target == "Disabled")) + { + return Visibility.Visible; + } + } + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/CRSim/Converters/TicketChecksToStringConverter.cs b/CRSim/Converters/TicketChecksToStringConverter.cs new file mode 100644 index 0000000..7ecb7f1 --- /dev/null +++ b/CRSim/Converters/TicketChecksToStringConverter.cs @@ -0,0 +1,29 @@ +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 => new { AreaName = w.Name, TicketName = tc.Name })) + .GroupBy(x => x.AreaName) + .Select(g => $"{g.Key}|{string.Join(",", g.Select(x => x.TicketName))}"); + + 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/Converters/TimeSpanToStringConverter.cs b/CRSim/Converters/TimeSpanToStringConverter.cs new file mode 100644 index 0000000..a28c49c --- /dev/null +++ b/CRSim/Converters/TimeSpanToStringConverter.cs @@ -0,0 +1,35 @@ +using Microsoft.UI.Xaml.Data; +using System.Globalization; + +namespace CRSim.Converters +{ + public partial class TimeSpanToStringConverter : IValueConverter + { + + public string Format { get; set; } = @"hh\:mm"; + public string Culture { get; set; } = "zh-CN"; + public string NullString { get; set; } = string.Empty; + public string ZeroString { get; set; } = string.Empty; + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is TimeSpan dateTime) + { + if(dateTime == new TimeSpan()) + { + return ZeroString; + } + var CultureInfo = new CultureInfo(Culture); + return (dateTime-dateTime.Days*TimeSpan.FromHours(24)).ToString(Format,CultureInfo); + } + if(value is null) + { + return NullString; + } + 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 20cdfb1..d3f8001 100644 --- a/CRSim/MainWindow.xaml +++ b/CRSim/MainWindow.xaml @@ -1,263 +1,61 @@ - - - + + - - - - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/CRSim/MainWindow.xaml.cs b/CRSim/MainWindow.xaml.cs index 055aa79..141f20b 100644 --- a/CRSim/MainWindow.xaml.cs +++ b/CRSim/MainWindow.xaml.cs @@ -1,251 +1,47 @@ -using CRSim.Core.Services; -using CRSim.ScreenSimulator.Models; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Threading.Tasks; -using System.Windows.Navigation; - + namespace CRSim { - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window +public sealed partial class MainWindow : Window { - private readonly IServiceProvider _serviceProvider; - private readonly Settings _settings; - private readonly INavigationService _navigationService; - public MainWindowViewModel ViewModel { get; } - public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService, ISettingsService settingsService) + public MainWindow() { - _serviceProvider = serviceProvider; - _settings = settingsService.GetSettings(); - DataContext = this; - ViewModel = viewModel; InitializeComponent(); - - UpdateWindowBackground(); - UpdateTitleBarButtonsVisibility(); - - _navigationService = navigationService; - _navigationService.Navigating += OnNavigating; - _navigationService.SetFrame(this.RootContentFrame); - _navigationService.Navigate(typeof(DashboardPage)); - - WindowChrome.SetWindowChrome( - this, - new WindowChrome - { - CaptionHeight = 50, - CornerRadius = default, - GlassFrameThickness = new Thickness(-1), - ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4), - UseAeroCaptionButtons = true - } - ); - - SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; - this.StateChanged += MainWindow_StateChanged; + ExtendsContentIntoTitleBar = true; + SetTitleBar(AppTitleBar); + SetWindowMinSize(1024, 768); } - private void MainWindow_StateChanged(object? sender, EventArgs e) + private void SetWindowMinSize(int minWidth, int minHeight) { - if (this.WindowState == WindowState.Maximized) + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); + var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd); + var appWindow = AppWindow.GetFromWindowId(windowId); + if (appWindow is not null) { - MainGrid.Margin = new Thickness(8); - } - else - { - MainGrid.Margin = default; - } - } - private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) - { - Dispatcher.Invoke(() => - { - UpdateTitleBarButtonsVisibility(); - }); - } - - private void OnNavigating(object? sender, NavigatingEventArgs e) - { - List list = ViewModel.GetNavigationItemHierarchyFromPageType(e.PageType); - - if (list.Count > 0) - { - TreeViewItem selectedTreeViewItem = null; - ItemsControl itemsControl = ControlsList; - foreach (ControlInfoDataItem item in list) + appWindow.SetPresenter(AppWindowPresenterKind.Overlapped); + if (appWindow.Presenter is OverlappedPresenter overlappedPresenter) { - if (itemsControl.ItemContainerGenerator.ContainerFromItem(item) is TreeViewItem tvi) - { - tvi.IsExpanded = true; - tvi.UpdateLayout(); - itemsControl = tvi; - selectedTreeViewItem = tvi; - } - } - - if (selectedTreeViewItem != null) - { - selectedTreeViewItem.IsSelected = true; - ControlsList_SelectedItemChanged(); + overlappedPresenter.PreferredMinimumWidth = minWidth; + overlappedPresenter.PreferredMinimumHeight = minHeight; } } } - private void UpdateWindowBackground() + private void nvSample_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs e) { - if ((!Utilities.IsBackdropDisabled() && - !Utilities.IsBackdropSupported())) - { - this.SetResourceReference(BackgroundProperty, "WindowBackground"); - } - } - private void ControlsList_SelectedItemChanged() - { - if (ControlsList.SelectedItem is ControlInfoDataItem navItem) - { - _navigationService.Navigate(navItem.PageType); - var tvi = ControlsList.ItemContainerGenerator.ContainerFromItem(navItem) as TreeViewItem; - tvi?.BringIntoView(); - } - } - private void UpdateTitleBarButtonsVisibility() - { - if (Utilities.IsBackdropDisabled() || !Utilities.IsBackdropSupported() || - SystemParameters.HighContrast == true) - { - MinimizeButton.Visibility = Visibility.Visible; - MaximizeButton.Visibility = Visibility.Visible; - CloseButton.Visibility = Visibility.Visible; - } - else - { - MinimizeButton.Visibility = Visibility.Collapsed; - MaximizeButton.Visibility = Visibility.Collapsed; - CloseButton.Visibility = Visibility.Collapsed; - } - } - private void MinimizeWindow(object sender, RoutedEventArgs e) - { - this.WindowState = WindowState.Minimized; - } - - private void MaximizeWindow(object sender, RoutedEventArgs e) - { - if (this.WindowState == WindowState.Maximized) - { - this.WindowState = WindowState.Normal; - MaximizeIcon.Text = "\uE922"; - } - else - { - this.WindowState = WindowState.Maximized; - MaximizeIcon.Text = "\uE923"; - } - } - - private void CloseWindow(object sender, RoutedEventArgs e) - { - Application.Current.Shutdown(); - } - - private void SettingsButton_Click(object sender, RoutedEventArgs e) - { - AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement((Button)sender); - peer.RaiseNotificationEvent( - AutomationNotificationKind.Other, - AutomationNotificationProcessing.ImportantMostRecent, - "Settings Page Opened", - "ButtonClickedActivity" - ); - } - - private void ControlsList_Loaded(object sender, RoutedEventArgs e) - { - if (ControlsList.Items.Count > 0) - { - TreeViewItem firstItem = (TreeViewItem)ControlsList.ItemContainerGenerator.ContainerFromItem(ControlsList.Items[0]); - if (firstItem != null) - { - firstItem.IsSelected = true; - } - } - } - - private void RootContentFrame_Navigated(object sender, NavigationEventArgs e) - { - ViewModel.UpdateCanNavigateBack(); - } - - private void ControlsList_PreviewKeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.Enter) - { - SelectedItemChanged(ControlsList.ItemContainerGenerator.ContainerFromItem((sender as TreeView).SelectedItem) as TreeViewItem); - } - } - - private void ControlsList_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) - { - if (e.OriginalSource is ToggleButton) + if(e.IsSettingsSelected) { + contentFrame.Navigate(typeof(Views.SettingsPage)); return; } - SelectedItemChanged(ControlsList.ItemContainerGenerator.ContainerFromItem((sender as TreeView).SelectedItem) as TreeViewItem); - } - private void SelectedItemChanged(TreeViewItem? tvi) - { - ControlsList_SelectedItemChanged(); - if (tvi != null) + if (nvSample.SelectedItem is NavigationViewItem selectedItem && selectedItem.Tag is string pageTag) { - tvi.IsExpanded = !tvi.IsExpanded; - } - } - - private async void Window_Loaded(object sender, RoutedEventArgs e) - { - if (_settings.ReopenUnclosedScreensOnLoad) - { - await ReopenUnclosedScreensAsync(); - } - } - - private async Task ReopenUnclosedScreensAsync() - { - List screenInfos = []; - if (File.Exists("Screen.json")) - { - var fileContent = await File.ReadAllTextAsync("Screen.json"); - screenInfos = JsonSerializer.Deserialize>(fileContent) ?? []; - } - else - { - return; - } - foreach (var screenInfo in screenInfos) - { - var Window = _serviceProvider.GetRequiredService(StylesInfoDataSource.Instance.StylesInfo.Where(x => x.ViewType.FullName == screenInfo.SelectedStyle).FirstOrDefault().ViewType) as dynamic; - Window.SessionID = screenInfo.SessionID; - if (!string.IsNullOrEmpty(screenInfo.Text)) + var pageType = Type.GetType($"CRSim.Views.{pageTag}"); + if (pageType != null) { - Window.ViewModel.Text = screenInfo.Text; + contentFrame.Navigate(pageType); } - if (screenInfo.SelectedLoaction != null && screenInfo.SelectedLoaction != 0) - { - Window.ViewModel.Location = screenInfo.SelectedLoaction.Value; - } - if (screenInfo.Video != null && screenInfo.Video != string.Empty) - { - Window.ViewModel.Video = new Uri(screenInfo.Video, UriKind.Absolute); - } - - Window.ViewModel.LoadData(_serviceProvider.GetRequiredService().GetStationByName(screenInfo.SelectedStationName), - screenInfo.SelectedTicketCheck ?? string.Empty, - screenInfo.SelectedPlatformName ?? string.Empty); ; - Window.Show(); } } } -} \ No newline at end of file +} diff --git a/CRSim/Models/ControlsInfoData.json b/CRSim/Models/ControlsInfoData.json deleted file mode 100644 index ed364b6..0000000 --- a/CRSim/Models/ControlsInfoData.json +++ /dev/null @@ -1,72 +0,0 @@ -[ - { - "UniqueId": "Home", - "Title": "主页", - "PageName": "DashboardPage", - "IconGlyph": "\uE80F" - }, - { - "UniqueId": "What's New", - "Title": "更新信息", - "PageName": "WhatsNewPage", - "IconGlyph": "\uEB51" - }, - { - "UniqueId": "Data Management", - "Title": "数据管理", - "PageName": "DataManagementPage", - "IconGlyph": "\uE81E", - "Description": "管理车站、车次、交路等信息。", - "ImagePath": "Assets/ControlImages/Data Management.png", - "IsGroup": true, - "Items": [ - { - "UniqueId": "Station", - "Title": "车站", - "PageName": "StationManagementPage", - "ImagePath": "Assets/ControlImages/Station Management.png", - "Description": "管理车站数据库。" - }, - { - "UniqueId": "Train Number", - "Title": "车次", - "PageName": "TrainNumberManagementPage", - "ImagePath": "Assets/ControlImages/TrainNumber Management.png", - "Description": "管理车次数据库。" - } - ] - }, - { - "UniqueId": "Start Simulation", - "Title": "开始模拟", - "PageName": "StartSimulationPage", - "IconGlyph": "\uE768", - "Description": "开始模拟信息显示。", - "ImagePath": "Assets/ControlImages/Start Simulation.png", - "IsGroup": true, - "Items": [ - { - "UniqueId": "Screen", - "Title": "引导屏", - "PageName": "ScreenSimulationPage", - "ImagePath": "Assets/ControlImages/Screen Simulation.png", - "Description": "模拟旅客引导显示屏。" - }, - { - "UniqueId": "Gate", - "Title": "闸机", - "PageName": "GateSimulationPage", - "ImagePath": "Assets/ControlImages/Gate Simulation.png", - "Description": "模拟闸机。(新建文件夹中)" - }, - { - "UniqueId": "12306", - "Title": "12306", - "PageName": "WebsiteSimulationPage", - "ImagePath": "Assets/ControlImages/Website Simulation.png", - "Description": "模拟12306网站。" - } - ] - } - -] \ No newline at end of file diff --git a/CRSim/Models/ControlsInfoDataSource.cs b/CRSim/Models/ControlsInfoDataSource.cs deleted file mode 100644 index 5784221..0000000 --- a/CRSim/Models/ControlsInfoDataSource.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace CRSim.Models -{ - public class ControlInfoDataItem - { - public string UniqueId { get; set; } - public string Title { get; set; } - public string Description { get; set; } - public string IconGlyph { get; set; } - public string ImagePath { get; set; } - public string PageName { get; set; } - public bool IsGroup { get; set; } = false; - - public Type PageType - { - get - { - return _assembly.GetType($"CRSim.Views.{PageName}"); - } - } - - public Uri ImageSource - { - get - { - return new Uri($"pack://application:,,,/{ImagePath}"); - } - } - - public ObservableCollection Items { get; set; } = []; - - public override string ToString() - { - return Title; - } - - private static readonly Assembly _assembly = typeof(ControlsInfoDataSource).Assembly; - } - - public sealed class ControlsInfoDataSource - { - - #region Singleton - - private static readonly ControlsInfoDataSource _instance; - - public static ControlsInfoDataSource Instance - { - get - { - return _instance; - } - } - - static ControlsInfoDataSource() - { - _instance = new ControlsInfoDataSource(); - } - - private ControlsInfoDataSource() - { - var jsonText = ReadControlsData(); - ControlsInfo = JsonSerializer.Deserialize>(jsonText); - } - - #endregion - - public ICollection ControlsInfo { get; } - - private static string ReadControlsData() - { - var assembly = typeof(ControlsInfoDataSource).Assembly; - var resourceName = "CRSim.Models.ControlsInfoData.json"; - - using var stream = assembly.GetManifestResourceStream(resourceName); - using var reader = new System.IO.StreamReader(stream); - return reader.ReadToEnd(); - } - - public ICollection GetControlsInfo(string groupName) - { - return ControlsInfo.FirstOrDefault(x => x.UniqueId == groupName)?.Items; - } - - public ICollection GetAllControlsInfo() - { - ObservableCollection allControls = []; - foreach (ControlInfoDataItem ci in ControlsInfo) - { - if(ci.UniqueId != "Samples") - { - var items = ci.Items; - foreach (ControlInfoDataItem item in items) - { - allControls.Add(item); - } - } - } - - return allControls; - } - - public ICollection GetGroupedControlsInfo() - { - return [.. ControlsInfo.Where(x => x.IsGroup == true)]; - } - } -} diff --git a/CRSim/Models/InfoItem.cs b/CRSim/Models/InfoItem.cs new file mode 100644 index 0000000..2512c5d --- /dev/null +++ b/CRSim/Models/InfoItem.cs @@ -0,0 +1,9 @@ +namespace CRSim.Models +{ + public class InfoItem + { + public string IconGlyph { get; set; } + public string Title { get; set; } + public string Detail { get; set; } + } +} diff --git a/CRSim/Models/NavigatedEventArgs.cs b/CRSim/Models/NavigatedEventArgs.cs deleted file mode 100644 index 1ae8ada..0000000 --- a/CRSim/Models/NavigatedEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace CRSim.Models -{ - public class NavigatingEventArgs - { - public Type? PageType { get; set; } = null; - - public NavigatingEventArgs() { } - - public NavigatingEventArgs(Type pageType) - { - PageType = pageType; - } - } -} diff --git a/CRSim/Package.appxmanifest b/CRSim/Package.appxmanifest new file mode 100644 index 0000000..b1cf795 --- /dev/null +++ b/CRSim/Package.appxmanifest @@ -0,0 +1,51 @@ + + + + + + + + + + 国铁信息显示模拟 + 电排骨 + Assets\CRSimIcon.png + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CRSim/Properties/GlobalUsing.cs b/CRSim/Properties/GlobalUsing.cs new file mode 100644 index 0000000..c5a27e9 --- /dev/null +++ b/CRSim/Properties/GlobalUsing.cs @@ -0,0 +1,37 @@ +global using System; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Text.Json; +global using System.Threading.Tasks; +global using System.Collections.Generic; +global using System.Collections.ObjectModel; +global using System.Reflection; +global using System.Timers; +global using System.Threading; + +global using Microsoft.UI.Xaml; +global using Microsoft.UI.Xaml.Controls; +global using Microsoft.UI.Dispatching; +global using Microsoft.UI.Windowing; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.DependencyInjection; + +global using CommunityToolkit.Mvvm.ComponentModel; +global using CommunityToolkit.Mvvm.Input; + +global using CRSim.Views; +global using CRSim.Models; +global using CRSim.Services; +global using CRSim.ViewModels; +global using CRSim.ScreenSimulator; +global using CRSim.Core.Utils; +global using CRSim.Core.Models; +global using CRSim.Core.Services; +global using CRSim.Core.Abstractions; + +global using OfficeOpenXml; + +global using CommunityToolkit.WinUI; + +global using Windows.Graphics; \ No newline at end of file diff --git a/CRSim/Properties/GlobalUsings.cs b/CRSim/Properties/GlobalUsings.cs deleted file mode 100644 index cb4ea48..0000000 --- a/CRSim/Properties/GlobalUsings.cs +++ /dev/null @@ -1,35 +0,0 @@ -global using System.IO; -global using System.Diagnostics; -global using System.Globalization; -global using System.ComponentModel; -global using System.Collections.ObjectModel; -global using System.Reflection; -global using System.Text; -global using System.Text.Json; -global using System.Windows; -global using System.Windows.Data; -global using System.Windows.Media; -global using System.Windows.Threading; -global using System.Windows.Input; -global using System.Windows.Shell; -global using System.Windows.Controls; -global using System.Windows.Controls.Primitives; -global using System.Windows.Automation; -global using System.Windows.Automation.Peers; - -global using Microsoft.Win32; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Hosting; - -global using CommunityToolkit.Mvvm.ComponentModel; -global using CommunityToolkit.Mvvm.Input; - -global using CRSim.Core.Services; -global using CRSim.Core.Models; - -global using CRSim.Services; -global using CRSim.Models; -global using CRSim.Views; -global using CRSim.ViewModels; - -global using CRSim.WebsiteSimulator; diff --git a/CRSim/Properties/launchSettings.json b/CRSim/Properties/launchSettings.json new file mode 100644 index 0000000..799e74e --- /dev/null +++ b/CRSim/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "CRSim (Package)": { + "commandName": "MsixPackage" + }, + "CRSim (Unpackaged)": { + "commandName": "Project", + "commandLineArgs": "-debug" + } + } +} \ No newline at end of file diff --git a/CRSim/Resources/PageStyles.xaml b/CRSim/Resources/PageStyles.xaml deleted file mode 100644 index 057c0a8..0000000 --- a/CRSim/Resources/PageStyles.xaml +++ /dev/null @@ -1,96 +0,0 @@ - - - 12 - 14 - 20 - 28 - 40 - 68 - - - M21.7999992370605,0L19.220495223999,0.26007080078125 16.81787109375,1.00595712661743 14.6436157226563,2.18616962432861 12.7492189407349,3.74921894073486 11.1861696243286,5.64361572265625 10.0059566497803,7.81787109375 9.26007080078125,10.2204961776733 9,12.8000001907349 9.65248012542725,16.8459720611572 11.4694375991821,20.3591785430908 14.2401514053345,23.1291217803955 17.7539005279541,24.9453010559082 18.4305686950684,24.8080005645752 18.6273498535156,24.3296756744385 18.6207065582275,23.4247951507568 18.609375,21.9468746185303 16.4340572357178,22.0373229980469 15.1187467575073,21.4822216033936 14.4708204269409,20.7821025848389 14.2976503372192,20.4375 13.8297338485718,19.5214366912842 13.3685493469238,18.947265625 12.8765497207642,18.5656261444092 12.3995819091797,18.1091804504395 12.4844465255737,17.87890625 12.7874250411987,17.7974605560303 12.9647998809814,17.7875003814697 13.8134965896606,18.0311241149902 14.4276065826416,18.4802703857422 14.8007507324219,18.9127178192139 14.926549911499,19.1062507629395 15.8880548477173,20.1437015533447 16.9443283081055,20.494140625 17.9229640960693,20.416259765625 18.6515502929688,20.1687507629395 18.9645938873291,19.1242198944092 19.4640502929688,18.4593753814697 17.3543262481689,18.0241260528564 15.4833002090454,17.014066696167 14.1450357437134,15.1450166702271 13.6336002349854,12.1328001022339 13.9853601455688,10.2268438339233 14.9500007629395,8.69764995574951 14.7027282714844,7.54188776016235 14.7441072463989,6.53565359115601 15.0765495300293,5.30859994888306 15.2825078964233,5.28076791763306 15.9191312789917,5.34375619888306 17.0145378112793,5.71729135513306 18.596851348877,6.62109994888306 21.799976348877,6.19062519073486 25.004674911499,6.62265014648438 26.5845413208008,5.71818733215332 27.6791000366211,5.34472513198853 28.315746307373,5.28210020065308 28.5218753814697,5.31015014648438 28.8556652069092,6.53784370422363 28.8976573944092,7.5438346862793 28.6499996185303,8.69764995574951 29.6154251098633,10.2268533706665 29.9656257629395,12.1328001022339 29.453296661377,15.1497011184692 28.1123065948486,17.0164012908936 26.2366523742676,18.020601272583 24.120325088501,18.4500007629395 24.7275562286377,19.3355484008789 24.9890747070313,20.8187503814697 24.9804744720459,23.0584030151367 24.9718742370605,24.3312511444092 25.1693305969238,24.8128852844238 25.8531246185303,24.9453010559082 29.3641395568848,23.1273632049561 32.1326217651367,20.3568344116211 33.948070526123,16.8442134857178 34.5999984741211,12.8000001907349 34.3399276733398,10.2204961776733 33.5940399169922,7.81787109375 32.4138298034668,5.64361572265625 30.8507804870605,3.74921894073486 28.9563827514648,2.18616962432861 26.7821273803711,1.00595712661743 24.3795032501221,0.26007080078125 21.7999992370605,0z - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CRSim/Resources/Templates.xaml b/CRSim/Resources/Templates.xaml deleted file mode 100644 index 2ed7c82..0000000 --- a/CRSim/Resources/Templates.xaml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file 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 00293d1..8739e1c 100644 --- a/CRSim/Services/DialogService.cs +++ b/CRSim/Services/DialogService.cs @@ -1,143 +1,254 @@ - +using CRSim.Core.Models; +using Microsoft.Win32; +using Windows.Storage; +using Windows.Storage.Pickers; + namespace CRSim.Services { public class DialogService : IDialogService { - private Window? _owner; - public void SetOwner(Window owner) + public XamlRoot? XamlRoot { get; set; } + public async Task GetInputAsync(string title,string placeholder) { - _owner = owner; - } - public string? GetInput(string title) - { - var dialog = new InputDialog(title) + ContentDialog dialog = new() { - Owner = _owner + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = title, + PrimaryButtonText = "确定", + CloseButtonText = "取消", + DefaultButton = ContentDialogButton.Primary, + Content = new InputDialog(placeholder) }; - bool? result = dialog.ShowDialog(); - if (result == true) + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) { - string input = dialog.UserInput; + var inputDialog = dialog.Content as InputDialog; + string input = inputDialog?.InputText??string.Empty; return input; } return null; } - public bool GetConfirm(string title) + public async Task GetConfirmAsync(string title) { - var dialog = new ConfirmDialog(title) + ContentDialog dialog = new() { - Owner = _owner + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = "警告", + PrimaryButtonText = "确定", + CloseButtonText = "取消", + DefaultButton = ContentDialogButton.Primary, + Content = title }; - bool? result = dialog.ShowDialog(); - if (result == true) + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) { return true; } return false; } - public void ShowMessage(string title, string message) + public async Task ShowMessageAsync(string title, string message) { - dynamic dialog = message.Length > 25 ? new LongMessageDialog(title, message) : new MessageDialog(title, message); - dialog.Owner = _owner; - dialog.ShowDialog(); + ContentDialog dialog = new() + { + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = title, + PrimaryButtonText = "确定", + DefaultButton = ContentDialogButton.Primary, + Content = message + }; + await dialog.ShowAsync(); + } + public async Task ShowTextAsync(string title, string message) + { + ContentDialog dialog = new() + { + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = title, + PrimaryButtonText = "确定", + DefaultButton = ContentDialogButton.Primary, + Content = new TextDialog(message) + }; + await dialog.ShowAsync(); } - public (string, List) GetInputTicketCheck(List waitingAreaNames) + public string? GetFile(string[] extensions) { - var dialog = new TicketCheckDialog(waitingAreaNames) - { - Owner = _owner - }; - bool? result = dialog.ShowDialog(); - if (result == true) - { - return (dialog.WaitingAreaName, dialog.TicketCheck); - } - return (null,null); - } + var dlg = new OpenFileDialog(); + dlg.Multiselect = false; - public string? GetFile(string filter) - { - OpenFileDialog openFileDialog = new() - { - Filter = filter - }; - if (openFileDialog.ShowDialog() == true) - { - string filePath = openFileDialog.FileName; - return filePath; - } - return null; - } - public TrainStop? GetInputTrainNumberStop(TrainStop? t) - { - var dialog = new TrainNumberStopDialog(t) - { - Owner = _owner - }; - bool? result = dialog.ShowDialog(); + 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) { - return dialog.GeneratedTrainStop; + return dlg.FileName; } return null; } - public TrainStop? GetInputTrainStop(List ticketChecks, List platforms) + public async Task GetInputTrainNumberStopAsync() { - var dialog = new TrainStopDialog(ticketChecks,platforms) + bool isButtonEnabled = false; + ContentDialog dialog = new() { - Owner = _owner + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = "编辑车次", + PrimaryButtonText = "确定", + CloseButtonText = "取消", + DefaultButton = ContentDialogButton.Primary, + IsPrimaryButtonEnabled = false }; - bool? result = dialog.ShowDialog(); - if (result == true) + var platformDialog = new TrainNumberStopDialog(isValid => dialog.IsPrimaryButtonEnabled = isValid); + dialog.Content = platformDialog; + dialog.PrimaryButtonClick += (s, e) => { - return dialog.GeneratedTrainStop; + platformDialog.GenerateTrainStop(); + }; + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + var inputDialog = dialog.Content as TrainNumberStopDialog; + return inputDialog.GeneratedTrainStop; + } + return null; + } + public async Task EditTrainNumberStopAsync(TrainStop trainStop) + { + bool isButtonEnabled = false; + ContentDialog dialog = new() + { + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = "编辑车次", + PrimaryButtonText = "确定", + CloseButtonText = "取消", + DefaultButton = ContentDialogButton.Primary, + IsPrimaryButtonEnabled = false + }; + var platformDialog = new TrainNumberStopDialog(trainStop, isValid => dialog.IsPrimaryButtonEnabled = isValid); + dialog.Content = platformDialog; + dialog.PrimaryButtonClick += (s, e) => + { + platformDialog.GenerateTrainStop(); + }; + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + var inputDialog = dialog.Content as TrainNumberStopDialog; + return inputDialog.GeneratedTrainStop; } return null; } - public TrainStop? GetInputTrainStop(List ticketChecks, List platforms, TrainStop trainStop) + public async Task GetInputTrainStopAsync(List waitingAreas, List platforms) { - var dialog = new TrainStopDialog(ticketChecks,platforms, trainStop) + bool isButtonEnabled = false; + ContentDialog dialog = new() { - Owner = _owner + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = "编辑车次", + PrimaryButtonText = "确定", + CloseButtonText = "取消", + DefaultButton = ContentDialogButton.Primary, + IsPrimaryButtonEnabled = false }; - bool? result = dialog.ShowDialog(); - if (result == true) + var platformDialog = new TrainStopDialog(waitingAreas, platforms,isValid => dialog.IsPrimaryButtonEnabled = isValid); + dialog.Content = platformDialog; + dialog.PrimaryButtonClick += (s, e) => { - return dialog.GeneratedTrainStop; - } - return null; - - } - - public string? SaveFile(string filter, string f) - { - SaveFileDialog saveFileDialog = new() - { - FileName = f, - CheckPathExists = true, - Filter = filter + platformDialog.GenerateTrainStop(); }; - if (saveFileDialog.ShowDialog() == true) + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) { - string filePath = saveFileDialog.FileName; - return filePath; + var inputDialog = dialog.Content as TrainStopDialog; + return inputDialog.GeneratedTrainStop; } return null; } - public List? GetInputPlatform() + public async Task EditInputTrainStopAsync(List waitingAreas, List platforms, TrainStop trainStop) { - var dialog = new PlatformDialog() + bool isButtonEnabled = false; + ContentDialog dialog = new() { - Owner = _owner + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = "编辑车次", + PrimaryButtonText = "确定", + CloseButtonText = "取消", + DefaultButton = ContentDialogButton.Primary, + IsPrimaryButtonEnabled = false }; - bool? result = dialog.ShowDialog(); + var platformDialog = new TrainStopDialog(waitingAreas, platforms, trainStop, isValid => dialog.IsPrimaryButtonEnabled = isValid); + dialog.Content = platformDialog; + dialog.PrimaryButtonClick += (s, e) => + { + platformDialog.GenerateTrainStop(); + }; + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + var inputDialog = dialog.Content as TrainStopDialog; + return inputDialog.GeneratedTrainStop; + } + return null; + } + + public string? SaveFile(string extension, string suggestedFileName) + { + 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 dialog.GeneratedPlatforms; + return dlg.FileName; + } + return null; + } + + public async Task?> GetInputPlatformAsync() + { + bool isButtonEnabled = false; + ContentDialog dialog = new() + { + XamlRoot = XamlRoot, + Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style, + Title = "新增站台", + PrimaryButtonText = "确定", + CloseButtonText = "取消", + DefaultButton = ContentDialogButton.Primary, + IsPrimaryButtonEnabled = false + }; + var platformDialog = new PlatformDialog(isValid => dialog.IsPrimaryButtonEnabled = isValid); + dialog.Content = platformDialog; + dialog.PrimaryButtonClick += (s, e) => + { + platformDialog.GeneratePlatform(); + }; + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + var inputDialog = dialog.Content as PlatformDialog; + return inputDialog.GeneratedPlatforms; } return null; } diff --git a/CRSim/Services/IDialogService.cs b/CRSim/Services/IDialogService.cs index 6c72c86..00f98bf 100644 --- a/CRSim/Services/IDialogService.cs +++ b/CRSim/Services/IDialogService.cs @@ -2,16 +2,17 @@ { public interface IDialogService { - string? GetFile(string filter); + string? GetFile(string[] filter); string? SaveFile(string filter,string fileName); - string? GetInput(string title); - bool GetConfirm(string title); - void ShowMessage(string title, string message); - TrainStop? GetInputTrainNumberStop(TrainStop? t); - TrainStop? GetInputTrainStop(List ticketChecks, List platforms); - TrainStop? GetInputTrainStop(List ticketChecks, List platforms, TrainStop trainStop); - List? GetInputPlatform(); - (string, List) GetInputTicketCheck(List waitingAreaNames); - void SetOwner(Window owner); + Task GetInputAsync(string title,string placeholder); + Task GetConfirmAsync(string title); + Task ShowMessageAsync(string title, string message); + Task ShowTextAsync(string title, string message); + Task GetInputTrainNumberStopAsync(); + Task EditTrainNumberStopAsync(TrainStop trainStop); + Task GetInputTrainStopAsync(List waitingAreas, List platforms); + Task EditInputTrainStopAsync(List waitingAreas, List platforms, TrainStop trainStop); + Task?> GetInputPlatformAsync(); + XamlRoot? XamlRoot { get; set; } } } diff --git a/CRSim/Services/INavigationService.cs b/CRSim/Services/INavigationService.cs deleted file mode 100644 index f016eb6..0000000 --- a/CRSim/Services/INavigationService.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace CRSim.Services -{ - public interface INavigationService - { - void Navigate(Type type); - - void NavigateTo(Type type); - - void SetFrame(Frame frame); - - void NavigateBack(); - - void NavigateForward(); - - bool IsBackHistoryNonEmpty(); - - event EventHandler Navigating; - } -} diff --git a/CRSim/Services/NavigationService.cs b/CRSim/Services/NavigationService.cs deleted file mode 100644 index cae170b..0000000 --- a/CRSim/Services/NavigationService.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace CRSim.Services -{ - public class NavigationService(IServiceProvider serviceProvider) : INavigationService - { - private Frame _frame; - - private Type _currentPageType = null; - - private readonly Stack _history = new(); - private readonly Stack _future = new(); - - private readonly IServiceProvider _serviceProvider = serviceProvider; - - public event EventHandler Navigating; - - public void SetFrame(Frame frame) - { - _frame = frame; - } - - public void NavigateTo(Type type) - { - if (type != null) - { - _future.Clear(); - RaiseNavigatingEvent(type); - } - } - - public void Navigate(Type type) - { - if (type != null) - { - _history.Push(_currentPageType); - _currentPageType = type; - var page = _serviceProvider.GetRequiredService(type); - _frame.Navigate(page); - } - } - - public void NavigateBack() - { - if (_history.Count > 0) - { - Type type = _history.Pop(); - if (type != null) - { - _future.Push(type); - RaiseNavigatingEvent(type); - _history.Pop(); - } - } - } - - public void NavigateForward() - { - if (_future.Count > 0) - { - Type type = _future.Pop(); - if (type != null) - { - _history.Push(type); - RaiseNavigatingEvent(type); - } - } - } - - public void RaiseNavigatingEvent(Type type) - { - Navigating?.Invoke(this, new NavigatingEventArgs(type)); - } - - public bool IsBackHistoryNonEmpty() - { - var item = _history.Peek(); - if (item == null) - { - return false; - } - else - { - return true; - } - } - } -} diff --git a/CRSim/Styles/Controls.xaml b/CRSim/Styles/Controls.xaml new file mode 100644 index 0000000..0b2b26c --- /dev/null +++ b/CRSim/Styles/Controls.xaml @@ -0,0 +1,17 @@ + + + + diff --git a/CRSim/Utilities.cs b/CRSim/Utilities.cs deleted file mode 100644 index 68dc9f8..0000000 --- a/CRSim/Utilities.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace CRSim -{ - internal static class Utilities - { - public static bool IsBackdropSupported() - { - var os = Environment.OSVersion; - var version = os.Version; - - return version.Major >= 10 && version.Build >= 22621; - } - - public static bool IsBackdropDisabled() - { - var appContextBackdropData = AppContext.GetData("Switch.System.Windows.Appearance.DisableFluentThemeWindowBackdrop"); - bool disableFluentThemeWindowBackdrop = false; - - if (appContextBackdropData != null) - { - disableFluentThemeWindowBackdrop = bool.Parse(Convert.ToString(appContextBackdropData)); - } - - return disableFluentThemeWindowBackdrop; - } - public static TimeSpan RoundToMinute(string timeString) - { - if (TimeSpan.TryParse(timeString, out var time)) - return TimeSpan.FromMinutes(Math.Round(time.TotalMinutes)); - return default; - } - } - -} diff --git a/CRSim/ViewModels/DashboardPageViewModel.cs b/CRSim/ViewModels/DashboardPageViewModel.cs new file mode 100644 index 0000000..5410ba3 --- /dev/null +++ b/CRSim/ViewModels/DashboardPageViewModel.cs @@ -0,0 +1,31 @@ +using System.Windows; + +namespace CRSim.ViewModels; +public partial class DashboardPageViewModel : ObservableObject +{ + [ObservableProperty] + public partial string? UpdateMessage { get; set; } = null; + public StyleManager StyleManager { get; } + private readonly INetworkService _networkService; + private readonly ISettingsService _settingsService; + public DashboardPageViewModel(StyleManager styleManager,INetworkService networkService,ISettingsService settingsService) + { + StyleManager = styleManager; + _networkService = networkService; + _settingsService = settingsService; + InitializeAsync(); + } + private async void InitializeAsync() + { + var url = _settingsService.GetSettings().ApiUri.Contains("47") + ? "https://api.github.com/repos/denglihong2007/CRSim/releases/latest" + : "https://crsim.com.cn/api/version"; + + var updateInfo = await _networkService.GetUpdateAsync(url); + + if (updateInfo is not null && updateInfo.Name != Assembly.GetExecutingAssembly().GetName().Version.ToString()) + { + UpdateMessage = $"有新版本 {updateInfo.Name} 可用,请前往“设置”下载安装更新!"; + } + } +} \ No newline at end of file diff --git a/CRSim/ViewModels/Pages/DashboardPageViewModel.cs b/CRSim/ViewModels/Pages/DashboardPageViewModel.cs deleted file mode 100644 index b336658..0000000 --- a/CRSim/ViewModels/Pages/DashboardPageViewModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace CRSim.ViewModels -{ - public partial class DashboardPageViewModel(INavigationService navigationService) : ObservableObject - { - [ObservableProperty] - private ICollection _navigationCards = ControlsInfoDataSource.Instance.GetGroupedControlsInfo(); - - private readonly INavigationService _navigationService = navigationService; - - [RelayCommand] - public void Navigate(object pageType){ - if (pageType is Type page) - { - _navigationService.NavigateTo(page); - } - } - - - } -} diff --git a/CRSim/ViewModels/Pages/DataManagement/StationManagementPageViewModel.cs b/CRSim/ViewModels/Pages/DataManagement/StationManagementPageViewModel.cs deleted file mode 100644 index 5bd8486..0000000 --- a/CRSim/ViewModels/Pages/DataManagement/StationManagementPageViewModel.cs +++ /dev/null @@ -1,670 +0,0 @@ -using OfficeOpenXml; - -namespace CRSim.ViewModels; - -public partial class StationManagementPageViewModel : ObservableObject -{ - public class TicketCheck - { - public string WaitingAreaName { get; set; } - public string Name { get; set; } - } - [ObservableProperty] - private string _pageTitle = "车站管理"; - - [ObservableProperty] - private string _pageDescription = "看起来不是很温馨的提醒:改完记得保存! Owo"; - - [ObservableProperty] - private bool _isSelected = false; - - [ObservableProperty] - private List _stationNames = []; - - [ObservableProperty] - private Station _selectedStation = new(); - - public ObservableCollection WaitingAreaNames { get; private set; } = []; - public ObservableCollection Platforms { get; private set; } = []; - - public ObservableCollection TicketChecks { get; private set; } = []; - - public ObservableCollection TrainStops { get; private set; } = []; - - private readonly IDatabaseService _databaseService; - - private readonly IDialogService _dialogService; - - private readonly INetworkService _networkService; - public StationManagementPageViewModel(IDatabaseService databaseService,IDialogService dialogService,INetworkService networkService) - { - _databaseService = databaseService; - _dialogService = dialogService; - _networkService = networkService; - RefreshStations(); - } - - #region Station - public void RefreshStations() - { - var stationsList = _databaseService.GetAllStations(); - StationNames = [.. stationsList.Select(s => s.Name)]; - } - [RelayCommand] - public void StationSelected(object selectedStation) - { - WaitingAreaNames.Clear(); - Platforms.Clear(); - TicketChecks.Clear(); - TrainStops.Clear(); - if (selectedStation is string selectedStationName) - { - SelectedStation = _databaseService.GetStationByName(selectedStationName); - 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 }); - } - } - foreach (TrainStop trainStop in SelectedStation.TrainStops) - { - TrainStops.Add(trainStop); - } - foreach (var platform in SelectedStation.Platforms) - { - Platforms.Add(platform); - } - IsSelected = true; - } - if (selectedStation == null) - { - IsSelected = false; - } - } - [RelayCommand] - public async Task AddStation() - { - string? station = _dialogService.GetInput("请输入车站名称"); - if (station != null) - { - if (StationNames.Contains(station)) - { - _dialogService.ShowMessage("添加失败", $"车站 {station} 已存在。"); - return; - } - _databaseService.AddStationByName(station); - await _databaseService.SaveData(); - RefreshStations(); - } - } - [RelayCommand] - public async Task DeleteStation(object selectedStation) - { - if (selectedStation is string s && !string.IsNullOrEmpty(s)) - { - _databaseService.DeleteStation(_databaseService.GetStationByName(s)); - await _databaseService.SaveData(); - RefreshStations(); - } - } - [RelayCommand] - public async Task DeleteAllStations() - { - if (!_dialogService.GetConfirm("当前操作会删除全部车站,是否继续?")) return; - foreach (string stationName in StationNames) - { - _databaseService.DeleteStation(_databaseService.GetStationByName(stationName)); - } - await _databaseService.SaveData(); - RefreshStations(); - } - public static Station GenerateStation(string stationName, ObservableCollection waitingAreaNames, ObservableCollection ticketChecks, ObservableCollection trainStops, ObservableCollection platforms) - { - var station = new Station - { - Name = stationName, - WaitingAreas = [.. waitingAreaNames.Select(name => new WaitingArea { Name = name })], - 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 = _dialogService.GetFile("可执行程序文件 (*.exe)|*.exe|所有文件 (*.*)|*.*"); - if (path == null) return; - try - { - await _databaseService.ImportStationFrom7D(path); - await _databaseService.SaveData(); - RefreshStations(); - } - catch (Exception e) - { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); - } - } - #endregion - - #region TicketCheck - [RelayCommand] - public void AddTicketCheck() - { - (string waitingAreaName, List ticketChecks) = _dialogService.GetInputTicketCheck([.. WaitingAreaNames]); - if (ticketChecks != null && waitingAreaName != null) - { - foreach (string ticketCheck in ticketChecks) - { - if (TicketChecks.Where(x => x.WaitingAreaName == waitingAreaName).Select(x => x.Name).Contains(ticketCheck)) - { - _dialogService.ShowMessage("添加失败", $"编号 {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 void DeleteAllTicketCheck() - { - if (!_dialogService.GetConfirm("当前操作会删除全部检票口,是否继续?")) return; - TicketChecks.Clear(); - } - #endregion - - #region WaitingArea - [RelayCommand] - public void AddWaitingArea() - { - string? waitingAreaName = _dialogService.GetInput("请输入候车室名称"); - if (waitingAreaName != null) - { - if (WaitingAreaNames.Contains(waitingAreaName)) - { - _dialogService.ShowMessage("添加失败", $"名称 {waitingAreaName} 候车室已存在。"); - return; - } - WaitingAreaNames.Add(waitingAreaName); - } - } - [RelayCommand] - public void DeleteWaitingArea(object selectedWaitingArea) - { - if (selectedWaitingArea is string n) - { - WaitingAreaNames.Remove(n); - } - } - [RelayCommand] - public void DeleteAllWaitingArea() - { - if (!_dialogService.GetConfirm("当前操作会删除全部候车室,是否继续?")) return; - WaitingAreaNames.Clear(); - } - #endregion - - #region Platform - [RelayCommand] - public void AddPlatform() - { - var platforms = _dialogService.GetInputPlatform(); - if (platforms != null) - { - foreach (var platform in platforms) - { - if (Platforms.Any(x => x.Name == platform.Name)) - { - _dialogService.ShowMessage("添加失败", $"编号 {platform.Name} 站台已存在。"); - return; - } - Platforms.Add(platform); - } - } - } - [RelayCommand] - public void DeletePlatform(object selectedPlatform) - { - if (selectedPlatform is Platform p) - { - Platforms.Remove(p); - } - } - [RelayCommand] - public void DeleteAllPlatform() - { - if (!_dialogService.GetConfirm("当前操作会删除全部站台,是否继续?")) return; - Platforms.Clear(); - } - #endregion - - #region TrainStop - [RelayCommand] - public void ImportFromTrainNumbers() - { - if (!CheckCanImport()) return; - var trainNumbers = _databaseService.GetAllTrainNumbers(); - if (trainNumbers.Count != 0) - { - TrainStops.Clear(); - } - foreach (TrainNumber trainNumber in trainNumbers) - { - var t = trainNumber.TimeTable.FirstOrDefault(x => x.Station == SelectedStation.Name); - if (t == null) continue; - t.Number = trainNumber.Number; - t = RandomTrainStopProperties(t); - t.Origin = trainNumber.TimeTable.First().Station; - t.Terminal = trainNumber.TimeTable.Last().Station; - TrainStops.Add(t); - } - } - [RelayCommand] - public async Task ImportFromLulutong() - { - if (!CheckCanImport()) return; - var path = _dialogService.GetFile("CSV 文件 (*.csv)|*.csv|所有文件 (*.*)|*.*"); - if (path == null) return; - try - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - var lines = new List(); - var reader = new StreamReader(path, Encoding.GetEncoding("GBK")); - while (!reader.EndOfStream) - { - var line = await reader.ReadLineAsync(); - lines.Add(line); - } - foreach (var line in lines.Skip(13)) - { - var data = line.Split(","); - var firstColumn = data[0].Trim(); - if (!string.IsNullOrEmpty(firstColumn) && char.IsLetterOrDigit(firstColumn[0])) - { - int spaceIndex = firstColumn.IndexOf(' '); - if (spaceIndex != -1) - { - firstColumn = firstColumn[..spaceIndex]; - } - if (TrainStops.Any(x => x.Number == firstColumn)) continue; - TimeSpan? departureTime = TimeSpan.Parse(data[4]); - TimeSpan? arrivalTime = departureTime.Value.Subtract(TimeSpan.FromMinutes(2)); - if (data[1].Split("-")[0] == SelectedStation.Name) - { - arrivalTime = null; - } - else if (data[1].Split("-")[1] == SelectedStation.Name) - { - departureTime = null; - } - var trainStop = new TrainStop() - { - Number = firstColumn, - Origin = data[1].Split("-")[0], - Terminal = data[1].Split("-")[1], - ArrivalTime = arrivalTime, - DepartureTime = departureTime, - }; - TrainStops.Add(RandomTrainStopProperties(trainStop)); - } - } - } - catch (Exception e) - { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); - } - } - [RelayCommand] - public async Task ImportFrompyETRC() - { - if (!CheckCanImport()) return; - - var path = _dialogService.GetFile("pyETRC 运行图文件 (*.pyetgr;*.json)|*.pyetgr;*.json|pyETRC 车次数据库文件 (*.pyetdb;*.json)|*.pyetdb;*.json|所有文件 (*.*)|*.*"); - if (path == null) return; - try - { - using var fs = File.OpenRead(path); - var doc = await JsonDocument.ParseAsync(fs); - - var root = doc.RootElement; - if (!root.TryGetProperty("trains", out var trains)) return; - - foreach (var train in trains.EnumerateArray()) - { - if (!train.TryGetProperty("timetable", out var timetable)) continue; - var stops = timetable.EnumerateArray().ToArray(); - if (stops.Length == 0) continue; - - string origin = stops[0].GetProperty("zhanming").GetString(); - string terminal = stops[^1].GetProperty("zhanming").GetString(); - - TimeSpan? arrivalTime = null; - TimeSpan? departureTime = null; - - foreach (var stop in stops) - { - string name = stop.GetProperty("zhanming").GetString(); - if (name != SelectedStation.Name) continue; - - string cfsj = stop.GetProperty("cfsj").GetString(); - string ddsj = stop.GetProperty("ddsj").GetString(); - - if (cfsj == ddsj) - { - if (name == origin) - { - departureTime = Utilities.RoundToMinute(cfsj); - arrivalTime = null; - } - else if (name == terminal) - { - departureTime = null; - arrivalTime = Utilities.RoundToMinute(ddsj); - } - } - else - { - departureTime = Utilities.RoundToMinute(cfsj); - arrivalTime = Utilities.RoundToMinute(ddsj); - } - break; - } - if (arrivalTime == null && departureTime == null) continue; - string number = train.GetProperty("checi").EnumerateArray().FirstOrDefault().GetString()?.Split('/')?.FirstOrDefault() ?? string.Empty; - - var trainStop = new TrainStop - { - Number = number, - Origin = origin, - Terminal = terminal, - ArrivalTime = arrivalTime, - DepartureTime = departureTime, - }; - - TrainStops.Add(RandomTrainStopProperties(trainStop)); - } - } - catch (Exception e) - { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); - } - } - - [RelayCommand] - public void ImportFromExcel() - { - if (TrainStops.Count != 0) - { - if (!_dialogService.GetConfirm("当前操作可能会清空时刻表。是否继续?")) - { - return; - } - } - var path = _dialogService.GetFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*"); - if (path == null) return; - try - { - ExcelPackage.License.SetNonCommercialPersonal("CRSim"); - using var package = new ExcelPackage(new FileInfo(path)); - var worksheet = package.Workbook.Worksheets[0]; - int rowCount = worksheet.Dimension.Rows; - TrainStops.Clear(); - for (int row = 2; row <= rowCount; row++) - { - if (worksheet.Cells[row, 1].Text.Trim() == "" || - 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() == "") - { - continue; - } - - TrainStops.Add(new TrainStop - { - Number = worksheet.Cells[row, 1].Text.Trim(), - 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() - }); - } - } - catch (Exception e) - { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); - } - } - [RelayCommand] - public void ExportToExcel() - { - var path = _dialogService.SaveFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*", "data"); - if (path == null) return; - ExcelPackage.License.SetNonCommercialPersonal("CRSim"); - using var package = new ExcelPackage(); - var worksheet = package.Workbook.Worksheets.Add("Sheet1"); - worksheet.Cells[1, 1].Value = "车次"; - 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 = "终到站"; - 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; - } - package.SaveAs(new FileInfo(path)); - } - [RelayCommand] - public void AddTrainStop() - { - var newTrainStop = _dialogService.GetInputTrainStop([.. TicketChecks.Select(x => $"{x.WaitingAreaName} - {x.Name}")], [.. Platforms.Select(x => x.Name)]); - if (newTrainStop != null) - { - if (TrainStops.Any(x => x.Number == newTrainStop.Number)) - { - _dialogService.ShowMessage("添加失败", $"车次 {newTrainStop.Number} 已存在。"); - return; - } - TrainStops.Add(newTrainStop); - } - } - [RelayCommand] - public void DeleteTrainStop(object selectedTrainStop) - { - if (selectedTrainStop is TrainStop s) - { - TrainStops.Remove(s); - } - } - [RelayCommand] - public void DeleteAllTrainStop() - { - if (!_dialogService.GetConfirm("当前操作会删除全部车次,是否继续?")) return; - TrainStops.Clear(); - } - [RelayCommand] - public void EditTrainStop(object _selectedTrainStop) - { - if (_selectedTrainStop is TrainStop selectedTrainStop) - { - var newTrainStop = _dialogService.GetInputTrainStop([.. TicketChecks.Select(x => $"{x.WaitingAreaName} - {x.Name}")], [.. Platforms.Select(x => x.Name)], selectedTrainStop); - if (newTrainStop != null) - { - TrainStops[TrainStops.IndexOf(selectedTrainStop)] = newTrainStop; - } - } - } - [RelayCommand] - public async Task ImportFromInternet() - { - try - { - if (!CheckCanImport()) return; - var stops = await _networkService.GetTrainNumbersAsync(SelectedStation.Name); - if (stops.Count != 0) - { - TrainStops.Clear(); - foreach (TrainStop t in stops) - { - TrainStops.Add(RandomTrainStopProperties(t)); - } - } - else - { - throw new Exception(); - } - } - catch - { - _dialogService.ShowMessage("获取失败", "服务器繁忙或车站不存在。"); - } - } - private bool CheckCanImport() - { - if (TicketChecks.Count == 0) - { - _dialogService.ShowMessage("导入失败", "请先添加检票口。"); - return false; - } - if (Platforms.Count == 0) - { - _dialogService.ShowMessage("导入失败", "请先添加站台。"); - return false; - } - if (TrainStops.Count != 0) - { - if (!_dialogService.GetConfirm("当前操作会清空时刻表。是否继续?")) - { - return false; - } - } - return true; - } - private TrainStop RandomTrainStopProperties(TrainStop t) - { - t.Landmark = new[] { "红色", "绿色", "褐色", "蓝色", "紫色", "黄色", "橙色", null }[new Random().Next(8)]; - t.Length = t.Number.StartsWith('G') || t.Number.StartsWith('D') || t.Number.StartsWith('C') ? Math.Abs(t.Number.GetHashCode()) % 3 == 0 ? 8 : 16 : 18; - t.Platform = Platforms[new Random().Next(Platforms.Count)].Name; - 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; - return t; - } - #endregion - - [RelayCommand] - public async Task SaveChanges() - { - if (!await Validate()) return; - _databaseService.UpdateStation(SelectedStation.Name, GenerateStation(SelectedStation.Name,WaitingAreaNames,TicketChecks,TrainStops,Platforms)); - await _databaseService.SaveData(); - } - - private Task Validate() - { - string warningMessage = ""; - var platformGroups = TrainStops - .Where(ts => !string.IsNullOrEmpty(ts.Platform) && ts.DepartureTime != null && ts.ArrivalTime != null) - .GroupBy(ts => ts.Platform); - foreach (var group in platformGroups) - { - var stops = group.OrderBy(ts => ts.ArrivalTime ?? TimeSpan.Zero).ToList(); - for (int i = 0; i < stops.Count - 1; i++) - { - var current = stops[i]; - var next = stops[i + 1]; - // 判断时间是否重叠 - if (current.DepartureTime != null && next.ArrivalTime != null && - current.DepartureTime > next.ArrivalTime) - { - warningMessage += $"\n站台 {group.Key} 车次 {current.Number} ({current.ArrivalTime:hh\\:mm}-{current.DepartureTime:hh\\:mm}) 与车次 {next.Number} ({next.ArrivalTime:hh\\:mm}-{next.DepartureTime:hh\\:mm}) 时间重叠;"; - } - } - } - - string errorMessage = ""; - foreach(var t in TicketChecks) - { - if (!WaitingAreaNames.Contains(t.WaitingAreaName)) - { - errorMessage += $"\n检票口 {t.Name} 所在的候车室 {t.WaitingAreaName} 不存在;"; - } - } - foreach(var s in TrainStops) - { - if(!Platforms.Any(x=> x.Name == s.Platform)) - { - errorMessage += $"\n车次 {s.Number} 所分配的站台 {s.Platform} 不存在;"; - } - if (!WaitingAreaNames.Contains(s.WaitingArea)) - { - errorMessage += $"\n车次 {s.Number} 所分配的候车区 {s.WaitingArea} 不存在;"; - } - else - { - foreach (var t in s.TicketChecks) - { - if (!TicketChecks.Any(x => x.Name == t && x.WaitingAreaName == s.WaitingArea)) - { - errorMessage += $"\n车次 {s.Number} 所分配的检票口 {t} 在候车区 {s.WaitingArea} 中不存在;"; - } - } - } - if (s.DepartureTime == null && s.ArrivalTime == null) - { - errorMessage += $"\n车次 {s.Number} 未配置到发时间;"; - } - if (s.Length==0) - { - errorMessage += $"\n车次 {s.Number} 长度为0;"; - } - } - if (errorMessage == "") - { - if(warningMessage!="") _dialogService.ShowMessage("警告", $"发现 {warningMessage.Split("\n").Length - 1} 个警告:{warningMessage}\n配置已保存,您可以选择忽略这些警告,亦或手动优化。"); - return Task.FromResult(true); - } - _dialogService.ShowMessage("保存失败", $"发现 {errorMessage.Split("\n").Length - 1} 个错误和 {warningMessage.Split("\n").Length - 1} 个警告:{errorMessage}{warningMessage}\n请修复所有错误后再次尝试保存。"); - return Task.FromResult(false); - } - -} \ No newline at end of file diff --git a/CRSim/ViewModels/Pages/DataManagementPageViewModel.cs b/CRSim/ViewModels/Pages/DataManagementPageViewModel.cs deleted file mode 100644 index 27529a0..0000000 --- a/CRSim/ViewModels/Pages/DataManagementPageViewModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace CRSim.ViewModels -{ - public partial class DataManagementPageViewModel(INavigationService navigationService) : ObservableObject - { - [ObservableProperty] - private string _pageTitle = "数据管理"; - - [ObservableProperty] - private string _pageDescription = "车站、车次、交路等信息管理"; - - [ObservableProperty] - private ICollection _navigationCards = ControlsInfoDataSource.Instance.GetControlsInfo("Data Management"); - - private readonly INavigationService _navigationService = navigationService; - - [RelayCommand] - public void Navigate(object pageType){ - if (pageType is Type page) - { - _navigationService.NavigateTo(page); - } - } - - - } -} diff --git a/CRSim/ViewModels/Pages/SettingsPageViewModel.cs b/CRSim/ViewModels/Pages/SettingsPageViewModel.cs deleted file mode 100644 index aa2c54b..0000000 --- a/CRSim/ViewModels/Pages/SettingsPageViewModel.cs +++ /dev/null @@ -1,111 +0,0 @@ -namespace CRSim.ViewModels -{ - public partial class SettingsPageViewModel : ObservableObject - { - [ObservableProperty] - private string _pageTitle = "设置"; - [ObservableProperty] - private string _appVersion = ""; - private Settings _settings; - private readonly ISettingsService _settingsService; - private readonly IDatabaseService _databaseService; - private readonly IDialogService _dialogService; - #region 偏好设置 - [ObservableProperty] - public string _timeOffset; - [ObservableProperty] - public string _departureCheckInAdvanceDuration; - [ObservableProperty] - public string _passingCheckInAdvanceDuration; - [ObservableProperty] - public string _stopDisplayUntilDepartureDuration; - [ObservableProperty] - public string _stopDisplayFromArrivalDuration; - [ObservableProperty] - public string _stopCheckInAdvanceDuration; - [ObservableProperty] - public string _maxPages; - [ObservableProperty] - public string _switchPageSeconds; - [ObservableProperty] - public string _userKey; - [ObservableProperty] - public bool _loadTodayOnly; - [ObservableProperty] - public bool _reopenUnclosedScreensOnLoad; - #endregion - public SettingsPageViewModel(ISettingsService settingsService, IDatabaseService databaseService, IDialogService dialogService) - { - LoadSettings(settingsService); - _settingsService = settingsService; - _databaseService = databaseService; - _dialogService = dialogService; - var version = Assembly.GetExecutingAssembly().GetName().Version; - AppVersion = $"CRSim v{version}"; - _dialogService = dialogService; - } - - private void LoadSettings(ISettingsService settingsService) - { - _settings = settingsService.GetSettings(); - TimeOffset = _settings.TimeOffset.TotalMinutes.ToString(); - DepartureCheckInAdvanceDuration = _settings.DepartureCheckInAdvanceDuration.TotalMinutes.ToString(); - PassingCheckInAdvanceDuration = _settings.PassingCheckInAdvanceDuration.TotalMinutes.ToString(); - StopDisplayUntilDepartureDuration = _settings.StopDisplayUntilDepartureDuration.TotalMinutes.ToString(); - StopDisplayFromArrivalDuration = _settings.StopDisplayFromArrivalDuration.TotalMinutes.ToString(); - StopCheckInAdvanceDuration = _settings.StopCheckInAdvanceDuration.TotalMinutes.ToString(); - MaxPages = _settings.MaxPages.ToString(); - SwitchPageSeconds = _settings.SwitchPageSeconds.ToString(); - UserKey = _settings.UserKey; - LoadTodayOnly = _settings.LoadTodayOnly; - ReopenUnclosedScreensOnLoad = _settings.ReopenUnclosedScreensOnLoad; - } - - [RelayCommand] - public void Unload() - { - UpdateSettings(TimeOffset, true, value => _settings.TimeOffset = TimeSpan.FromMinutes(value)); - UpdateSettings(DepartureCheckInAdvanceDuration, false, value => _settings.DepartureCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); - UpdateSettings(PassingCheckInAdvanceDuration, false, value => _settings.PassingCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); - UpdateSettings(StopDisplayUntilDepartureDuration, false, value => _settings.StopDisplayUntilDepartureDuration = TimeSpan.FromMinutes(value)); - UpdateSettings(StopDisplayFromArrivalDuration, false, value => _settings.StopDisplayFromArrivalDuration = TimeSpan.FromMinutes(value)); - UpdateSettings(StopCheckInAdvanceDuration, false, value => _settings.StopCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); - UpdateSettings(MaxPages, false, value => _settings.MaxPages = value); - UpdateSettings(SwitchPageSeconds, false, value => _settings.SwitchPageSeconds = value); - _settings.UserKey = UserKey; - _settings.LoadTodayOnly = LoadTodayOnly; - _settings.ReopenUnclosedScreensOnLoad = ReopenUnclosedScreensOnLoad; - _settingsService.SaveSettings(); - } - private void UpdateSettings(string input,bool allowNegative, Action updateAction) - { - if (int.TryParse(input, out int value) && (allowNegative || value >= 0)) - { - updateAction(value); - } - } - - [RelayCommand] - public async Task ClearData() - { - if (!_dialogService.GetConfirm("该操作将会清空所有数据,请否继续?")) return; - _databaseService.ClearData(); - await _databaseService.SaveData(); - } - [RelayCommand] - public async Task ExportData() - { - string? path = _dialogService.SaveFile("JSON 文件 (*.json)|*.json|所有文件 (*.*)|*.*","data"); - if (string.IsNullOrWhiteSpace(path)) return; - await _databaseService.ExportData(path); - } - [RelayCommand] - public async Task ImportData() - { - string? path = _dialogService.GetFile("JSON 文件 (*.json)|*.json|所有文件 (*.*)|*.*"); - if (string.IsNullOrWhiteSpace(path)) return; - _databaseService.ImportData(path); - await _databaseService.SaveData(); - } - } -} diff --git a/CRSim/ViewModels/Pages/Simulation/ScreenSimulationPageViewModel.cs b/CRSim/ViewModels/Pages/Simulation/ScreenSimulationPageViewModel.cs deleted file mode 100644 index b675fb3..0000000 --- a/CRSim/ViewModels/Pages/Simulation/ScreenSimulationPageViewModel.cs +++ /dev/null @@ -1,275 +0,0 @@ -using CRSim.ScreenSimulator.Models; -using System.Text.Json.Serialization; - -namespace CRSim.ViewModels; - -public partial class ScreenSimulationPageViewModel : ObservableObject -{ - [ObservableProperty] - private string _pageTitle = "引导屏模拟"; - - [ObservableProperty] - private string _pageDescription = ""; - - [ObservableProperty] - private ICollection _styleCards = StylesInfoDataSource.Instance.StylesInfo; - - private Type selectedStyle; - - [ObservableProperty] - private string _selectedStyleName = ""; - - [ObservableProperty] - private bool _stationNeeded; - - [ObservableProperty] - private bool _ticketCheckNeeded; - - [ObservableProperty] - private bool _platformNeeded; - - [ObservableProperty] - private bool _textNeeded; - - [ObservableProperty] - private bool _videoNeeded; - - [ObservableProperty] - private bool _locationNeeded; - - [ObservableProperty] - private string _text = ""; - - [ObservableProperty] - private string _video = ""; - - public string SelectedStationName = ""; - - public string SelectedTicketCheck = ""; - - public string SelectedPlatformName = ""; - - public int SelectedLoaction = 0; - - public ObservableCollection StationNames { get; private set; } = []; - public ObservableCollection TicketChecks { get; private set; } = []; - public ObservableCollection Platforms { get; private set; } = []; - public ObservableCollection Locations { get; private set; } = []; - - [ObservableProperty] - private bool _isStartSimulationAvailable = false; - - private readonly IDatabaseService _databaseService; - - private readonly IServiceProvider _serviceProvider; - - private readonly IDialogService _dialogService; - public ScreenSimulationPageViewModel(IDatabaseService databaseService, IServiceProvider serviceProvider, IDialogService dialogService) - { - _databaseService = databaseService; - _serviceProvider = serviceProvider; - _dialogService = dialogService; - foreach (Station station in _databaseService.GetAllStations()) - { - StationNames.Add(station.Name); - } - } - - private void CheckCanStart() - { - IsStartSimulationAvailable = selectedStyle != null && - (!StationNeeded || !string.IsNullOrWhiteSpace(SelectedStationName)) && - (!TicketCheckNeeded || !string.IsNullOrWhiteSpace(SelectedTicketCheck)) && - (!LocationNeeded || SelectedLoaction != 0) && - (!PlatformNeeded || !string.IsNullOrWhiteSpace(SelectedPlatformName)); - } - - [RelayCommand] - public void SelectStyle(object s) - { - if (s is Type style) - { - selectedStyle = style; - SelectedStyleName = StylesInfoDataSource.Instance.GetStyleName(style); - var neededParameters = StylesInfoDataSource.Instance.GetParametersInfo(style); - StationNeeded = neededParameters.Contains("Station"); - TicketCheckNeeded = neededParameters.Contains("TicketCheck"); - PlatformNeeded = neededParameters.Contains("Platform"); - VideoNeeded = neededParameters.Contains("Video"); - TextNeeded = neededParameters.Contains("Text"); - LocationNeeded = neededParameters.Contains("Location"); - } - CheckCanStart(); - } - [RelayCommand] - public void SelectStation(object s) - { - if(s is string v) - { - var station = _databaseService.GetStationByName(v); - SelectedStationName = station.Name; - SelectedPlatformName = ""; - SelectedTicketCheck = ""; - SelectedLoaction = 0; - TicketChecks.Clear(); - foreach (var waitingArea in station.WaitingAreas) - { - foreach (string ticketCheck in waitingArea.TicketChecks) - { - TicketChecks.Add($"{waitingArea.Name} - {ticketCheck}");//重复检票口名称的临时解决方案 - } - } - Platforms.Clear(); - foreach (var platform in station.Platforms) - { - Platforms.Add(platform); - } - Locations.Clear(); - } - CheckCanStart(); - } - [RelayCommand] - public void SelectTicketCheck(object s) - { - if (s is string ticketCheck) - { - SelectedTicketCheck = ticketCheck; - } - CheckCanStart(); - } - [RelayCommand] - public void SelectLocation(object s) - { - if (s is int location) - { - SelectedLoaction = location; - } - CheckCanStart(); - } - [RelayCommand] - public void SelectPlatform(object s) - { - if (s is Platform platform) - { - SelectedPlatformName = platform.Name; - SelectedLoaction = 0; - Locations.Clear(); - for (int i = 1; i <= platform.Length; i++) - { - Locations.Add(i); - } - } - CheckCanStart(); - } - [RelayCommand] - public void SelectVideo() - { - string? Path = _dialogService.GetFile("视频文件 (*.mp4)|*.mp4|所有文件 (*.*)|*.*"); - if (Path == null) return; - Video = Path; - } - [RelayCommand] - public async Task StartSimulation() - { - var Window = _serviceProvider.GetRequiredService(selectedStyle) as dynamic; - var SessionID = Guid.NewGuid(); - Window.SessionID = SessionID; - if (TextNeeded && Text != string.Empty) - { - Window.ViewModel.Text = Text; - } - if (LocationNeeded && SelectedLoaction != 0) - { - Window.ViewModel.Location = SelectedLoaction; - } - if (VideoNeeded && Video != string.Empty) - { - Window.ViewModel.Video = new Uri(Video, UriKind.Absolute); - } - - Window.ViewModel.LoadData(_databaseService.GetStationByName(SelectedStationName), - TicketCheckNeeded ? SelectedTicketCheck : string.Empty, - PlatformNeeded ? SelectedPlatformName : string.Empty); - Window.Show(); - - await SaveScreenAsync(SessionID); - } - private static readonly JsonSerializerOptions CachedJsonSerializerOptions = new() - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - - private async Task SaveScreenAsync(Guid guid) - { - List screenInfos = []; - if (File.Exists("Screen.json")) - { - var fileContent = await File.ReadAllTextAsync("Screen.json"); - screenInfos = JsonSerializer.Deserialize>(fileContent) ?? []; - } - ScreenInfo screenInfo = new() - { - SessionID = guid, - SelectedStyle = selectedStyle.FullName, - Text = TextNeeded && !string.IsNullOrEmpty(Text) ? Text : null, - Video = VideoNeeded && !string.IsNullOrEmpty(Video) ? Video : null, - SelectedStationName = SelectedStationName, - SelectedLoaction = LocationNeeded && SelectedLoaction != 0 ? SelectedLoaction : null, - SelectedTicketCheck = TicketCheckNeeded ? SelectedTicketCheck : null, - SelectedPlatformName = PlatformNeeded ? SelectedPlatformName : null - }; - screenInfos.Add(screenInfo); - var serializedContent = JsonSerializer.Serialize(screenInfos, CachedJsonSerializerOptions); - await File.WriteAllTextAsync("Screen.json", serializedContent); - } - #region StyleSearch - private string _keyWord = string.Empty; - private string _station = string.Empty; - private string _type = string.Empty; - private string _author = string.Empty; - public List StyleStations { get; private set; } = [.. new[] { "全部" }.Concat(StylesInfoDataSource.Instance.StylesInfo.Select(x => x.Region).Distinct())]; - public List StyleTypes { get; private set; } = [.. new[] { "全部" }.Concat(StylesInfoDataSource.Instance.StylesInfo.Select(x => x.Type).Distinct())]; - public List StyleAuthors { get; private set; } = [.. new[] { "全部" }.Concat(StylesInfoDataSource.Instance.StylesInfo.Select(x => x.Author).Distinct())]; - - [RelayCommand] - public void TextSearch(object s) - { - if(s is string str) - { - _keyWord = str; - Search(); - } - } - [RelayCommand] - public void StationSearch(object s) - { - if (s is string str) - { - _station = str; - Search(); - } - } - [RelayCommand] - public void TypeSearch(object s) - { - if (s is string str) - { - _type = str; - Search(); - } - } - [RelayCommand] - public void AuthorSearch(object s) - { - if (s is string str) - { - _author = str; - Search(); - } - } - private void Search() - { - StyleCards = [.. StylesInfoDataSource.Instance.StylesInfo.Where(x =>(x.Region==_station||_station==string.Empty||_station=="全部") && (x.Type==_type||_type==string.Empty||_type=="全部") && (x.Author==_author||_author==string.Empty||_author=="全部") && x.Title.Contains(_keyWord))]; - } - #endregion -} diff --git a/CRSim/ViewModels/Pages/Simulation/WebsiteSimulationPageViewModel.cs b/CRSim/ViewModels/Pages/Simulation/WebsiteSimulationPageViewModel.cs deleted file mode 100644 index 87b3675..0000000 --- a/CRSim/ViewModels/Pages/Simulation/WebsiteSimulationPageViewModel.cs +++ /dev/null @@ -1,34 +0,0 @@ -using CRSim.WebsiteSimulator; - -namespace CRSim.ViewModels; - -public partial class WebsiteSimulationPageViewModel(Simulator simulator, IDialogService dialogService) : ObservableObject -{ - [ObservableProperty] - private string _pageTitle = "12306模拟"; - - [ObservableProperty] - private string _pageDescription = ""; - - private readonly Simulator _simulator = simulator; - - private readonly IDialogService _dialogService = dialogService; - - [RelayCommand] - public async Task StartSimulation() - { - try - { - await _simulator.Start(); - } - catch(Exception e) - { - _dialogService.ShowMessage("认证失败", e.Message); - } - } - [RelayCommand] - public void StopSimulation() - { - _simulator.Stop(); - } -} diff --git a/CRSim/ViewModels/Pages/StartSimulationPageViewModel.cs b/CRSim/ViewModels/Pages/StartSimulationPageViewModel.cs deleted file mode 100644 index ee8aadf..0000000 --- a/CRSim/ViewModels/Pages/StartSimulationPageViewModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace CRSim.ViewModels -{ - public partial class StartSimulationPageViewModel(INavigationService navigationService) : ObservableObject - { - [ObservableProperty] - private string _pageTitle = "开始模拟"; - - [ObservableProperty] - private string _pageDescription = "请选择你想模拟的项目"; - - [ObservableProperty] - private ICollection _navigationCards = ControlsInfoDataSource.Instance.GetControlsInfo("Start Simulation"); - - private readonly INavigationService _navigationService = navigationService; - - [RelayCommand] - public void Navigate(object pageType){ - if (pageType is Type page) - { - _navigationService.NavigateTo(page); - } - } - - - } -} diff --git a/CRSim/ViewModels/PluginManagementPageViewModel.cs b/CRSim/ViewModels/PluginManagementPageViewModel.cs new file mode 100644 index 0000000..fd3af09 --- /dev/null +++ b/CRSim/ViewModels/PluginManagementPageViewModel.cs @@ -0,0 +1,147 @@ +using CRSim.Core.Models.Plugin; +using System.Diagnostics; + +namespace CRSim.ViewModels; + +public partial class PluginManagementPageViewModel : ObservableObject +{ + [ObservableProperty] + public partial string PageTitle { get; set; } = "插件管理"; + + [ObservableProperty] + public partial string SearchText { get; set; } = ""; + + [ObservableProperty] + public partial PluginInfo? SelectedPlugin { get; set; } + + public List Plugins = IPluginService.OnlinePlugins; + + public List FilteredPlugins => [.. Plugins.Where(x => x.Manifest.Name.Contains(SearchText) && (SelectedAuthor == "全体作者" || x.Manifest.Author == SelectedAuthor))]; + + public List Authors => ["全体作者",.. Plugins.Select(x => x.Manifest.Author).Distinct()]; + + [ObservableProperty] + public partial string SelectedAuthor { get; set; } = "全体作者"; + + private readonly IPluginService _pluginService; + private readonly IDialogService _dialogService; + + public PluginManagementPageViewModel(IPluginService pluginService,IDialogService dialogService) + { + _pluginService = pluginService; + _dialogService = dialogService; + _ = InitAsync(); + } + private async Task InitAsync() + { + await _pluginService.LoadOnlinePluginsAsync(); + OnPropertyChanged(nameof(FilteredPlugins)); + } + + [RelayCommand] + public void PluginSelected(object args) + { + if(args is SelectionChangedEventArgs selectionChangedEventArgs && selectionChangedEventArgs.AddedItems.Count == 1) + { + SelectedPlugin = (PluginInfo)selectionChangedEventArgs.AddedItems.FirstOrDefault(); + } + } + + [RelayCommand] + public async Task InstallPluginOnline() + { + await _pluginService.InstallPluginOnlineAsync(SelectedPlugin); + OnPropertyChanged(nameof(SelectedPlugin)); + } + [RelayCommand] + public async Task InstallPluginLocal() + { + if (_dialogService.GetFile([".crsp"]) is string filePath) + { + try + { + await _pluginService.InstallPluginLocalAsync(filePath); + } + catch (Exception ex) + { + await _dialogService.ShowTextAsync("安装失败", $"{ex}"); + } + } + } + [RelayCommand] + public void UninstallPlugin() + { + SelectedPlugin.IsUninstalling = true; + OnPropertyChanged(nameof(SelectedPlugin)); + } + + [RelayCommand] + public void CancelUninstallPlugin() + { + SelectedPlugin.IsUninstalling = false; + OnPropertyChanged(nameof(SelectedPlugin)); + } + [RelayCommand] + public async Task PackPlugin() + { + if (SelectedPlugin == null) return; + string? filePath = _dialogService.SaveFile(".crsp", $"{SelectedPlugin.Manifest.Id}"); + if (filePath == null) return; + try + { + await _pluginService.PackPluginAsync(SelectedPlugin, filePath); + } + catch (Exception ex) + { + await _dialogService.ShowTextAsync("打包失败", $"{ex}"); + } + } + [RelayCommand] + public static void Restart() + { + string exePath = Environment.ProcessPath; + Process.Start(new ProcessStartInfo(exePath) + { + UseShellExecute = true + }); + Application.Current.Exit(); + } + + [RelayCommand] + public void SourceSelected(object args) + { + if(args is SelectorBarItem selectorBarItem) + { + SelectedPlugin = null; + Plugins = (selectorBarItem?.Text == "本地") ? IPluginService.OnlinePlugins : IPluginService.LoadedPlugins; + OnPropertyChanged(nameof(Authors)); + SelectedAuthor = "全体作者"; + OnPropertyChanged(nameof(FilteredPlugins)); + } + } + + [RelayCommand] + public static void OpenPluginFolder() + { + Process.Start(new ProcessStartInfo + { + FileName = AppPaths.PluginsRootPath, + UseShellExecute = true + }); + } + + [RelayCommand] + public async Task Refresh() + { + await _pluginService.LoadOnlinePluginsAsync(); + OnPropertyChanged(nameof(Authors)); + SelectedAuthor = "全体作者"; + OnPropertyChanged(nameof(FilteredPlugins)); + } + + [RelayCommand] + public void Search(object args) + { + OnPropertyChanged(nameof(FilteredPlugins)); + } +} \ No newline at end of file diff --git a/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs new file mode 100644 index 0000000..964172a --- /dev/null +++ b/CRSim/ViewModels/ScreenSimulatorPageViewModel.cs @@ -0,0 +1,172 @@ +using CRSim.Core.Models.Plugin; +using CRSim.ScreenSimulator.Models; + +namespace CRSim.ViewModels; + +public partial class ScreenSimulatorPageViewModel(IEnumerable plugins, IDatabaseService databaseService, IDialogService dialogService, StyleManager styleManager) : ObservableObject +{ + public string PageTitle = "引导屏模拟"; + public List StyleInfos => styleManager.StyleInfos; + + [ObservableProperty] + public partial PluginInfo SelectedStyle { get; set; } + + [ObservableProperty] + public partial bool StationNeeded { get; set; } = true; + + [ObservableProperty] + public partial bool TicketCheckNeeded { get; set; } + + [ObservableProperty] + public partial bool PlatformNeeded { get; set; } + + [ObservableProperty] + public partial bool TextNeeded { get; set; } + + [ObservableProperty] + public partial bool VideoNeeded { get; set; } + + [ObservableProperty] + public partial bool LocationNeeded { get; set; } + public List StationNames => [.. databaseService.GetAllStations().Select(x => x.Name)]; + public ObservableCollection TicketChecks { get; private set; } = []; + public ObservableCollection Platforms { get; private set; } = []; + public ObservableCollection Locations { get; private set; } = []; + + [ObservableProperty] + public partial string Text { get; set; } + + [ObservableProperty] + public partial string Video { get; set; } + + public string SelectedStationName = ""; + + public TicketCheck? SelectedTicketCheck; + + public string SelectedPlatformName = ""; + + public int SelectedLoaction = 0; + + [ObservableProperty] + public partial bool IsStartSimulationAvailable { get; private set; } = false; + + private PluginBase SelectedStylePlugin => plugins.Where(x => x.Info == SelectedStyle).FirstOrDefault(); + + public DateTimeOffset SelectedDate = DateTime.Today; + + public TimeSpan SelectedTime = DateTime.Now - DateTime.Today; + + [RelayCommand] + public void StyleSelected(object selectedStyle) + { + if(selectedStyle is PluginInfo style) + { + SelectedStyle = style; + var paramaters = style.StyleInfo?.Parameters; + StationNeeded = paramaters.Contains("station"); + TicketCheckNeeded = paramaters.Contains("ticketCheck"); + PlatformNeeded = paramaters.Contains("platform"); + TextNeeded = paramaters.Contains("text"); + VideoNeeded = paramaters.Contains("video"); + LocationNeeded = paramaters.Contains("location"); + } + } + [RelayCommand] + public void StationSelected(object s) + { + if (s is string v) + { + var station = databaseService.GetStationByName(v); + SelectedStationName = station.Name; + SelectedPlatformName = ""; + SelectedTicketCheck = null; + SelectedLoaction = 0; + TicketChecks.Clear(); + foreach (var waitingArea in station.WaitingAreas) + { + foreach (var ticketCheck in waitingArea.TicketChecks) + { + TicketChecks.Add(new TicketCheck() + { + Id = ticketCheck.Id, + Name = waitingArea.Name + " - " + ticketCheck.Name, + }); + } + } + Platforms.Clear(); + foreach (var platform in station.Platforms) + { + Platforms.Add(platform); + } + Locations.Clear(); + } + CheckCanStart(); + } + [RelayCommand] + public void SelectTicketCheck(object s) + { + if (s is TicketCheck ticketCheck) + { + SelectedTicketCheck = ticketCheck; + } + CheckCanStart(); + } + [RelayCommand] + public void SelectLocation(object s) + { + if (s is int location) + { + SelectedLoaction = location; + } + CheckCanStart(); + } + [RelayCommand] + public void SelectPlatform(object s) + { + if (s is Platform platform) + { + SelectedPlatformName = platform.Name; + SelectedLoaction = 0; + Locations.Clear(); + for (int i = 1; i <= platform.Length; i++) + { + Locations.Add(i); + } + } + CheckCanStart(); + } + [RelayCommand] + public async Task SelectVideo() + { + string? Path = dialogService.GetFile([".wmv",".asf",".mp4",".m4v",".mov",".mpg",".mpeg"]); + if (Path == null) return; + Video = Path; + } + [RelayCommand] + public void StartSimulation() + { + // 构建 Session 对象 + var session = new Session + { + ID = Guid.NewGuid(), + StyleName = SelectedStyle.Manifest.Name, + SimulateTime = SelectedDate.Add(SelectedTime).DateTime, + 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 : null, + PlatformName = PlatformNeeded ? SelectedPlatformName : string.Empty, + Loaction = (LocationNeeded && SelectedLoaction != 0) ? SelectedLoaction : 0 + }; + styleManager.ShowWindow(SelectedStylePlugin.View, session); + } + + private void CheckCanStart() + { + IsStartSimulationAvailable = SelectedStyle != null && + (!StationNeeded || !string.IsNullOrWhiteSpace(SelectedStationName)) && + (!TicketCheckNeeded || SelectedTicketCheck is not null) && + (!LocationNeeded || SelectedLoaction != 0) && + (!PlatformNeeded || !string.IsNullOrWhiteSpace(SelectedPlatformName)); + } +} \ No newline at end of file diff --git a/CRSim/ViewModels/SettingsPageViewModel.cs b/CRSim/ViewModels/SettingsPageViewModel.cs new file mode 100644 index 0000000..1700494 --- /dev/null +++ b/CRSim/ViewModels/SettingsPageViewModel.cs @@ -0,0 +1,222 @@ +using Downloader; +using System.Diagnostics; +using System.Diagnostics.Metrics; +using System.IO.Packaging; +using System.Reflection; + +namespace CRSim.ViewModels +{ + public partial class SettingsPageViewModel : ObservableObject + { + [ObservableProperty] + public partial string PageTitle { get; set; } = "设置"; + + [ObservableProperty] + public partial string AppVersion { get; set; } = ""; + public ObservableCollection Apis { get; } = + [ + new InfoItem { Title = "官方源", Detail = "http://47.122.74.193:25565" }, + new InfoItem { Title = "镜像站源", Detail = "https://crsim.com.cn/api" }, + ]; + private Settings _settings; + private readonly ISettingsService _settingsService; + private readonly IDatabaseService _databaseService; + private readonly INetworkService _networkService; + private readonly IDialogService _dialogService; + + [ObservableProperty] + public partial int UpdateProgress { get; set; } + + #region 偏好设置 + [ObservableProperty] + public partial string DepartureCheckInAdvanceDuration { get; set; } + + [ObservableProperty] + public partial string PassingCheckInAdvanceDuration { get; set; } + + [ObservableProperty] + public partial string StopDisplayUntilDepartureDuration { get; set; } + + [ObservableProperty] + public partial string StopDisplayFromArrivalDuration { get; set; } + + [ObservableProperty] + public partial string StopCheckInAdvanceDuration { get; set; } + + [ObservableProperty] + public partial string MaxPages { get; set; } + + [ObservableProperty] + public partial string SwitchPageSeconds { get; set; } + + [ObservableProperty] + public partial InfoItem ApiUri { get; set; } + + [ObservableProperty] + public partial string UserKey { get; set; } + + [ObservableProperty] + public partial bool LoadTodayOnly { get; set; } + + [ObservableProperty] + public partial bool ReopenUnclosedScreensOnLoad { get; set; } + #endregion + public SettingsPageViewModel(ISettingsService settingsService, IDatabaseService databaseService, IDialogService dialogService, INetworkService networkService) + { + _settingsService = settingsService; + _databaseService = databaseService; + _dialogService = dialogService; + _networkService = networkService; + var version = Assembly.GetExecutingAssembly().GetName().Version; + AppVersion = $"Version {version}"; + LoadSettings(); + + } + + private void LoadSettings() + { + _settings = _settingsService.GetSettings(); + DepartureCheckInAdvanceDuration = _settings.DepartureCheckInAdvanceDuration.TotalMinutes.ToString(); + PassingCheckInAdvanceDuration = _settings.PassingCheckInAdvanceDuration.TotalMinutes.ToString(); + StopDisplayUntilDepartureDuration = _settings.StopDisplayUntilDepartureDuration.TotalMinutes.ToString(); + StopDisplayFromArrivalDuration = _settings.StopDisplayFromArrivalDuration.TotalMinutes.ToString(); + StopCheckInAdvanceDuration = _settings.StopCheckInAdvanceDuration.TotalMinutes.ToString(); + ApiUri = Apis.Where(x => x.Detail == _settings.ApiUri).FirstOrDefault(); + MaxPages = _settings.MaxPages.ToString(); + SwitchPageSeconds = _settings.SwitchPageSeconds.ToString(); + UserKey = _settings.UserKey; + LoadTodayOnly = _settings.LoadTodayOnly; + ReopenUnclosedScreensOnLoad = _settings.ReopenUnclosedScreensOnLoad; + } + + [RelayCommand] + public void Unload() + { + UpdateSettings(DepartureCheckInAdvanceDuration, false, value => _settings.DepartureCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); + UpdateSettings(PassingCheckInAdvanceDuration, false, value => _settings.PassingCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); + UpdateSettings(StopDisplayUntilDepartureDuration, false, value => _settings.StopDisplayUntilDepartureDuration = TimeSpan.FromMinutes(value)); + UpdateSettings(StopDisplayFromArrivalDuration, false, value => _settings.StopDisplayFromArrivalDuration = TimeSpan.FromMinutes(value)); + UpdateSettings(StopCheckInAdvanceDuration, false, value => _settings.StopCheckInAdvanceDuration = TimeSpan.FromMinutes(value)); + UpdateSettings(MaxPages, false, value => _settings.MaxPages = value); + UpdateSettings(SwitchPageSeconds, false, value => _settings.SwitchPageSeconds = value); + _settings.UserKey = UserKey; + _settings.LoadTodayOnly = LoadTodayOnly; + _settings.ReopenUnclosedScreensOnLoad = ReopenUnclosedScreensOnLoad; + _settings.ApiUri = ApiUri.Detail; + _settingsService.SaveSettings(); + } + private static void UpdateSettings(string input, bool allowNegative, Action updateAction) + { + if (int.TryParse(input, out int value) && (allowNegative || value >= 0)) + { + updateAction(value); + } + } + + [RelayCommand] + public async Task ClearData() + { + if (!await _dialogService.GetConfirmAsync("该操作将会清空所有数据,请否继续?")) return; + _databaseService.ClearData(); + await _databaseService.SaveData(); + } + [RelayCommand] + public async Task CheckUpdate() + { + var update = ApiUri.Title == "官方源" ? + await _networkService.GetUpdateAsync("https://api.github.com/repos/denglihong2007/CRSim/releases/latest") : + await _networkService.GetUpdateAsync("https://crsim.com.cn/api/version"); + if (update is null) + { + await _dialogService.ShowMessageAsync("错误", "检查更新失败。"); + } + else if (update.Name == AppVersion.Split(' ')[1]) + { + await _dialogService.ShowMessageAsync("信息", "当前已经是最新版本。"); + } + else + { + await _dialogService.ShowTextAsync("发现新版本 " + update.Name, update.Body + "\n系统即将下载安装新版本。"); + DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + var downloader = new DownloadService(); + UpdateProgress = 0; + try + { + int lastProgress = 0; + var lastUpdate = DateTime.MinValue; + downloader.DownloadProgressChanged += (s, e) => + { + var now = DateTime.Now; + if ((e.ProgressPercentage - lastProgress >= 1) || (now - lastUpdate).TotalMilliseconds >= 100) + { + lastProgress = (int)e.ProgressPercentage; + lastUpdate = now; + + dispatcherQueue.TryEnqueue(() => + { + UpdateProgress = (int)e.ProgressPercentage; + }); + } + }; + var path = Path.Combine(AppPaths.TempPath, "update.zip"); + await downloader.DownloadFileTaskAsync(update.Assets[0].BrowserDownloadUrl, path); + UpdateProgress = 0; + + if (!FileHashHelper.VerifySHA256(path, update.Assets[0].Digest.Split(':')[1])) + { + await _dialogService.ShowMessageAsync("错误", "下载的文件校验失败,请重试。"); + return; + } + + var programDirectory = AppDomain.CurrentDomain.BaseDirectory; + var appExePath = Process.GetCurrentProcess().MainModule.FileName; + var appName = Path.GetFileName(appExePath); + string batchScript = $@" + @echo off + taskkill /F /IM ""{appName}"" >nul 2>&1 + timeout /t 2 >nul + cd /d ""{programDirectory}"" + powershell -Command ""Expand-Archive -Path '{path}' -DestinationPath '{programDirectory}' -Force"" + del /f /q ""{path}"" + start """" ""{appExePath}"" + exit /b 0 + "; + string batchFilePath = Path.Combine(Path.GetTempPath(), "CRSimUpdate.bat"); + File.WriteAllText(batchFilePath, batchScript); + var processInfo = new ProcessStartInfo + { + FileName = "cmd.exe", + Arguments = $"/c \"{batchFilePath}\"", + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = false, + CreateNoWindow = true + }; + + Process.Start(processInfo); + Application.Current.Exit(); + } + catch (Exception e) + { + await _dialogService.ShowTextAsync("错误", "更新失败。\n" + e); + UpdateProgress = 0; + return; + } + } + } + [RelayCommand] + public async Task ExportData() + { + string? path = _dialogService.SaveFile(".json", "data"); + if (string.IsNullOrWhiteSpace(path)) return; + await _databaseService.ExportData(path); + } + [RelayCommand] + public async Task ImportData() + { + string? path = _dialogService.GetFile([".json"]); + if (string.IsNullOrWhiteSpace(path)) return; + _databaseService.ImportData(path); + await _databaseService.SaveData(); + } + } +} \ No newline at end of file diff --git a/CRSim/ViewModels/StationManagementPageViewModel.cs b/CRSim/ViewModels/StationManagementPageViewModel.cs new file mode 100644 index 0000000..cc20eaf --- /dev/null +++ b/CRSim/ViewModels/StationManagementPageViewModel.cs @@ -0,0 +1,830 @@ +using CRSim.Converters; +using System.Text.RegularExpressions; + +namespace CRSim.ViewModels; + +public partial class StationManagementPageViewModel : ObservableObject +{ + public ObservableCollection InfoItems { get; set; } = []; + + [ObservableProperty] + public partial string PageTitle { get; set; } = "车站管理"; + + [ObservableProperty] + public partial bool IsSelected { get; set; } = false; + + [ObservableProperty] + public partial List StationNames { get; set; } = []; + + [ObservableProperty] + public partial Station SelectedStation { get; set; } = new(); + + public ObservableCollection WaitingAreas { get; private set; } = []; + + public ObservableCollection Platforms { get; private set; } = []; + + public ObservableCollection TrainStops { get; private set; } = []; + + private readonly IDatabaseService _databaseService = App.AppHost.Services.GetService(); + + private readonly IDialogService _dialogService = App.AppHost.Services.GetService(); + + private readonly INetworkService _networkService = App.AppHost.Services.GetService(); + public StationManagementPageViewModel() + { + RefreshStations(); + } + private void UpdateInfoItems() + { + InfoItems.Clear(); + InfoItems.Add(new InfoItem() + { + IconGlyph = "\uEC08", + Title = "车站名称", + Detail = SelectedStation.Name + }); + InfoItems.Add(new InfoItem() + { + IconGlyph = "\uE7C0", + Title = "列车停靠数", + Detail = TrainStops.Count.ToString() + }); + InfoItems.Add(new InfoItem() + { + IconGlyph = "\uECA5", + Title = "候车室数", + Detail = WaitingAreas.Count.ToString() + }); + InfoItems.Add(new InfoItem() + { + IconGlyph = "\uF161", + Title = "检票口数", + Detail = WaitingAreas.SelectMany(x => x.TicketChecks).Count().ToString() + }); + InfoItems.Add(new InfoItem() + { + IconGlyph = "\uE799", + Title = "站台数", + Detail = Platforms.Count.ToString() + }); + } + + #region Station + public void RefreshStations() + { + var stationsList = _databaseService.GetAllStations(); + StationNames = [.. stationsList.Select(s => s.Name)]; + } + [RelayCommand] + public void StationSelected(object args) + { + WaitingAreas.Clear(); + Platforms.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) + { + WaitingAreas.Add(waitingArea); + } + foreach (TrainStop trainStop in SelectedStation.TrainStops) + { + TrainStops.Add(trainStop); + } + foreach (var platform in SelectedStation.Platforms) + { + Platforms.Add(platform); + } + IsSelected = true; + UpdateInfoItems(); + } + else + { + IsSelected = false; + } + } + + [RelayCommand] + public async Task AddStation() + { + string? station = await _dialogService.GetInputAsync("请输入车站名称", string.Empty); + if (station != null) + { + if (StationNames.Contains(station)) + { + await _dialogService.ShowMessageAsync("添加失败", $"车站 {station} 已存在。"); + return; + } + _databaseService.AddStationByName(station); + await _databaseService.SaveData(); + RefreshStations(); + } + } + [RelayCommand] + public async Task DeleteStation(object selectedStation) + { + if (selectedStation is string s && !string.IsNullOrEmpty(s)) + { + _databaseService.DeleteStation(_databaseService.GetStationByName(s)); + await _databaseService.SaveData(); + RefreshStations(); + } + } + [RelayCommand] + public async Task DeleteAllStations() + { + if (!await _dialogService.GetConfirmAsync("当前操作会删除全部车站,是否继续?")) return; + foreach (string stationName in StationNames) + { + _databaseService.DeleteStation(_databaseService.GetStationByName(stationName)); + } + await _databaseService.SaveData(); + RefreshStations(); + } + public static Station GenerateStation(string stationName, ObservableCollection waitingAreas, ObservableCollection trainStops, ObservableCollection platforms) + { + var station = new Station + { + Name = stationName, + WaitingAreas = [.. waitingAreas], + TrainStops = [.. trainStops], + Platforms = [.. platforms] + }; + return station; + } + [RelayCommand] + public async Task ImportFrom7D() + { + var path = _dialogService.GetFile([".exe"]); + if (path == null) return; + try + { + await _databaseService.ImportStationFrom7D(path); + await _databaseService.SaveData(); + RefreshStations(); + } + catch (Exception e) + { + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); + } + } + #endregion + + #region WaitingArea + [RelayCommand] + public async Task AddWaitingArea() + { + string? waitingAreaName = await _dialogService.GetInputAsync("请输入候车室名称", string.Empty); + if (waitingAreaName != null) + { + if (WaitingAreas.Any(x => x.Name == waitingAreaName)) + { + await _dialogService.ShowMessageAsync("添加失败", $"名称 {waitingAreaName} 候车室已存在。"); + return; + } + WaitingAreas.Add(new WaitingArea { Name = waitingAreaName }); + } + } + [RelayCommand] + public void DeleteWaitingArea(object selectedWaitingArea) + { + if (selectedWaitingArea is WaitingArea n) + { + WaitingAreas.Remove(n); + } + } + [RelayCommand] + public async Task DeleteAllTicketChecks() + { + 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 + [RelayCommand] + public async Task AddPlatform() + { + var platforms = await _dialogService.GetInputPlatformAsync(); + if (platforms != null) + { + foreach (var platform in platforms) + { + if (Platforms.Any(x => x.Name == platform.Name)) + { + await _dialogService.ShowMessageAsync("添加失败", $"编号 {platform.Name} 站台已存在。"); + return; + } + Platforms.Add(platform); + } + } + } + [RelayCommand] + public void DeletePlatform(object selectedPlatform) + { + if (selectedPlatform is Platform p) + { + Platforms.Remove(p); + } + } + [RelayCommand] + public async Task DeleteAllPlatform() + { + if (!await _dialogService.GetConfirmAsync("当前操作会删除全部站台,是否继续?")) return; + Platforms.Clear(); + } + #endregion + + #region TrainStop + [RelayCommand] + public async Task ImportFromTrainNumbers() + { + if (!await CheckCanImport()) return; + var trainNumbers = _databaseService.GetAllTrainNumbers(); + if (trainNumbers.Count != 0) + { + TrainStops.Clear(); + } + foreach (TrainNumber trainNumber in trainNumbers) + { + var t = trainNumber.TimeTable.FirstOrDefault(x => x.Station == SelectedStation.Name); + if (t == null) continue; + t.Number = trainNumber.Number; + t = RandomTrainStopProperties(t); + t.Origin = trainNumber.TimeTable.First().Station; + t.Terminal = trainNumber.TimeTable.Last().Station; + TrainStops.Add(t); + } + } + [RelayCommand] + public async Task ImportFromLulutong() + { + if (!await CheckCanImport()) return; + var path = _dialogService.GetFile([".csv"]); + if (path == null) return; + try + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + var lines = new List(); + var reader = new StreamReader(path, Encoding.GetEncoding("GBK")); + while (!reader.EndOfStream) + { + var line = await reader.ReadLineAsync(); + lines.Add(line); + } + foreach (var line in lines.Skip(13)) + { + var data = line.Split(","); + var firstColumn = data[0].Trim(); + if (!string.IsNullOrEmpty(firstColumn) && char.IsLetterOrDigit(firstColumn[0])) + { + int spaceIndex = firstColumn.IndexOf(' '); + if (spaceIndex != -1) + { + firstColumn = firstColumn[..spaceIndex]; + } + if (TrainStops.Any(x => x.Number == firstColumn)) continue; + TimeSpan? departureTime = TimeSpan.Parse(data[4]); + TimeSpan? arrivalTime = departureTime.Value.Subtract(TimeSpan.FromMinutes(2)); + if (data[1].Split("-")[0] == SelectedStation.Name) + { + arrivalTime = null; + } + else if (data[1].Split("-")[1] == SelectedStation.Name) + { + departureTime = null; + } + var trainStop = new TrainStop() + { + Number = firstColumn, + Origin = data[1].Split("-")[0], + Terminal = data[1].Split("-")[1], + ArrivalTime = arrivalTime, + DepartureTime = departureTime, + }; + TrainStops.Add(RandomTrainStopProperties(trainStop)); + } + } + } + catch (Exception e) + { + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); + } + } + [RelayCommand] + public async Task ImportFrompyETRC() + { + if (!await CheckCanImport()) return; + + var path = _dialogService.GetFile([".pyetgr", ".pyetdb", ".json"]); + if (path == null) return; + try + { + using var fs = File.OpenRead(path); + var doc = await JsonDocument.ParseAsync(fs); + + var root = doc.RootElement; + if (!root.TryGetProperty("trains", out var trains)) return; + + foreach (var train in trains.EnumerateArray()) + { + if (!train.TryGetProperty("timetable", out var timetable)) continue; + var stops = timetable.EnumerateArray().ToArray(); + if (stops.Length == 0) continue; + + string origin = stops[0].GetProperty("zhanming").GetString(); + string terminal = stops[^1].GetProperty("zhanming").GetString(); + + TimeSpan? arrivalTime = null; + TimeSpan? departureTime = null; + + foreach (var stop in stops) + { + string name = stop.GetProperty("zhanming").GetString(); + if (name != SelectedStation.Name) continue; + + string cfsj = stop.GetProperty("cfsj").GetString(); + string ddsj = stop.GetProperty("ddsj").GetString(); + + if (cfsj == ddsj) + { + if (name == origin) + { + departureTime = RoundToMinute(cfsj); + arrivalTime = null; + } + else if (name == terminal) + { + departureTime = null; + arrivalTime = RoundToMinute(ddsj); + } + } + else + { + departureTime = RoundToMinute(cfsj); + arrivalTime = RoundToMinute(ddsj); + } + break; + } + if (arrivalTime == null && departureTime == null) continue; + string number = train.GetProperty("checi").EnumerateArray().FirstOrDefault().GetString()?.Split('/')?.FirstOrDefault() ?? string.Empty; + + var trainStop = new TrainStop + { + Number = number, + Origin = origin, + Terminal = terminal, + ArrivalTime = arrivalTime, + DepartureTime = departureTime, + }; + + TrainStops.Add(RandomTrainStopProperties(trainStop)); + } + } + catch (Exception e) + { + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); + } + } + private static TimeSpan RoundToMinute(string timeString) + { + if (TimeSpan.TryParse(timeString, out var time)) + return TimeSpan.FromMinutes(Math.Round(time.TotalMinutes)); + return default; + } + [RelayCommand] + public async Task ImportFromExcel() + { + if (TrainStops.Count != 0) + { + if (!await _dialogService.GetConfirmAsync("当前操作可能会清空时刻表。是否继续?")) + { + return; + } + } + var path = _dialogService.GetFile([".xlsx"]); + if (path == null) return; + try + { + ExcelPackage.License.SetNonCommercialPersonal("CRSim"); + using var package = new ExcelPackage(new FileInfo(path)); + var worksheet = package.Workbook.Worksheets[0]; + int rowCount = worksheet.Dimension.Rows; + TrainStops.Clear(); + for (int row = 2; row <= rowCount; row++) + { + if (worksheet.Cells[row, 1].Text.Trim() == "" || + worksheet.Cells[row, 2].Text.Trim() == "" || + worksheet.Cells[row, 5].Text.Trim() == "" || + worksheet.Cells[row, 6].Text.Trim() == "" || + worksheet.Cells[row, 8].Text.Trim() == "" || + worksheet.Cells[row, 9].Text.Trim() == "") + { + continue; + } + + TrainStops.Add(new TrainStop + { + Number = worksheet.Cells[row, 1].Text.Trim(), + 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, + TicketCheckIds = [.. worksheet.Cells[row, 5].Text + .Split(' ', StringSplitOptions.RemoveEmptyEntries) + .SelectMany(entry => + { + var parts = entry.Split('|', 2); // 分成候车区名和检票口字符串 + if (parts.Length < 2) + return null; + + var waitingAreaName = parts[0]; + var ticketCheckNames = parts[1].Split(',', StringSplitOptions.RemoveEmptyEntries); + + var waitingArea = WaitingAreas.FirstOrDefault(w => w.Name == waitingAreaName); + if (waitingArea == null) + { + waitingArea = new WaitingArea + { + Name = waitingAreaName, + TicketChecks = [] + }; + WaitingAreas.Add(waitingArea); + } + + return ticketCheckNames.Select(tcName => + { + var tc = waitingArea.TicketChecks.FirstOrDefault(c => c.Name == tcName); + if (tc == null) + { + tc = new TicketCheck { Name = tcName }; + waitingArea.TicketChecks.Add(tc); + } + return tc.Id; + }); + })], + 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(), + Status = worksheet.Cells[row, 10].Text switch + { + "停运" => null, + "正点" => TimeSpan.Zero, + var t => TimeSpan.TryParseExact(t, @"hh\:mm", null, out TimeSpan ts) ? ts : TimeSpan.Zero + } + }); + } + } + catch (Exception e) + { + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); + } + } + [RelayCommand] + public async Task ExportToExcel() + { + var path = _dialogService.SaveFile(".xlsx", "data"); + if (path == null) return; + var converter = new TicketChecksToStringConverter() { WaitingAreas = WaitingAreas }; + ExcelPackage.License.SetNonCommercialPersonal("CRSim"); + using var package = new ExcelPackage(); + var worksheet = package.Workbook.Worksheets.Add("Sheet1"); + worksheet.Cells[1, 1].Value = "车次"; + 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 = "终到站"; + 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 = (string)converter.Convert(TrainStops[i].TicketCheckIds, null, null, 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; + worksheet.Cells[i + 2, 10].Value = TrainStops[i].Status switch + { + null => "停运", + var ts when ts == TimeSpan.Zero => "正点", + var ts => ts.Value.ToString(@"hh\:mm") + }; + } + await package.SaveAsAsync(new FileInfo(path)); + } + [RelayCommand] + public async Task AddTrainStop() + { + var newTrainStop = await _dialogService.GetInputTrainStopAsync([.. WaitingAreas], [.. Platforms.Select(x => x.Name)]); + if (newTrainStop != null) + { + if (TrainStops.Any(x => x.Number == newTrainStop.Number)) + { + await _dialogService.ShowMessageAsync("添加失败", $"车次 {newTrainStop.Number} 已存在。"); + return; + } + TrainStops.Add(newTrainStop); + } + } + [RelayCommand] + public void DeleteTrainStop(object selectedTrainStop) + { + if (selectedTrainStop is TrainStop s) + { + TrainStops.Remove(s); + } + } + [RelayCommand] + public async Task DeleteAllTrainStop() + { + if (!await _dialogService.GetConfirmAsync("当前操作会删除全部车次,是否继续?")) return; + TrainStops.Clear(); + } + [RelayCommand] + public async Task EditTrainStop(object _selectedTrainStop) + { + if (_selectedTrainStop is TrainStop 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) + { + await _dialogService.ShowMessageAsync("修改失败", $"车次 {newTrainStop.Number} 已存在。"); + return; + } + TrainStops[TrainStops.IndexOf(selectedTrainStop)] = newTrainStop; + } + } + } + [RelayCommand] + public async Task ImportFromInternet() + { + if (!await CheckCanImport()) return; + var stops = await _networkService.GetTrainNumbersAsync(SelectedStation.Name); + if (stops is not null && stops.Count != 0) + { + TrainStops.Clear(); + foreach (TrainStop t in stops) + { + TrainStops.Add(RandomTrainStopProperties(t)); + } + } + else + { + await _dialogService.ShowMessageAsync("获取失败", "服务器繁忙或车站不存在。"); + } + } + private async Task CheckCanImport() + { + if (WaitingAreas.All(x => x.TicketChecks.Count == 0)) + { + await _dialogService.ShowMessageAsync("导入失败", "请先添加检票口。"); + return false; + } + if (Platforms.Count == 0) + { + await _dialogService.ShowMessageAsync("导入失败", "请先添加站台。"); + return false; + } + if (TrainStops.Count != 0) + { + if (!await _dialogService.GetConfirmAsync("当前操作会清空时刻表。是否继续?")) + { + return false; + } + } + return true; + } + private TrainStop RandomTrainStopProperties(TrainStop t) + { + t.Landmark = new[] { "红色", "绿色", "褐色", "蓝色", "紫色", "黄色", "橙色", null }[new Random().Next(8)]; + t.Length = t.Number.StartsWith('G') || t.Number.StartsWith('D') || t.Number.StartsWith('C') ? Math.Abs(t.Number.GetHashCode()) % 3 == 0 ? 8 : 16 : 18; + t.Platform = ( + Platforms + .Where(p => !TrainStops.Any(s => s.Platform == p.Name && + ((s.ArrivalTime ?? (s.DepartureTime - TimeSpan.FromMinutes(3))) < + (t.DepartureTime ?? (t.ArrivalTime + TimeSpan.FromMinutes(3)))) && + ((s.DepartureTime ?? (s.ArrivalTime + TimeSpan.FromMinutes(3))) > + (t.ArrivalTime ?? (t.DepartureTime - TimeSpan.FromMinutes(3)))))) + .Select(p => p.Name) + .ToList() + ) is { Count: > 0 } free ? free[Random.Shared.Next(free.Count)] : Platforms[Random.Shared.Next(Platforms.Count)].Name; + if (t.DepartureTime.HasValue) + { + string platformA = t.Platform + "A"; + string platformB = t.Platform + "B"; + // 查找有匹配 A 和 B 检票口的候车区 + var targetWaitingArea = WaitingAreas + .FirstOrDefault(w => + w.TicketChecks.Any(tc => tc.Name == platformA) && + w.TicketChecks.Any(tc => tc.Name == platformB)); + + if (targetWaitingArea is not null) + { + t.TicketCheckIds = [.. targetWaitingArea.TicketChecks + .Where(tc => tc.Name == platformA || tc.Name == platformB) + .Select(tc => tc.Id)]; + } + else + { + var allTicketChecks = WaitingAreas.SelectMany(w => w.TicketChecks).ToList(); + if (allTicketChecks.Count > 0) + { + var randomIndex = Random.Shared.Next(allTicketChecks.Count); + t.TicketCheckIds = [allTicketChecks[randomIndex].Id]; + } + else + { + t.TicketCheckIds = []; + } + } + } + return t; + } + #endregion + + [RelayCommand] + public async Task SaveChanges() + { + if (!await Validate()) return; + _databaseService.UpdateStation(SelectedStation.Name, GenerateStation(SelectedStation.Name, WaitingAreas, TrainStops, Platforms)); + await _databaseService.SaveData(); + UpdateInfoItems(); + } + + 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); + foreach (var group in platformGroups) + { + var stops = group.OrderBy(ts => ts.ArrivalTime ?? TimeSpan.Zero).ToList(); + for (int i = 0; i < stops.Count - 1; i++) + { + var current = stops[i]; + var next = stops[i + 1]; + // 判断时间是否重叠 + if (current.DepartureTime != null && next.ArrivalTime != null && + current.DepartureTime > next.ArrivalTime) + { + warningMessage += $"\n站台 {group.Key} 车次 {current.Number} ({current.ArrivalTime:hh\\:mm}-{current.DepartureTime:hh\\:mm}) 与车次 {next.Number} ({next.ArrivalTime:hh\\:mm}-{next.DepartureTime:hh\\:mm}) 时间重叠;"; + } + } + } + + foreach (var s in TrainStops) + { + if (s.DepartureTime.HasValue) + { + var allTicketChecks = new HashSet(WaitingAreas.SelectMany(x => x.TicketChecks.Select(tc => tc.Id))); + var toRemove = s.TicketCheckIds.Where(id => !allTicketChecks.Contains(id)).ToList(); + foreach (var id in toRemove) + { + warningMessage += $"\n车次 {s.Number} 所分配的检票口 {id} 不存在,已自动删除该检票口;"; + s.TicketCheckIds.Remove(id); + } + } + if (!Platforms.Any(x => x.Name == s.Platform)) + { + errorMessage += $"\n车次 {s.Number} 所分配的站台 {s.Platform} 不存在;"; + } + if (s.DepartureTime == null && s.ArrivalTime == null) + { + errorMessage += $"\n车次 {s.Number} 未配置到发时间;"; + } + if (s.Length == 0) + { + errorMessage += $"\n车次 {s.Number} 长度为0;"; + } + } + if (errorMessage == "") + { + if (warningMessage != "") await _dialogService.ShowTextAsync("警告", $"发现 {warningMessage.Split("\n").Length - 1} 个警告:{warningMessage}\n配置已保存,您可以选择忽略这些警告,亦或手动优化。"); + return true; + } + await _dialogService.ShowTextAsync("保存失败", $"发现 {errorMessage.Split("\n").Length - 1} 个错误和 {warningMessage.Split("\n").Length - 1} 个警告:{errorMessage}{warningMessage}\n请修复所有错误后再次尝试保存。"); + return false; + } + +} \ No newline at end of file diff --git a/CRSim/ViewModels/Pages/DataManagement/TrainNumberManagementPageViewModel.cs b/CRSim/ViewModels/TrainNumberManagementPageViewModel.cs similarity index 62% rename from CRSim/ViewModels/Pages/DataManagement/TrainNumberManagementPageViewModel.cs rename to CRSim/ViewModels/TrainNumberManagementPageViewModel.cs index 5068a51..e647a1b 100644 --- a/CRSim/ViewModels/Pages/DataManagement/TrainNumberManagementPageViewModel.cs +++ b/CRSim/ViewModels/TrainNumberManagementPageViewModel.cs @@ -1,32 +1,20 @@ -using CRSim.Core.Models; -using OfficeOpenXml; -using System; -using System.Collections.Specialized; -using System.IO; -using static System.Runtime.InteropServices.JavaScript.JSType; +namespace CRSim.ViewModels; -namespace CRSim.ViewModels; - -public partial class TrainNumberManagementPageViewModel : ObservableObject +public partial class TrainNumberManagementPageViewModel : ObservableObject { - [ObservableProperty] - private string _pageTitle = "车次管理"; - - [ObservableProperty] - private string _pageDescription = "看起来不是很温馨的提醒:改完记得保存! Owo"; + [ObservableProperty] + public partial string PageTitle { get; set; } = "车次管理"; [ObservableProperty] - private bool _isSelected = false; - + public partial bool IsSelected { get; set; } = false; public ObservableCollection TrainNumbers { get; set; } = []; [ObservableProperty] - private TrainNumber _selectedTrainNumber = new(); + public partial TrainNumber SelectedTrainNumber { get; set; } = new(); [ObservableProperty] - private int _progressValue = 0; - - public ObservableCollection TimeTable { get; private set; }= []; + public partial int ProgressValue { get; set; } = 0; + public ObservableCollection TimeTable { get; private set; } = []; public ObservableCollection
Sections { get; private set; } = []; @@ -38,30 +26,39 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject #region CheckBox Status [ObservableProperty] - private bool _sWChecked = false; + public partial bool SWChecked { get; set; } = false; + [ObservableProperty] - private bool _zYYChecked = false; + public partial bool ZYYChecked { get; set; } = false; + [ObservableProperty] - private bool _zYChecked = false; + public partial bool ZYChecked { get; set; } = false; + [ObservableProperty] - private bool _zEChecked = false; + public partial bool ZEChecked { get; set; } = false; + [ObservableProperty] - private bool _rWChecked = false; + public partial bool RWChecked { get; set; } = false; + [ObservableProperty] - private bool _yWChecked = false; + public partial bool YWChecked { get; set; } = false; + [ObservableProperty] - private bool _gRWChecked = false; + public partial bool GRWChecked { get; set; } = false; + [ObservableProperty] - private bool _rZChecked = false; + public partial bool RZChecked { get; set; } = false; + [ObservableProperty] - private bool _yZChecked = false; + public partial bool YZChecked { get; set; } = false; + [ObservableProperty] - private bool _wZChecked = false; + public partial bool WZChecked { get; set; } = false; #endregion - public TrainNumberManagementPageViewModel(IDatabaseService databaseService,IDialogService dialogService,INetworkService networkService) + public TrainNumberManagementPageViewModel(IDatabaseService databaseService, IDialogService dialogService, INetworkService networkService) { - _databaseService = databaseService; + _databaseService = databaseService; _dialogService = dialogService; _networkService = networkService; RefreshTrainNumbers(); @@ -80,11 +77,11 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject [RelayCommand] public void TrainNumberSelected(object selectedNumber) { - if (selectedNumber is TrainNumber s) + if (selectedNumber is SelectionChangedEventArgs sc && sc.AddedItems.Count > 0) { TimeTable.Clear(); Sections.Clear(); - SelectedTrainNumber = _databaseService.GetTrainNumberByNumber(s.Number); + SelectedTrainNumber = _databaseService.GetTrainNumberByNumber(((TrainNumber)sc.AddedItems[0]).Number); foreach (TrainStop t in SelectedTrainNumber.TimeTable) { TimeTable.Add(t); @@ -110,7 +107,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject { SWChecked = false; ZYYChecked = false; - ZYChecked = false; + ZYChecked = false; ZEChecked = false; GRWChecked = false; RWChecked = false; @@ -121,7 +118,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject } IsSelected = true; } - if(selectedNumber == null) + if (selectedNumber == null) { IsSelected = false; } @@ -129,15 +126,15 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject [RelayCommand] public async Task AddTrainNumber() { - string? number = _dialogService.GetInput("请输入车次号")?.ToUpper(); + string? number = (await _dialogService.GetInputAsync("请输入车次号",""))?.ToUpper(); if (number != null) { if (TrainNumbers.Select(x => x.Number).Contains(number)) { - _dialogService.ShowMessage("添加失败", $"车次号 {number} 已存在。"); + await _dialogService.ShowMessageAsync("添加失败", $"车次号 {number} 已存在。"); return; } - _databaseService.AddTrainNumber(number); + _databaseService.AddTrainNumber(new TrainNumber() { Number = number }); await _databaseService.SaveData(); RefreshTrainNumbers(); } @@ -155,7 +152,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject [RelayCommand] public async Task DeleteAllTrainNumbers() { - if (!_dialogService.GetConfirm("当前操作会删除全部车次,是否继续?")) return; + if (!await _dialogService.GetConfirmAsync("当前操作会删除全部车次,是否继续?")) return; var trainNumbers = _databaseService.GetAllTrainNumbers().ToList(); foreach (TrainNumber trainNumber in trainNumbers) { @@ -167,7 +164,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject [RelayCommand] public async Task ImportFromLulutong() { - var path = _dialogService.GetFile("CSV 文件 (*.csv)|*.csv|所有文件 (*.*)|*.*"); + var path = _dialogService.GetFile([".csv"]); if (path == null) return; try { @@ -175,7 +172,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject var intersection = numbers.Intersect(TrainNumbers.Select(x => x.Number)).ToList(); if (intersection.Count != 0) { - if (!_dialogService.GetConfirm("当前操作可能覆盖现有车次配置,是否继续?")) return; + if (!await _dialogService.GetConfirmAsync("当前操作可能覆盖现有车次配置,是否继续?")) return; } foreach (string s in intersection) { @@ -191,8 +188,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject List? timeTable = await _networkService.GetTimeTableAsync(number); await Task.Delay(100); timeTable ??= []; - _databaseService.AddTrainNumber(number); - _databaseService.UpdateTrainNumber(_databaseService.GetTrainNumberByNumber(number), timeTable, GenerateSections(number,timeTable)); + _databaseService.AddTrainNumber(new TrainNumber() { Number = number, TimeTable = timeTable, Sections = GenerateSections(number, timeTable) }); } ProgressValue = 0; await _databaseService.SaveData(); @@ -200,7 +196,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject } catch (Exception e) { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); ProgressValue = 0; } } @@ -209,9 +205,9 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject { if (TrainNumbers.Count != 0) { - if (!_dialogService.GetConfirm("当前操作可能覆盖现有车次配置,是否继续?")) return; + if (!await _dialogService.GetConfirmAsync("当前操作可能覆盖现有车次配置,是否继续?")) return; } - var path = _dialogService.GetFile("pyETRC 运行图文件 (*.pyetgr;*.json)|*.pyetgr;*.json|pyETRC 车次数据库文件 (*.pyetdb;*.json)|*.pyetdb;*.json|所有文件 (*.*)|*.*"); + var path = _dialogService.GetFile([".pyetgr", ".pyetdb", ".json"]); if (path == null) return; try { @@ -236,8 +232,8 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject string name = stop.GetProperty("zhanming").GetString(); string cfsj = stop.GetProperty("cfsj").GetString(); string ddsj = stop.GetProperty("ddsj").GetString(); - if (i != 0) arrivalTime = Utilities.RoundToMinute(ddsj); - if (i != stops.Length-1) departureTime = Utilities.RoundToMinute(cfsj); + if (i != 0) arrivalTime = RoundToMinute(ddsj); + if (i != stops.Length - 1) departureTime = RoundToMinute(cfsj); if (arrivalTime == departureTime) continue; timeTable.Add(new TrainStop { @@ -248,16 +244,20 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject } if (!TrainNumbers.Select(x => x.Number).Contains(number)) { - _databaseService.AddTrainNumber(number); + _databaseService.AddTrainNumber(new TrainNumber() + { + Number = number, + TimeTable = timeTable, + Sections = GenerateSections(number, timeTable) + }); } - _databaseService.UpdateTrainNumber(_databaseService.GetTrainNumberByNumber(number), timeTable, GenerateSections(number, timeTable)); } await _databaseService.SaveData(); RefreshTrainNumbers(); } catch (Exception e) { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); } } #endregion @@ -266,7 +266,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject [RelayCommand] public async Task TimeTableImportFromInternetBatch() { - if (!_dialogService.GetConfirm("当前操作会覆盖现有车次配置,是否继续?")) return; + if (!await _dialogService.GetConfirmAsync("当前操作会覆盖现有车次配置,是否继续?")) return; TrainNumberSelected(null); var numbers = TrainNumbers.Select(x => x.Number).ToList(); foreach (string s in numbers) @@ -283,42 +283,19 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject List? timeTable = await _networkService.GetTimeTableAsync(number); await Task.Delay(100); timeTable ??= []; - _databaseService.AddTrainNumber(number); - var sections = new List
(); - var isEmu = number.StartsWith('G') || number.StartsWith('D') || number.StartsWith('C'); - for (int i = 1; i < timeTable.Count; i++) - { - sections.Add(new Section - { - From = timeTable[i - 1].Station, - To = timeTable[i].Station, - Tickets = new Tickets - { - SW = isEmu ? new() : null, - ZYY = isEmu ? new() : null, - ZY = isEmu ? new() : null, - ZE = isEmu ? new() : null, - RZ = !isEmu ? new() : null, - GRW = !isEmu ? new() : null, - RW = !isEmu ? new() : null, - WZ = new(), - YW = !isEmu ? new() : null, - YZ = !isEmu ? new() : null - } - }); - } - _databaseService.UpdateTrainNumber(_databaseService.GetTrainNumberByNumber(number), timeTable, sections); + _databaseService.AddTrainNumber(new TrainNumber() { Number = number, TimeTable = timeTable, Sections = GenerateSections(number,timeTable) }); } ProgressValue = 0; await _databaseService.SaveData(); RefreshTrainNumbers(); } + [RelayCommand] - public async Task TimeTableImportFromInternet() + public async Task TimeTableImportFromInternet() { if (TimeTable.Count != 0) { - if (!_dialogService.GetConfirm("当前操作可能清空时刻表。是否继续?")) + if (!await _dialogService.GetConfirmAsync("当前操作可能清空时刻表。是否继续?")) { return; } @@ -336,18 +313,18 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject } else { - _dialogService.ShowMessage("获取失败", "未找到该车次。"); + await _dialogService.ShowMessageAsync("获取失败", "未找到该车次。"); } } [RelayCommand] - public void AddTrainStop(object selectedTrainStop) + public async Task AddTrainStop(object selectedTrainStop) { - var newTrainStop = _dialogService.GetInputTrainNumberStop(null); + var newTrainStop = await _dialogService.GetInputTrainNumberStopAsync(); if (newTrainStop != null) { if (TimeTable.Any(x => x.Station == newTrainStop.Station)) { - _dialogService.ShowMessage("错误", $"车站 {newTrainStop.Station} 已存在。"); + await _dialogService.ShowMessageAsync("错误", $"车站 {newTrainStop.Station} 已存在。"); } if (selectedTrainStop != null) { @@ -363,25 +340,25 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject [RelayCommand] public void DeleteTrainStop(object selectedTrainStop) { - if(selectedTrainStop is TrainStop s) + if (selectedTrainStop is TrainStop s) { TimeTable.Remove(s); RefreshSections(); } } [RelayCommand] - public void DeleteAllTrainStop() + public async Task DeleteAllTrainStop() { - if (!_dialogService.GetConfirm("当前操作会删除全部停站,是否继续?")) return; + if (!await _dialogService.GetConfirmAsync("当前操作会删除全部停站,是否继续?")) return; TimeTable.Clear(); RefreshSections(); } [RelayCommand] - public void EditTrainStop(object _selectedTrainStop) + public async Task EditTrainStop(object _selectedTrainStop) { if (_selectedTrainStop is TrainStop selectedTrainStop) { - var newTrainStop = _dialogService.GetInputTrainNumberStop(selectedTrainStop); + var newTrainStop = await _dialogService.EditTrainNumberStopAsync(selectedTrainStop); if (newTrainStop != null) { TimeTable[TimeTable.IndexOf(selectedTrainStop)] = newTrainStop; @@ -391,7 +368,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject [RelayCommand] public void TimeTableExportToExcel() { - var path = _dialogService.SaveFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*", "data"); + var path = _dialogService.SaveFile(".xlsx", "data"); if (path == null) return; ExcelPackage.License.SetNonCommercialPersonal("CRSim"); using var package = new ExcelPackage(); @@ -408,16 +385,16 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject package.SaveAs(new FileInfo(path)); } [RelayCommand] - public void TimeTableImportFromExcel() + public async Task TimeTableImportFromExcel() { if (TimeTable.Count != 0) { - if (!_dialogService.GetConfirm("当前操作可能会清空时刻表。是否继续?")) + if (!await _dialogService.GetConfirmAsync("当前操作可能会清空时刻表。是否继续?")) { return; } } - var path = _dialogService.GetFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*"); + var path = _dialogService.GetFile([".xlsx"]); if (path == null) return; try { @@ -439,20 +416,20 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject { Station = worksheet.Cells[row, 1].Text.Trim(), ArrivalTime = TimeSpan.TryParseExact(worksheet.Cells[row, 2].Text.Trim(), @"hh\:mm", null, out TimeSpan arrival) ? arrival : null, - DepartureTime = TimeSpan.TryParseExact(worksheet.Cells[row, 3].Text.Trim(), @"hh\:mm", null, out TimeSpan departure) ? departure:null + DepartureTime = TimeSpan.TryParseExact(worksheet.Cells[row, 3].Text.Trim(), @"hh\:mm", null, out TimeSpan departure) ? departure : null }); } RefreshSections(); } catch (Exception e) { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); } } #endregion #region Sections - private static List
GenerateSections(string number,List timeTable) + private static List
GenerateSections(string number, List timeTable) { var sections = new List
(); var isEmu = number.StartsWith('G') || number.StartsWith('D') || number.StartsWith('C'); @@ -497,7 +474,6 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject } RefreshSectionsUI(); } - } private void RefreshSections() @@ -505,9 +481,9 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject if (TimeTable.Count < 2) return; List
oldSections = [.. Sections]; Sections.Clear(); - for(int i=1;i< TimeTable.Count;i++) + for (int i = 1; i < TimeTable.Count; i++) { - if (oldSections.Any(x=>x.From== TimeTable[i - 1].Station&& x.To == TimeTable[i].Station)) + if (oldSections.Any(x => x.From == TimeTable[i - 1].Station && x.To == TimeTable[i].Station)) { Sections.Add(oldSections.FirstOrDefault(x => x.From == TimeTable[i - 1].Station && x.To == TimeTable[i].Station)); continue; @@ -536,45 +512,38 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject #region Price [RelayCommand] - public void SetPriceBatch(object s) + public async Task SetPriceBatch(object s) { if (s is string type) { - if (!_dialogService.GetConfirm("当前操作会覆盖现有车次配置,是否继续?")) return; + if (!await _dialogService.GetConfirmAsync("当前操作会覆盖现有车次配置,是否继续?")) return; TrainNumberSelected(null); - Dictionary<(string, string, string), double> sectionPrices = []; string[] seatTypes = ["SW", "GRW", "YW", "ZE", "ZYY", "ZY", "WZ", "RW", "YZ", "RZ"]; foreach (var trainNumber in TrainNumbers) { - bool isHighSpeed = trainNumber.Number.StartsWith('G') || trainNumber.Number.StartsWith('D') || trainNumber.Number.StartsWith('C'); - if ((type == "1" && isHighSpeed) || (type == "2" && !isHighSpeed)) continue; - + bool isHighSpeed = trainNumber.Number.StartsWith('G') + || trainNumber.Number.StartsWith('D') + || trainNumber.Number.StartsWith('C'); + if ((type == "1" && !isHighSpeed) || (type == "2" && isHighSpeed)) + continue; foreach (var section in trainNumber.Sections) { foreach (var seatType in seatTypes) { - if (section.Tickets.GetType().GetProperty(seatType)?.GetValue(section.Tickets) is Ticket ticket && (!sectionPrices.ContainsKey((section.From, section.To, seatType)) || sectionPrices[(section.From, section.To, seatType)] == 0.0)) + if (section.Tickets.GetType().GetProperty(seatType)?.GetValue(section.Tickets) + is Ticket ticket) { - sectionPrices[(section.From, section.To, seatType)] = ticket.Price; - } - } - } - } - - foreach (var trainNumber in TrainNumbers) - { - bool isHighSpeed = trainNumber.Number.StartsWith('G') || trainNumber.Number.StartsWith('D') || trainNumber.Number.StartsWith('C'); - if ((type == "1" && isHighSpeed) || (type == "2" && !isHighSpeed)) continue; - - foreach (var section in trainNumber.Sections) - { - foreach (var seatType in seatTypes) - { - if (section.Tickets.GetType().GetProperty(seatType)?.GetValue(section.Tickets) is Ticket ticket) - { - ticket.Price = sectionPrices[(section.From, section.To, seatType)]; + var key = (section.From, section.To, seatType); + // 如果没有价格或价格为 0,则存入 sectionPrices + if (!sectionPrices.TryGetValue(key, out double value) || value == 0.0) + { + value = ticket.Price; + sectionPrices[key] = value; + } + // 统一用 sectionPrices 的值覆盖回 ticket.Price + ticket.Price = value; } } } @@ -582,9 +551,9 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject } } [RelayCommand] - public void PriceImportFromExcel() + public async Task PriceImportFromExcel() { - var path = _dialogService.GetFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*"); + var path = _dialogService.GetFile([".xlsx"]); if (path == null) return; try { @@ -597,59 +566,29 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject var section = Sections.FirstOrDefault(x => x.From == worksheet.Cells[row, 1].Text.Trim() && x.To == worksheet.Cells[row, 2].Text.Trim()); if (section != null) { - if (section.Tickets.SW != null) - { - section.Tickets.SW.Price = double.TryParse(worksheet.Cells[row, 3].Text.Trim(), out double sw) ? sw : 0.0; - } - if (section.Tickets.ZYY != null) - { - section.Tickets.ZYY.Price = double.TryParse(worksheet.Cells[row, 4].Text.Trim(), out double zyy) ? zyy : 0.0; - } - if (section.Tickets.ZY != null) - { - section.Tickets.ZY.Price = double.TryParse(worksheet.Cells[row, 5].Text.Trim(), out double zy) ? zy : 0.0; - } - if (section.Tickets.ZE != null) - { - section.Tickets.ZE.Price = double.TryParse(worksheet.Cells[row, 6].Text.Trim(), out double ze) ? ze : 0.0; - } - if (section.Tickets.GRW != null) - { - section.Tickets.GRW.Price = double.TryParse(worksheet.Cells[row, 7].Text.Trim(), out double grw) ? grw : 0.0; - } - if (section.Tickets.RW != null) - { - section.Tickets.RW.Price = double.TryParse(worksheet.Cells[row, 8].Text.Trim(), out double rw) ? rw : 0.0; - } - if (section.Tickets.YW != null) - { - section.Tickets.YW.Price = double.TryParse(worksheet.Cells[row, 9].Text.Trim(), out double yw) ? yw : 0.0; - } - if (section.Tickets.RZ != null) - { - section.Tickets.RZ.Price = double.TryParse(worksheet.Cells[row, 10].Text.Trim(), out double rz) ? rz : 0.0; - } - if (section.Tickets.YZ != null) - { - section.Tickets.YZ.Price = double.TryParse(worksheet.Cells[row, 11].Text.Trim(), out double yz) ? yz : 0.0; - } - if (section.Tickets.WZ != null) - { - section.Tickets.WZ.Price = double.TryParse(worksheet.Cells[row, 12].Text.Trim(), out double wz) ? wz : 0.0; - } + section.Tickets.SW?.Price = double.TryParse(worksheet.Cells[row, 3].Text.Trim(), out double sw) ? sw : 0.0; + section.Tickets.ZYY?.Price = double.TryParse(worksheet.Cells[row, 4].Text.Trim(), out double zyy) ? zyy : 0.0; + section.Tickets.ZY?.Price = double.TryParse(worksheet.Cells[row, 5].Text.Trim(), out double zy) ? zy : 0.0; + section.Tickets.ZE?.Price = double.TryParse(worksheet.Cells[row, 6].Text.Trim(), out double ze) ? ze : 0.0; + section.Tickets.GRW?.Price = double.TryParse(worksheet.Cells[row, 7].Text.Trim(), out double grw) ? grw : 0.0; + section.Tickets.RW?.Price = double.TryParse(worksheet.Cells[row, 8].Text.Trim(), out double rw) ? rw : 0.0; + section.Tickets.YW?.Price = double.TryParse(worksheet.Cells[row, 9].Text.Trim(), out double yw) ? yw : 0.0; + section.Tickets.RZ?.Price = double.TryParse(worksheet.Cells[row, 10].Text.Trim(), out double rz) ? rz : 0.0; + section.Tickets.YZ?.Price = double.TryParse(worksheet.Cells[row, 11].Text.Trim(), out double yz) ? yz : 0.0; + section.Tickets.WZ?.Price = double.TryParse(worksheet.Cells[row, 12].Text.Trim(), out double wz) ? wz : 0.0; } } RefreshSectionsUI(); } catch (Exception e) { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); } } [RelayCommand] public void PriceExportToExcel() { - var path = _dialogService.SaveFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*", "data"); + var path = _dialogService.SaveFile(".xlsx", "data"); if (path == null) return; ExcelPackage.License.SetNonCommercialPersonal("CRSim"); using var package = new ExcelPackage(); @@ -687,26 +626,26 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject #region Quantity [RelayCommand] - public void SetQuantityBatch(object s) + public async Task SetQuantityBatch(object s) { - if(s is string extent) + if (s is string extent) { - if (!_dialogService.GetConfirm("当前操作会覆盖现有车次配置,是否继续?")) return; + if (!await _dialogService.GetConfirmAsync("当前操作会覆盖现有车次配置,是否继续?")) return; TrainNumberSelected(null); - foreach(var trainNumber in TrainNumbers) + foreach (var trainNumber in TrainNumbers) { - foreach(var Section in trainNumber.Sections) + foreach (var Section in trainNumber.Sections) { - if (Section.Tickets.SW != null) Section.Tickets.SW.Quantity = RandomQuantity(extent); - if (Section.Tickets.GRW != null) Section.Tickets.GRW.Quantity = RandomQuantity(extent); - if (Section.Tickets.YW != null) Section.Tickets.YW.Quantity = RandomQuantity(extent); - if (Section.Tickets.ZE != null) Section.Tickets.ZE.Quantity = RandomQuantity(extent); - if (Section.Tickets.ZYY != null) Section.Tickets.ZYY.Quantity = RandomQuantity(extent); - if (Section.Tickets.ZY != null) Section.Tickets.ZY.Quantity = RandomQuantity(extent); - if (Section.Tickets.WZ != null) Section.Tickets.WZ.Quantity = RandomQuantity(extent); - if (Section.Tickets.RW != null) Section.Tickets.RW.Quantity = RandomQuantity(extent); - if (Section.Tickets.YZ != null) Section.Tickets.YZ.Quantity = RandomQuantity(extent); - if (Section.Tickets.RZ != null) Section.Tickets.RZ.Quantity = RandomQuantity(extent); + Section.Tickets.SW?.Quantity = RandomQuantity(extent); + Section.Tickets.GRW?.Quantity = RandomQuantity(extent); + Section.Tickets.YW?.Quantity = RandomQuantity(extent); + Section.Tickets.ZE?.Quantity = RandomQuantity(extent); + Section.Tickets.ZYY?.Quantity = RandomQuantity(extent); + Section.Tickets.ZY?.Quantity = RandomQuantity(extent); + Section.Tickets.WZ?.Quantity = RandomQuantity(extent); + Section.Tickets.RW?.Quantity = RandomQuantity(extent); + Section.Tickets.YZ?.Quantity = RandomQuantity(extent); + Section.Tickets.RZ?.Quantity = RandomQuantity(extent); } } } @@ -727,9 +666,9 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject return 0; } [RelayCommand] - public void QuantityImportFromExcel() + public async Task QuantityImportFromExcel() { - var path = _dialogService.GetFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*"); + var path = _dialogService.GetFile([".xlsx"]); if (path == null) return; try { @@ -742,59 +681,29 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject var section = Sections.FirstOrDefault(x => x.From == worksheet.Cells[row, 1].Text.Trim() && x.To == worksheet.Cells[row, 2].Text.Trim()); if (section != null) { - if (section.Tickets.SW != null) - { - section.Tickets.SW.Quantity = int.TryParse(worksheet.Cells[row, 3].Text.Trim(), out int sw) ? sw : 0; - } - if (section.Tickets.ZYY != null) - { - section.Tickets.ZYY.Quantity = int.TryParse(worksheet.Cells[row, 4].Text.Trim(), out int zyy) ? zyy : 0; - } - if (section.Tickets.ZY != null) - { - section.Tickets.ZY.Quantity = int.TryParse(worksheet.Cells[row, 5].Text.Trim(), out int zy) ? zy : 0; - } - if (section.Tickets.ZE != null) - { - section.Tickets.ZE.Quantity = int.TryParse(worksheet.Cells[row, 6].Text.Trim(), out int ze) ? ze : 0; - } - if (section.Tickets.GRW != null) - { - section.Tickets.GRW.Quantity = int.TryParse(worksheet.Cells[row, 7].Text.Trim(), out int grw) ? grw : 0; - } - if (section.Tickets.RW != null) - { - section.Tickets.RW.Quantity = int.TryParse(worksheet.Cells[row, 8].Text.Trim(), out int rw) ? rw : 0; - } - if (section.Tickets.YW != null) - { - section.Tickets.YW.Quantity = int.TryParse(worksheet.Cells[row, 9].Text.Trim(), out int yw) ? yw : 0; - } - if (section.Tickets.RZ != null) - { - section.Tickets.RZ.Quantity = int.TryParse(worksheet.Cells[row, 10].Text.Trim(), out int rz) ? rz : 0; - } - if (section.Tickets.YZ != null) - { - section.Tickets.YZ.Quantity = int.TryParse(worksheet.Cells[row, 11].Text.Trim(), out int yz) ? yz : 0; - } - if (section.Tickets.WZ != null) - { - section.Tickets.WZ.Quantity = int.TryParse(worksheet.Cells[row, 12].Text.Trim(), out int wz) ? wz : 0; - } + section.Tickets.SW?.Quantity = int.TryParse(worksheet.Cells[row, 3].Text.Trim(), out int sw) ? sw : 0; + section.Tickets.ZYY?.Quantity = int.TryParse(worksheet.Cells[row, 4].Text.Trim(), out int zyy) ? zyy : 0; + section.Tickets.ZY?.Quantity = int.TryParse(worksheet.Cells[row, 5].Text.Trim(), out int zy) ? zy : 0; + section.Tickets.ZE?.Quantity = int.TryParse(worksheet.Cells[row, 6].Text.Trim(), out int ze) ? ze : 0; + section.Tickets.GRW?.Quantity = int.TryParse(worksheet.Cells[row, 7].Text.Trim(), out int grw) ? grw : 0; + section.Tickets.RW?.Quantity = int.TryParse(worksheet.Cells[row, 8].Text.Trim(), out int rw) ? rw : 0; + section.Tickets.YW?.Quantity = int.TryParse(worksheet.Cells[row, 9].Text.Trim(), out int yw) ? yw : 0; + section.Tickets.RZ?.Quantity = int.TryParse(worksheet.Cells[row, 10].Text.Trim(), out int rz) ? rz : 0; + section.Tickets.YZ?.Quantity = int.TryParse(worksheet.Cells[row, 11].Text.Trim(), out int yz) ? yz : 0; + section.Tickets.WZ?.Quantity = int.TryParse(worksheet.Cells[row, 12].Text.Trim(), out int wz) ? wz : 0; } } RefreshSectionsUI(); } catch (Exception e) { - _dialogService.ShowMessage("导入失败", "文件格式错误或被占用。\n"+e.Message); + await _dialogService.ShowMessageAsync("导入失败", "文件格式错误或被占用。\n" + e.Message); } } [RelayCommand] public void QuantityExportToExcel() { - var path = _dialogService.SaveFile("Excel 工作薄 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*", "data"); + var path = _dialogService.SaveFile(".xlsx", "data"); if (path == null) return; ExcelPackage.License.SetNonCommercialPersonal("CRSim"); using var package = new ExcelPackage(); @@ -838,12 +747,12 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject await _databaseService.SaveData(); } - private Task Validate() + private async Task Validate() { string message = ""; if (TimeTable.Count == 0) { - return Task.FromResult(false); + return false; } if (TimeTable.First().ArrivalTime != null) { @@ -857,7 +766,7 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject { message += "\n存在多个重复停站;"; } - if (TimeTable.Count(x => x.ArrivalTime == null)!=1) + if (TimeTable.Count(x => x.ArrivalTime == null) != 1) { message += "\n存在多个始发站;"; } @@ -865,9 +774,9 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject { message += "\n存在多个终到站;"; } - if (message == "") return Task.FromResult(true); - _dialogService.ShowMessage("保存失败", $"发现 {message.Split("\n").Length - 1} 个错误:{message}\n请修复所有错误后再次尝试保存。"); - return Task.FromResult(false); + if (message == "") return true; + await _dialogService.ShowMessageAsync("保存失败", $"发现 {message.Split("\n").Length - 1} 个错误:{message}\n请修复所有错误后再次尝试保存。"); + return false; } private static List ReadCsvFirstColumn(string filePath) @@ -906,10 +815,16 @@ public partial class TrainNumberManagementPageViewModel : ObservableObject public void RefreshSectionsUI() { var temp = Sections.ToList(); - Sections.Clear(); + Sections.Clear(); foreach (var item in temp) { - Sections.Add(item); + Sections.Add(item); } } + public static TimeSpan RoundToMinute(string timeString) + { + if (TimeSpan.TryParse(timeString, out var time)) + return TimeSpan.FromMinutes(Math.Round(time.TotalMinutes)); + return default; + } } \ No newline at end of file diff --git a/CRSim/ViewModels/WebsiteSimulatorPageViewModel.cs b/CRSim/ViewModels/WebsiteSimulatorPageViewModel.cs new file mode 100644 index 0000000..d3cb5a3 --- /dev/null +++ b/CRSim/ViewModels/WebsiteSimulatorPageViewModel.cs @@ -0,0 +1,27 @@ +using CRSim.WebsiteSimulator; + +namespace CRSim.ViewModels; + +public partial class WebsiteSimulatorPageViewModel(IDialogService _dialogService, Simulator _simulator) : ObservableObject +{ + public string PageTitle = "12306模拟"; + + [RelayCommand] + public async Task StartSimulation() + { + try + { + await _simulator.Start(); + } + catch (Exception e) + { + await _dialogService.ShowMessageAsync("认证失败", e.Message); + } + } + + [RelayCommand] + public void StopSimulation() + { + _simulator.Stop(); + } +} \ No newline at end of file diff --git a/CRSim/ViewModels/Windows/MainWindowViewModel.cs b/CRSim/ViewModels/Windows/MainWindowViewModel.cs deleted file mode 100644 index 36d8545..0000000 --- a/CRSim/ViewModels/Windows/MainWindowViewModel.cs +++ /dev/null @@ -1,151 +0,0 @@ -namespace CRSim.ViewModels -{ - public partial class MainWindowViewModel : ObservableObject - { - [ObservableProperty] - private string _applicationTitle = "CRSim"; - - private readonly DispatcherTimer _timer; - - private string _searchText = string.Empty; - - [ObservableProperty] - private ICollection _controls; - [ObservableProperty] - private ControlInfoDataItem? _selectedControl; - private readonly INavigationService _navigationService; - [ObservableProperty] - private bool _canNavigateback; - - [RelayCommand] - public void Settings() - { - _navigationService.Navigate(typeof(SettingsPage)); - } - - [RelayCommand] - public void Back() - { - _navigationService.NavigateBack(); - } - - [RelayCommand] - public void Forward() - { - _navigationService.NavigateForward(); - } - - public MainWindowViewModel(INavigationService navigationService) - { - _controls = ControlsInfoDataSource.Instance.ControlsInfo; - _navigationService = navigationService; - - _timer = new DispatcherTimer - { - Interval = TimeSpan.FromMilliseconds(400) - }; - _timer.Tick += PerformSearchNavigation; - } - - public void UpdateSearchText(string searchText) - { - _searchText = searchText; - _timer.Stop(); - _timer.Start(); - } - - private void PerformSearchNavigation(object? sender, EventArgs e) - { - _timer.Stop(); - if (string.IsNullOrWhiteSpace(_searchText)) - { - return; - } - - _navigationService.NavigateTo(GetNavigationPageTypeFromName(_searchText, _controls)); - } - - private static Type? GetNavigationPageTypeFromName(string name, ICollection pages) - { - if (pages == null) - { - return null; - } - - foreach (var item in pages) - { - if (item.Title.Equals(name, StringComparison.OrdinalIgnoreCase)) - { - return item.PageType!; - } - - Type? type = GetNavigationPageTypeFromName(name, item.Items); - if (type != null) - { - return type; - } - } - return null; - } - - internal List GetNavigationItemHierarchyFromPageType(Type? pageType) - { - List list = []; - Stack _stack = []; - Stack _revStack = []; - - if (pageType == null) - { - return list; - } - - foreach (var item in Controls) - { - _stack.Push(item); - bool found = FindNavigationItemsHierarchyFromPageType(pageType, ref _stack); - if (found) - { - break; - } - _stack.Pop(); - } - - while (_stack.Count > 0) - { - _revStack.Push(_stack.Pop()); - } - - foreach (var item in _revStack) - { - list.Add(item); - } - - return list; - } - - private static bool FindNavigationItemsHierarchyFromPageType(Type pageType, ref Stack stack) - { - var item = stack.Peek(); - if (pageType == item.PageType) - { - return true; - } - - foreach (var child in item.Items) - { - stack.Push(child); - bool found = FindNavigationItemsHierarchyFromPageType(pageType, ref stack); - if (found) { return true; } - stack.Pop(); - } - - return false; - } - - internal void UpdateCanNavigateBack() - { - CanNavigateback = _navigationService.IsBackHistoryNonEmpty(); - } - - } -} diff --git a/CRSim/Views/DashboardPage.xaml b/CRSim/Views/DashboardPage.xaml new file mode 100644 index 0000000..3d93ae5 --- /dev/null +++ b/CRSim/Views/DashboardPage.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - CommunityToolkit.Mvvm - - - Microsoft.Extensions.DependencyInjection - - - Microsoft.Extensions.Hosting - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CRSim/Views/Pages/SettingsPage.xaml.cs b/CRSim/Views/Pages/SettingsPage.xaml.cs deleted file mode 100644 index ed490a3..0000000 --- a/CRSim/Views/Pages/SettingsPage.xaml.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Text.RegularExpressions; -using System.Windows.Documents; -using System.Windows.Navigation; -using System.Windows.Shapes; -using CRSim.ViewModels; - -namespace CRSim.Views -{ - /// - /// Interaction logic for SettingsPage.xaml - /// - public partial class SettingsPage : Page - { - public SettingsPageViewModel ViewModel { get; } - - public SettingsPage(SettingsPageViewModel viewModel) - { - ViewModel = viewModel; - DataContext = this; - InitializeComponent(); - } - - private void Open_ToolkitInformation(object sender, RoutedEventArgs e) - { - Process.Start(new ProcessStartInfo("https://www.nuget.org/packages/CommunityToolkit.Mvvm/") { UseShellExecute = true }); - } - - private void Open_DIInformation(object sender, RoutedEventArgs e) - { - Process.Start(new ProcessStartInfo("https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection/") { UseShellExecute = true }); - } - - private void Open_HostingInformation(object sender, RoutedEventArgs e) - { - Process.Start(new ProcessStartInfo("https://www.nuget.org/packages/Microsoft.Extensions.Hosting") { UseShellExecute = true }); - } - - private void Open_Issues(object sender, RoutedEventArgs e) - { - Process.Start(new ProcessStartInfo("https://github.com/denglihong2007/CRSim/issues/new") { UseShellExecute = true }); - } - } -} diff --git a/CRSim/Views/Pages/Simulation/ScreenSimulationPage.xaml b/CRSim/Views/Pages/Simulation/ScreenSimulationPage.xaml deleted file mode 100644 index ddf62ee..0000000 --- a/CRSim/Views/Pages/Simulation/ScreenSimulationPage.xaml +++ /dev/null @@ -1,285 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 你想选哪个就选哪个! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 配置模拟参数 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 留空即为默认 - - - - + + + + + + + + + + + + + + + diff --git a/CRSim/Views/PluginManagementPage.xaml.cs b/CRSim/Views/PluginManagementPage.xaml.cs new file mode 100644 index 0000000..303c7cb --- /dev/null +++ b/CRSim/Views/PluginManagementPage.xaml.cs @@ -0,0 +1,23 @@ +using System.Diagnostics; + +namespace CRSim.Views; + +public sealed partial class PluginManagementPage : Page +{ + public PluginManagementPageViewModel ViewModel { get; } = App.AppHost.Services.GetService(); + + public PluginManagementPage() + { + InitializeComponent(); + DataContext = ViewModel; + } + + private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args) + { + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + App.AppHost.Services.GetService().XamlRoot = this.XamlRoot; + } +} diff --git a/CRSim/Views/ScreenSimulatorPage.xaml b/CRSim/Views/ScreenSimulatorPage.xaml new file mode 100644 index 0000000..9e8caf2 --- /dev/null +++ b/CRSim/Views/ScreenSimulatorPage.xaml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +