150 lines
4.2 KiB
C#
150 lines
4.2 KiB
C#
using Prism.Commands;
|
|
using Prism.Services.Dialogs;
|
|
using Store.Management.Api.Transport;
|
|
using Store.Management.Framework.Controls;
|
|
using Store.Management.Framework.Services;
|
|
using Store.Management.Framework.ViewModels;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Windows.Input;
|
|
|
|
namespace Store.Management.ViewModels;
|
|
|
|
public enum EEE
|
|
{
|
|
[Description("管理员")]
|
|
Admin,
|
|
[Description("用户")]
|
|
User
|
|
}
|
|
/// <summary>登录校验、认证请求和窗口生命周期内的请求取消。</summary>
|
|
public sealed class LoginViewModel(ILoginService loginService) : ConfirmDialogViewModel, IDisposable
|
|
{
|
|
private EEE _ee;
|
|
public EEE ee
|
|
{
|
|
get => _ee;
|
|
set => SetProperty(ref _ee, value);
|
|
}
|
|
private readonly CancellationTokenSource _lifetime = new();
|
|
private string _userName = "";
|
|
private string _passwordHash = "";
|
|
private bool _disposed;
|
|
private bool _authenticated;
|
|
|
|
public const int InputMaxLength = 250;
|
|
public LoadingStatus LoadingStatus { get; } = new();
|
|
public string UserName { get => _userName; set => SetProperty(ref _userName, value); }
|
|
public bool CanEdit => !LoadingStatus.IsLoading && !_disposed && !_authenticated;
|
|
public string LoginButtonText => LoadingStatus.IsLoading ? "正在登录…" : "登录";
|
|
|
|
public event EventHandler? ClearPasswordRequested;
|
|
|
|
/// <summary>仅由密码控件桥接调用,不保存明文密码。</summary>
|
|
public void UpdatePassword(string password)
|
|
{
|
|
if (!CanEdit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 服务端要求 UTF-8 密码的 32 位大写 MD5,保留首尾空格。
|
|
_passwordHash = string.IsNullOrEmpty(password)
|
|
? "" : Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(password)));
|
|
}
|
|
|
|
public override ICommand ConfirmCommand => LoginCommand;
|
|
|
|
public override void OnDialogOpened(IDialogParameters parameters)
|
|
{
|
|
Title = "门店管理 · 登录";
|
|
}
|
|
|
|
public override void OnDialogClosed() => Dispose();
|
|
|
|
public AsyncDelegateCommand LoginCommand => field ??= new AsyncDelegateCommand(LoginAsync, () => CanEdit);
|
|
|
|
private async Task LoginAsync()
|
|
{
|
|
if (!CanEdit)
|
|
{
|
|
return;
|
|
}
|
|
if (string.IsNullOrWhiteSpace(UserName))
|
|
{
|
|
LoadingStatus.Error("请输入用户名。");
|
|
return;
|
|
}
|
|
if (string.IsNullOrEmpty(_passwordHash))
|
|
{
|
|
LoadingStatus.Error("请输入密码。");
|
|
return;
|
|
}
|
|
|
|
LoadingStatus.Initialize("正在验证账号,请稍候…");
|
|
NotifyStateChanged();
|
|
try
|
|
{
|
|
await loginService.LoginAsync(UserName, _passwordHash, _lifetime.Token);
|
|
if (!_disposed)
|
|
{
|
|
_authenticated = true;
|
|
OnRequestClose(new DialogResult(ButtonResult.OK));
|
|
}
|
|
}
|
|
catch (OperationCanceledException) when (_disposed)
|
|
{
|
|
// 关闭窗口产生的取消无需提示。
|
|
}
|
|
catch (ApiException ex)
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
LoadingStatus.Error(ex.Message);
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
LoadingStatus.Error("登录已取消,请重试。");
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
LoadingStatus.Error("登录失败,请稍后重试。");
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_passwordHash = "";
|
|
if (!_disposed)
|
|
{
|
|
LoadingStatus.Done();
|
|
ClearPasswordRequested?.Invoke(this, EventArgs.Empty);
|
|
NotifyStateChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void NotifyStateChanged()
|
|
{
|
|
RaisePropertyChanged(nameof(CanEdit));
|
|
RaisePropertyChanged(nameof(LoginButtonText));
|
|
LoginCommand.RaiseCanExecuteChanged();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
{
|
|
return;
|
|
}
|
|
_disposed = true;
|
|
_passwordHash = "";
|
|
_lifetime.Cancel();
|
|
_lifetime.Dispose();
|
|
}
|
|
}
|