更新20260604
This commit is contained in:
@@ -33,30 +33,14 @@ public partial class App : Application
|
|||||||
|
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<IStandardRepository, StandardRepository>();
|
services.AddSingleton<IStandardRepository, StandardRepository>();
|
||||||
services.AddSingleton<IPasswordAccessService, PasswordAccessService>();
|
|
||||||
services.AddSingleton<IModbusTelemetryService, ModbusTelemetryService>();
|
services.AddSingleton<IModbusTelemetryService, ModbusTelemetryService>();
|
||||||
services.AddSingleton<IRs485PumpFlowService, Rs485PumpFlowService>();
|
services.AddSingleton<IRs485PumpFlowService, Rs485PumpFlowService>();
|
||||||
services.AddSingleton<IPumpActuationService, Rs485PumpActuationService>();
|
services.AddSingleton<IPumpActuationService, Rs485PumpActuationService>();
|
||||||
services.AddTransient<StartupPasswordViewModel>();
|
|
||||||
services.AddTransient<StartupPasswordWindow>();
|
|
||||||
services.AddSingleton<MainViewModel>();
|
services.AddSingleton<MainViewModel>();
|
||||||
services.AddSingleton<MainWindow>();
|
services.AddSingleton<MainWindow>();
|
||||||
|
|
||||||
_serviceProvider = services.BuildServiceProvider();
|
_serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
var passwordAccessService = _serviceProvider.GetRequiredService<IPasswordAccessService>();
|
|
||||||
var passwordStatus = passwordAccessService.GetStatus();
|
|
||||||
if (passwordStatus.ShouldPromptAtStartup)
|
|
||||||
{
|
|
||||||
var passwordWindow = _serviceProvider.GetRequiredService<StartupPasswordWindow>();
|
|
||||||
var passwordResult = passwordWindow.ShowDialog();
|
|
||||||
if (passwordResult != true || !passwordAccessService.GetStatus().CanLaunch)
|
|
||||||
{
|
|
||||||
Shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
||||||
mainWindow.Show();
|
mainWindow.Show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,30 +19,14 @@ public partial class App : Application
|
|||||||
|
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddSingleton<IStandardRepository, StandardRepository>();
|
services.AddSingleton<IStandardRepository, StandardRepository>();
|
||||||
services.AddSingleton<IPasswordAccessService, PasswordAccessService>();
|
|
||||||
services.AddSingleton<IModbusTelemetryService, ModbusTelemetryService>();
|
services.AddSingleton<IModbusTelemetryService, ModbusTelemetryService>();
|
||||||
services.AddSingleton<IRs485PumpFlowService, Rs485PumpFlowService>();
|
services.AddSingleton<IRs485PumpFlowService, Rs485PumpFlowService>();
|
||||||
services.AddSingleton<IPumpActuationService, Rs485PumpActuationService>();
|
services.AddSingleton<IPumpActuationService, Rs485PumpActuationService>();
|
||||||
services.AddTransient<StartupPasswordViewModel>();
|
|
||||||
services.AddTransient<StartupPasswordWindow>();
|
|
||||||
services.AddSingleton<MainViewModel>();
|
services.AddSingleton<MainViewModel>();
|
||||||
services.AddSingleton<MainWindow>();
|
services.AddSingleton<MainWindow>();
|
||||||
|
|
||||||
_serviceProvider = services.BuildServiceProvider();
|
_serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
var passwordAccessService = _serviceProvider.GetRequiredService<IPasswordAccessService>();
|
|
||||||
var passwordStatus = passwordAccessService.GetStatus();
|
|
||||||
if (passwordStatus.ShouldPromptAtStartup)
|
|
||||||
{
|
|
||||||
var passwordWindow = _serviceProvider.GetRequiredService<StartupPasswordWindow>();
|
|
||||||
var passwordResult = passwordWindow.ShowDialog();
|
|
||||||
if (passwordResult != true || !passwordAccessService.GetStatus().CanLaunch)
|
|
||||||
{
|
|
||||||
Shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
||||||
mainWindow.Show();
|
mainWindow.Show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace Cardiopulmonarybypasssystems.Models;
|
|
||||||
|
|
||||||
public sealed class PasswordAccessStatus
|
|
||||||
{
|
|
||||||
public int Stage { get; init; }
|
|
||||||
public bool CanLaunch { get; init; }
|
|
||||||
public bool IsPermanent { get; init; }
|
|
||||||
public bool ShouldPromptAtStartup { get; init; }
|
|
||||||
public bool CanSubmitPassword { get; init; }
|
|
||||||
public bool RequiresPassword => !CanLaunch;
|
|
||||||
public string StageText { get; init; } = string.Empty;
|
|
||||||
public string StatusTitle { get; init; } = string.Empty;
|
|
||||||
public string StatusDetail { get; init; } = string.Empty;
|
|
||||||
public int RemainingDays { get; init; }
|
|
||||||
public DateTime? ExpiresAt { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Cardiopulmonarybypasssystems.Models;
|
|
||||||
|
|
||||||
namespace Cardiopulmonarybypasssystems.Services;
|
|
||||||
|
|
||||||
public interface IPasswordAccessService
|
|
||||||
{
|
|
||||||
PasswordAccessStatus GetStatus();
|
|
||||||
bool TryUnlock(string password, out PasswordAccessStatus status, out string message);
|
|
||||||
}
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using System.Text.Json;
|
|
||||||
using Cardiopulmonarybypasssystems.Models;
|
|
||||||
|
|
||||||
namespace Cardiopulmonarybypasssystems.Services;
|
|
||||||
|
|
||||||
public sealed class PasswordAccessService : IPasswordAccessService
|
|
||||||
{
|
|
||||||
private const int ExpirationReminderDays = 3;
|
|
||||||
private const string SettingsDirectoryName = "Cardiopulmonarybypasssystems";
|
|
||||||
private const string StatusFileName = "password-access.json";
|
|
||||||
public const string FirstStagePassword = "152026001";
|
|
||||||
public const string SecondStagePassword = "302026002";
|
|
||||||
private const int InitialAccessDays = 20;
|
|
||||||
private const int ExtendedAccessDays = 30;
|
|
||||||
|
|
||||||
private static readonly string StatusFilePath = Path.Combine(
|
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
||||||
SettingsDirectoryName,
|
|
||||||
StatusFileName);
|
|
||||||
|
|
||||||
public PasswordAccessStatus GetStatus()
|
|
||||||
{
|
|
||||||
var state = LoadOrCreateState();
|
|
||||||
return BuildStatus(state, persistState: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryUnlock(string password, out PasswordAccessStatus status, out string message)
|
|
||||||
{
|
|
||||||
var normalizedPassword = password?.Trim() ?? string.Empty;
|
|
||||||
var state = LoadOrCreateState();
|
|
||||||
var currentStatus = BuildStatus(state, persistState: true);
|
|
||||||
|
|
||||||
if (currentStatus.IsPermanent)
|
|
||||||
{
|
|
||||||
status = currentStatus;
|
|
||||||
message = "当前版本已永久有效,不需要再输入密码。";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentStatus.CanLaunch)
|
|
||||||
{
|
|
||||||
status = currentStatus;
|
|
||||||
message = currentStatus.ShouldPromptAtStartup
|
|
||||||
? $"当前仍在有效期内,可直接进入系统;满 {GetStageDays(currentStatus.Stage)} 天后再输入密码。"
|
|
||||||
: $"{GetStageName(currentStatus.Stage)}仍在有效期内,无需再次输入密码。";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedPassword = currentStatus.Stage switch
|
|
||||||
{
|
|
||||||
0 => FirstStagePassword,
|
|
||||||
1 => SecondStagePassword,
|
|
||||||
_ => string.Empty
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!string.Equals(normalizedPassword, expectedPassword, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
status = currentStatus;
|
|
||||||
message = currentStatus.Stage switch
|
|
||||||
{
|
|
||||||
0 => "密码错误,请输入第 1 次时效密码。",
|
|
||||||
1 => "密码错误,请输入第 2 次时效密码。",
|
|
||||||
_ => "密码错误。"
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.Now;
|
|
||||||
|
|
||||||
if (currentStatus.Stage == 1)
|
|
||||||
{
|
|
||||||
var permanentState = new PasswordAccessState
|
|
||||||
{
|
|
||||||
Stage = 2,
|
|
||||||
ActivatedAt = state?.ActivatedAt ?? now,
|
|
||||||
ExpiresAt = null
|
|
||||||
};
|
|
||||||
|
|
||||||
SaveState(permanentState);
|
|
||||||
status = BuildStatus(permanentState, persistState: false);
|
|
||||||
message = "第 2 次密码验证通过,系统已永久有效。";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var expiresAt = now.AddDays(ExtendedAccessDays);
|
|
||||||
var nextState = new PasswordAccessState
|
|
||||||
{
|
|
||||||
Stage = 1,
|
|
||||||
ActivatedAt = now,
|
|
||||||
ExpiresAt = expiresAt
|
|
||||||
};
|
|
||||||
|
|
||||||
SaveState(nextState);
|
|
||||||
status = BuildStatus(nextState, persistState: false);
|
|
||||||
message = $"第 1 次密码验证通过,已继续开放 {ExtendedAccessDays} 天,有效期至 {expiresAt:yyyy-MM-dd HH:mm}。";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PasswordAccessStatus BuildStatus(PasswordAccessState? state, bool persistState)
|
|
||||||
{
|
|
||||||
if (state is null)
|
|
||||||
{
|
|
||||||
state = CreateInitialState();
|
|
||||||
if (persistState)
|
|
||||||
{
|
|
||||||
SaveState(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.Stage >= 2)
|
|
||||||
{
|
|
||||||
return new PasswordAccessStatus
|
|
||||||
{
|
|
||||||
Stage = 2,
|
|
||||||
CanLaunch = true,
|
|
||||||
IsPermanent = true,
|
|
||||||
ShouldPromptAtStartup = false,
|
|
||||||
CanSubmitPassword = false,
|
|
||||||
StageText = "当前阶段:永久有效",
|
|
||||||
StatusTitle = "系统已永久有效",
|
|
||||||
StatusDetail = "当前可直接进入系统。",
|
|
||||||
RemainingDays = int.MaxValue
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.Now;
|
|
||||||
if (state.ExpiresAt > now)
|
|
||||||
{
|
|
||||||
var remainingDays = Math.Max(1, (int)Math.Ceiling((state.ExpiresAt.Value - now).TotalDays));
|
|
||||||
var isReminderWindow = remainingDays <= ExpirationReminderDays;
|
|
||||||
var isInitialStage = state.Stage == 0;
|
|
||||||
return new PasswordAccessStatus
|
|
||||||
{
|
|
||||||
Stage = state.Stage,
|
|
||||||
CanLaunch = true,
|
|
||||||
IsPermanent = false,
|
|
||||||
ShouldPromptAtStartup = isReminderWindow,
|
|
||||||
CanSubmitPassword = false,
|
|
||||||
StageText = isInitialStage ? $"当前阶段:首次使用({InitialAccessDays} 天)" : $"当前阶段:第 1 次续期({ExtendedAccessDays} 天)",
|
|
||||||
StatusTitle = isReminderWindow
|
|
||||||
? isInitialStage ? "首次使用即将到期" : "第 1 次续期即将到期"
|
|
||||||
: "时效有效",
|
|
||||||
StatusDetail = isReminderWindow
|
|
||||||
? isInitialStage
|
|
||||||
? $"剩余 {remainingDays} 天,到期后需输入第 1 次时效密码。"
|
|
||||||
: $"剩余 {remainingDays} 天,到期后需输入第 2 次时效密码。"
|
|
||||||
: $"当前可直接进入系统。",
|
|
||||||
RemainingDays = remainingDays,
|
|
||||||
ExpiresAt = state.ExpiresAt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.Stage == 0)
|
|
||||||
{
|
|
||||||
return new PasswordAccessStatus
|
|
||||||
{
|
|
||||||
Stage = 0,
|
|
||||||
CanLaunch = false,
|
|
||||||
IsPermanent = false,
|
|
||||||
ShouldPromptAtStartup = true,
|
|
||||||
CanSubmitPassword = true,
|
|
||||||
StageText = "当前阶段:首次使用已到期",
|
|
||||||
StatusTitle = "需要输入第 1 次时效密码",
|
|
||||||
StatusDetail = $"输入密码后继续使用 {ExtendedAccessDays} 天。",
|
|
||||||
RemainingDays = 0,
|
|
||||||
ExpiresAt = state.ExpiresAt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PasswordAccessStatus
|
|
||||||
{
|
|
||||||
Stage = 1,
|
|
||||||
CanLaunch = false,
|
|
||||||
IsPermanent = false,
|
|
||||||
ShouldPromptAtStartup = true,
|
|
||||||
CanSubmitPassword = true,
|
|
||||||
StageText = "当前阶段:第 1 次续期已到期",
|
|
||||||
StatusTitle = "需要输入第 2 次时效密码",
|
|
||||||
StatusDetail = "输入密码后系统转为永久有效。",
|
|
||||||
RemainingDays = 0,
|
|
||||||
ExpiresAt = state.ExpiresAt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetStageDays(int stage) => stage switch
|
|
||||||
{
|
|
||||||
0 => InitialAccessDays,
|
|
||||||
1 => ExtendedAccessDays,
|
|
||||||
_ => 0
|
|
||||||
};
|
|
||||||
|
|
||||||
private static string GetStageName(int stage) => stage switch
|
|
||||||
{
|
|
||||||
0 => "首次使用阶段",
|
|
||||||
1 => "第 1 次续期阶段",
|
|
||||||
_ => "当前阶段"
|
|
||||||
};
|
|
||||||
|
|
||||||
private static PasswordAccessState LoadOrCreateState()
|
|
||||||
{
|
|
||||||
var state = LoadState();
|
|
||||||
if (state is not null)
|
|
||||||
{
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
var initialState = CreateInitialState();
|
|
||||||
SaveState(initialState);
|
|
||||||
return initialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PasswordAccessState CreateInitialState()
|
|
||||||
{
|
|
||||||
var now = DateTime.Now;
|
|
||||||
return new PasswordAccessState
|
|
||||||
{
|
|
||||||
Stage = 0,
|
|
||||||
ActivatedAt = now,
|
|
||||||
ExpiresAt = now.AddDays(InitialAccessDays)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PasswordAccessState? LoadState()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!File.Exists(StatusFilePath))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = File.ReadAllText(StatusFilePath);
|
|
||||||
return JsonSerializer.Deserialize<PasswordAccessState>(json);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SaveState(PasswordAccessState state)
|
|
||||||
{
|
|
||||||
var directory = Path.GetDirectoryName(StatusFilePath);
|
|
||||||
if (!string.IsNullOrWhiteSpace(directory))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(state, new JsonSerializerOptions { WriteIndented = true });
|
|
||||||
File.WriteAllText(StatusFilePath, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class PasswordAccessState
|
|
||||||
{
|
|
||||||
public int Stage { get; set; }
|
|
||||||
public DateTime ActivatedAt { get; set; }
|
|
||||||
public DateTime? ExpiresAt { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
<Window x:Class="Cardiopulmonarybypasssystems.StartupPasswordWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
Title="系统进入验证"
|
|
||||||
Width="640"
|
|
||||||
Height="500"
|
|
||||||
Loaded="Window_Loaded"
|
|
||||||
WindowStartupLocation="CenterScreen"
|
|
||||||
ResizeMode="NoResize">
|
|
||||||
<Grid Margin="18">
|
|
||||||
<Border CornerRadius="22"
|
|
||||||
Background="#FFF7FAFB"
|
|
||||||
BorderBrush="#FFD7E2E6"
|
|
||||||
BorderThickness="1">
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="126" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Border Grid.Row="0"
|
|
||||||
CornerRadius="22,22,0,0"
|
|
||||||
Padding="24,20">
|
|
||||||
<Border.Background>
|
|
||||||
<LinearGradientBrush StartPoint="0,0"
|
|
||||||
EndPoint="1,1">
|
|
||||||
<GradientStop Color="#FF123744"
|
|
||||||
Offset="0" />
|
|
||||||
<GradientStop Color="#FF0F5F75"
|
|
||||||
Offset="0.55" />
|
|
||||||
<GradientStop Color="#FF2F7D7A"
|
|
||||||
Offset="1" />
|
|
||||||
</LinearGradientBrush>
|
|
||||||
</Border.Background>
|
|
||||||
<StackPanel VerticalAlignment="Center">
|
|
||||||
<TextBlock FontSize="26"
|
|
||||||
FontWeight="Bold"
|
|
||||||
Foreground="White"
|
|
||||||
Text="系统进入验证" />
|
|
||||||
<Border Margin="0,10,0,0"
|
|
||||||
Padding="10,6"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
Background="#1FFFFFFF"
|
|
||||||
CornerRadius="12">
|
|
||||||
<TextBlock FontSize="12"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="White"
|
|
||||||
Text="{Binding StageText}" />
|
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<StackPanel Grid.Row="1"
|
|
||||||
Margin="24,16,24,14">
|
|
||||||
<Border
|
|
||||||
Padding="14,10"
|
|
||||||
Background="#FFEAF3F5"
|
|
||||||
CornerRadius="14">
|
|
||||||
<DockPanel LastChildFill="False">
|
|
||||||
<StackPanel DockPanel.Dock="Left"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<TextBlock FontSize="12"
|
|
||||||
Foreground="#FF52636B"
|
|
||||||
Text="到期:" />
|
|
||||||
<TextBlock FontSize="13"
|
|
||||||
FontWeight="Bold"
|
|
||||||
Foreground="{StaticResource HeaderBrush}"
|
|
||||||
Text="{Binding ExpiresAtText}" />
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel DockPanel.Dock="Right"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<TextBlock FontSize="12"
|
|
||||||
Foreground="#FF52636B"
|
|
||||||
Text="剩余:" />
|
|
||||||
<TextBlock FontSize="13"
|
|
||||||
FontWeight="Bold"
|
|
||||||
Foreground="{StaticResource HeaderBrush}"
|
|
||||||
Text="{Binding RemainingText}" />
|
|
||||||
</StackPanel>
|
|
||||||
</DockPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<Border Margin="0,16,0,0"
|
|
||||||
Padding="16"
|
|
||||||
Background="White"
|
|
||||||
BorderBrush="#FFDCE7EA"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="16">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBlock FontSize="12"
|
|
||||||
FontWeight="SemiBold"
|
|
||||||
Foreground="{StaticResource HeaderBrush}"
|
|
||||||
Text="密码输入" />
|
|
||||||
<TextBox x:Name="PasswordInput"
|
|
||||||
Margin="0,10,0,0"
|
|
||||||
MinHeight="38"
|
|
||||||
FontSize="15"
|
|
||||||
Text="{Binding PasswordText, UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
TextChanged="PasswordInput_OnTextChanged" />
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Border Grid.Row="2"
|
|
||||||
Padding="18,14"
|
|
||||||
Background="White"
|
|
||||||
BorderBrush="#FFE2EBEE"
|
|
||||||
BorderThickness="1,1,0,0"
|
|
||||||
CornerRadius="0,0,22,22">
|
|
||||||
<UniformGrid Columns="3">
|
|
||||||
<Button Width="112"
|
|
||||||
Margin="0,0,10,0"
|
|
||||||
Command="{Binding CloseCommand}"
|
|
||||||
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
|
|
||||||
Background="#FF667E88"
|
|
||||||
Content="退出" />
|
|
||||||
<Button Width="112"
|
|
||||||
Margin="0,0,10,0"
|
|
||||||
Command="{Binding ContinueCommand}"
|
|
||||||
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
|
|
||||||
Background="#FF2B8F6A"
|
|
||||||
Content="继续进入"
|
|
||||||
IsEnabled="{Binding CanContinue}" />
|
|
||||||
<Button Width="112"
|
|
||||||
Margin="0,0,10,0"
|
|
||||||
Command="{Binding SubmitCommand}"
|
|
||||||
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
|
|
||||||
Background="#FF0F5F75"
|
|
||||||
Content="提交密码"
|
|
||||||
IsEnabled="{Binding CanSubmit}" />
|
|
||||||
</UniformGrid>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using System.Windows;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using Cardiopulmonarybypasssystems.ViewModels;
|
|
||||||
|
|
||||||
namespace Cardiopulmonarybypasssystems;
|
|
||||||
|
|
||||||
public partial class StartupPasswordWindow : Window
|
|
||||||
{
|
|
||||||
private readonly StartupPasswordViewModel _viewModel;
|
|
||||||
|
|
||||||
public StartupPasswordWindow(StartupPasswordViewModel viewModel)
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
_viewModel = viewModel;
|
|
||||||
DataContext = viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
PasswordInput.Focus();
|
|
||||||
Keyboard.Focus(PasswordInput);
|
|
||||||
PasswordInput.SelectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PasswordInput_OnTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
|
||||||
{
|
|
||||||
_viewModel.PasswordText = PasswordInput.Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Windows;
|
|
||||||
using Cardiopulmonarybypasssystems.Models;
|
|
||||||
using Cardiopulmonarybypasssystems.Services;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
|
|
||||||
namespace Cardiopulmonarybypasssystems.ViewModels;
|
|
||||||
|
|
||||||
public partial class StartupPasswordViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
private readonly IPasswordAccessService _passwordAccessService;
|
|
||||||
|
|
||||||
public StartupPasswordViewModel(IPasswordAccessService passwordAccessService)
|
|
||||||
{
|
|
||||||
_passwordAccessService = passwordAccessService;
|
|
||||||
ProductName = "心肺转流检测系统";
|
|
||||||
SoftwareVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "1.0.0";
|
|
||||||
RefreshStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string productName = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string softwareVersion = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string passwordText = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string statusTitle = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string statusDetail = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string stageText = string.Empty;
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string expiresAtText = "--";
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string remainingText = "--";
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private string footerHint = "规则:先使用 20 天,提前 3 天提醒;满 20 天输入密码后再使用 30 天,提前 3 天再次提醒。";
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool canSubmit;
|
|
||||||
|
|
||||||
public bool CanContinue => _passwordAccessService.GetStatus().CanLaunch;
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void Submit(Window? window)
|
|
||||||
{
|
|
||||||
if (!_passwordAccessService.TryUnlock(PasswordText, out _, out var message))
|
|
||||||
{
|
|
||||||
RefreshStatus();
|
|
||||||
StatusDetail = message;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PasswordText = string.Empty;
|
|
||||||
RefreshStatus();
|
|
||||||
StatusDetail = message;
|
|
||||||
|
|
||||||
if (_passwordAccessService.GetStatus().CanLaunch && window is not null)
|
|
||||||
{
|
|
||||||
window.DialogResult = true;
|
|
||||||
window.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void Continue(Window? window)
|
|
||||||
{
|
|
||||||
if (!_passwordAccessService.GetStatus().CanLaunch || window is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.DialogResult = true;
|
|
||||||
window.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void Close(Window? window)
|
|
||||||
{
|
|
||||||
window?.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void AppendPasswordCharacter(string? value)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PasswordText += value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void BackspacePassword()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(PasswordText))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PasswordText = PasswordText[..^1];
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void ClearPassword()
|
|
||||||
{
|
|
||||||
PasswordText = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshStatus()
|
|
||||||
{
|
|
||||||
var status = _passwordAccessService.GetStatus();
|
|
||||||
StatusTitle = status.StatusTitle;
|
|
||||||
StatusDetail = status.StatusDetail;
|
|
||||||
StageText = status.StageText;
|
|
||||||
ExpiresAtText = status.ExpiresAt?.ToString("yyyy-MM-dd HH:mm") ?? (status.IsPermanent ? "永久有效" : "--");
|
|
||||||
RemainingText = status.IsPermanent ? "永久" : status.RemainingDays > 0 ? $"{status.RemainingDays} 天" : "需输入密码";
|
|
||||||
CanSubmit = status.CanSubmitPassword;
|
|
||||||
OnPropertyChanged(nameof(CanContinue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user