LoginViewModel.cs 4.0 KB
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
        {
            // 启动时已 EnsureLatestAsync,这里只读取 AppData 中已生效配置
            var cfg = _cfg.Load();

            var host = cfg["server"]?["ipAddress"]?.GetValue<string>() ?? "127.0.0.1";
            var port = cfg["server"]?["port"]?.GetValue<int?>() ?? 80;

            var path = cfg["apiEndpoints"]?["login"]?.GetValue<string>() ?? "normalService/pda/auth/login";
            if (!path.StartsWith("/")) path = "/" + path;

            // 直接拼绝对 URL,避免修改 HttpClient.BaseAddress 引发异常
            var fullUrl = new Uri($"http://{host}:{port}{path}");
            System.Diagnostics.Debug.WriteLine($"[API] {fullUrl}");

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

            var resp = await ApiClient.Instance.PostAsJsonAsync(fullUrl, payload, cts.Token);

            if (!resp.IsSuccessStatusCode)
            {
                var raw = await resp.Content.ReadAsStringAsync(cts.Token);
                await Application.Current.MainPage.DisplayAlert("登录失败",
                    $"HTTP {(int)resp.StatusCode}: {raw}", "确定");
                return;
            }

            var result = await resp.Content.ReadFromJsonAsync<ApiResponse<LoginResult>>(_json, cts.Token);
            bool ok = (result?.success == true) || (result?.code is 0 or 200);
            var token = result?.result?.token;

            if (ok && !string.IsNullOrWhiteSpace(token))
            {
                await TokenStorage.SaveAsync(token!);
                ApiClient.SetBearer(token);
                if (ok && !string.IsNullOrWhiteSpace(token))
                {
                    await TokenStorage.SaveAsync(token);
                    ApiClient.SetBearer(token);

                    App.SwitchToLoggedInShell();
                }

            }
            else
            {
                await Application.Current.MainPage.DisplayAlert("登录失败", result?.message ?? "登录返回无效", "确定");
            }
        }
        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; }
    }
}