LoginViewModel.cs
4.0 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Net.Http.Json;
using System.Text.Json;
namespace IndustrialControl.ViewModels;
public partial class LoginViewModel : ObservableObject
{
private readonly IConfigLoader _cfg;
[ObservableProperty] private string userName = string.Empty;
[ObservableProperty] private string password = string.Empty;
[ObservableProperty] private bool isBusy;
[ObservableProperty] private bool showPassword; // false=默认隐藏
private static readonly JsonSerializerOptions _json = new() { PropertyNameCaseInsensitive = true };
public LoginViewModel(IConfigLoader cfg)
{
_cfg = cfg;
}
[RelayCommand]
public async Task LoginAsync()
{
if (IsBusy) return;
IsBusy = true;
try
{
// 读取配置(App 启动时如果已 EnsureLatestAsync,这里 Load() 就够了)
var cfg = _cfg.Load();
var scheme = cfg["server"]?["scheme"]?.GetValue<string>() ?? "http";
var host = cfg["server"]?["ipAddress"]?.GetValue<string>() ?? "127.0.0.1";
var port = cfg["server"]?["port"]?.GetValue<int?>();
var path = cfg["apiEndpoints"]?["login"]?.GetValue<string>() ?? "/normalService/pda/auth/login";
if (!path.StartsWith("/")) path = "/" + path;
var baseUrl = port is > 0 and < 65536 ? $"{scheme}://{host}:{port}" : $"{scheme}://{host}";
var fullUrl = new Uri(baseUrl + path);
if (string.IsNullOrWhiteSpace(UserName) || string.IsNullOrWhiteSpace(Password))
{
await Application.Current.MainPage.DisplayAlert("提示", "请输入用户名和密码", "确定");
return;
}
var payload = new { username = UserName, password = Password };
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
// 登录接口不需要带 Bearer;我们仍用 ApiClient 调
var resp = await ApiClient.Instance.PostAsJsonAsync(fullUrl, payload, cts.Token);
var raw = await resp.Content.ReadAsStringAsync(cts.Token);
if (!resp.IsSuccessStatusCode)
{
await Application.Current.MainPage.DisplayAlert("登录失败", $"HTTP {(int)resp.StatusCode}: {raw}", "确定");
return;
}
var result = JsonSerializer.Deserialize<ApiResponse<LoginResult>>(raw,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var ok = (result?.success == true) || (result?.code is 0 or 200);
var token = result?.result?.token;
if (!ok || string.IsNullOrWhiteSpace(token))
{
await Application.Current.MainPage.DisplayAlert("登录失败", result?.message ?? "登录返回无效", "确定");
return;
}
// ★ 只需保存;后续所有经 DI 的 HttpClient 都会由 AuthHeaderHandler 自动加 Authorization 头
await TokenStorage.SaveAsync(token!);
System.Diagnostics.Debug.WriteLine("saved token len=" + token?.Length);
// 进入主壳
App.SwitchToLoggedInShell();
}
catch (OperationCanceledException)
{
await Application.Current.MainPage.DisplayAlert("超时", "登录请求超时,请检查网络", "确定");
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("异常", ex.Message, "确定");
}
finally
{
IsBusy = false;
}
}
[RelayCommand]
private void TogglePassword() => ShowPassword = !ShowPassword;
private sealed class ApiResponse<T>
{
public int code { get; set; }
public bool success { get; set; }
public string? message { get; set; }
public T? result { get; set; }
}
private sealed class LoginResult
{
public string? token { get; set; }
public object? userInfo { get; set; }
}
}