WorkCamera/client/WorkCameraExport/Forms/LoginForm.cs
2026-01-06 00:42:25 +08:00

404 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using WorkCameraExport.Models;
using WorkCameraExport.Services;
using WorkCameraExport.Services.Interfaces;
namespace WorkCameraExport.Forms
{
/// <summary>
/// 登录窗体
/// </summary>
public partial class LoginForm : Form
{
private readonly ApiService _apiService;
private readonly IConfigService? _configService;
private readonly AppSettings _settings;
// 验证码相关
private string _captchaUuid = "";
private bool _captchaRequired = true;
/// <summary>
/// 登录成功后的 Token
/// </summary>
public string? Token { get; private set; }
/// <summary>
/// 登录成功后的用户信息
/// </summary>
public UserInfo? UserInfo { get; private set; }
/// <summary>
/// 是否为自动登录成功
/// </summary>
public bool IsAutoLogin { get; private set; }
public LoginForm(ApiService apiService, IConfigService? configService = null)
{
InitializeComponent();
_apiService = apiService;
_configService = configService;
_settings = AppSettings.Load();
LoadSettings();
}
/// <summary>
/// 加载保存的设置
/// </summary>
private void LoadSettings()
{
txtServerUrl.Text = _settings.ServerUrl;
txtUsername.Text = _settings.Username;
chkRememberMe.Checked = _settings.RememberMe;
// 如果记住登录且 Token 未过期,尝试自动登录
if (_settings.RememberMe &&
!string.IsNullOrEmpty(_settings.SavedToken) &&
_settings.TokenExpireTime.HasValue &&
_settings.TokenExpireTime.Value > DateTime.Now)
{
TryAutoLogin();
}
}
/// <summary>
/// 尝试自动登录
/// </summary>
private async void TryAutoLogin()
{
if (string.IsNullOrEmpty(_settings.ServerUrl) ||
string.IsNullOrEmpty(_settings.SavedToken))
{
return;
}
SetControlsEnabled(false);
lblStatus.Text = "正在自动登录...";
lblStatus.ForeColor = Color.Blue;
try
{
_apiService.SetBaseUrl(_settings.ServerUrl);
_apiService.SetToken(_settings.SavedToken);
// 验证 Token 是否有效(通过获取当前用户信息)
var (success, _, _) = await _apiService.GetCurrentUserAsync();
if (success)
{
Token = _settings.SavedToken;
IsAutoLogin = true;
DialogResult = DialogResult.OK;
Close();
return;
}
// Token 无效,清除保存的登录信息并提示用户
_settings.ClearLoginInfo();
_configService?.ClearCredentials();
lblStatus.Text = "登录已失效,请重新登录";
lblStatus.ForeColor = Color.Orange;
}
catch
{
_settings.ClearLoginInfo();
_configService?.ClearCredentials();
lblStatus.Text = "自动登录失败,请重新登录";
lblStatus.ForeColor = Color.Red;
}
finally
{
SetControlsEnabled(true);
// 加载验证码
await LoadCaptchaAsync();
}
}
/// <summary>
/// 服务器地址输入框失去焦点时加载验证码
/// </summary>
private async void txtServerUrl_Leave(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(txtServerUrl.Text))
{
await LoadCaptchaAsync();
}
}
/// <summary>
/// 加载验证码
/// </summary>
private async Task LoadCaptchaAsync()
{
if (string.IsNullOrWhiteSpace(txtServerUrl.Text))
{
return;
}
try
{
_apiService.SetBaseUrl(txtServerUrl.Text.Trim());
var (success, message, data) = await _apiService.GetCaptchaAsync();
if (success && data != null)
{
_captchaUuid = data.Uuid;
_captchaRequired = data.CaptchaOff?.ToLower() != "off";
// 显示或隐藏验证码控件
SetCaptchaVisible(_captchaRequired);
if (_captchaRequired && !string.IsNullOrEmpty(data.Img))
{
// 将 Base64 转换为图片
var imageBytes = Convert.FromBase64String(data.Img);
using var ms = new MemoryStream(imageBytes);
picCaptcha.Image?.Dispose();
picCaptcha.Image = Image.FromStream(ms);
}
lblStatus.Text = "";
}
else
{
lblStatus.Text = "获取验证码失败,点击图片重试";
lblStatus.ForeColor = Color.Orange;
}
}
catch (Exception ex)
{
lblStatus.Text = $"连接服务器失败: {ex.Message}";
lblStatus.ForeColor = Color.Red;
}
}
/// <summary>
/// 设置验证码控件可见性
/// </summary>
private void SetCaptchaVisible(bool visible)
{
lblCaptcha.Visible = visible;
txtCaptcha.Visible = visible;
picCaptcha.Visible = visible;
// 调整其他控件位置
if (visible)
{
chkRememberMe.Location = new Point(30, 265);
btnLogin.Location = new Point(30, 300);
btnCancel.Location = new Point(210, 300);
lblStatus.Location = new Point(30, 355);
this.ClientSize = new Size(400, 450);
}
else
{
chkRememberMe.Location = new Point(30, 200);
btnLogin.Location = new Point(30, 240);
btnCancel.Location = new Point(210, 240);
lblStatus.Location = new Point(30, 295);
this.ClientSize = new Size(400, 390);
}
}
/// <summary>
/// 点击验证码图片刷新
/// </summary>
private async void picCaptcha_Click(object sender, EventArgs e)
{
txtCaptcha.Text = "";
await LoadCaptchaAsync();
}
/// <summary>
/// 登录按钮点击事件
/// </summary>
private async void btnLogin_Click(object sender, EventArgs e)
{
// 验证输入
if (string.IsNullOrWhiteSpace(txtServerUrl.Text))
{
ShowError("请输入服务器地址");
txtServerUrl.Focus();
return;
}
if (string.IsNullOrWhiteSpace(txtUsername.Text))
{
ShowError("请输入用户名");
txtUsername.Focus();
return;
}
if (string.IsNullOrWhiteSpace(txtPassword.Text))
{
ShowError("请输入密码");
txtPassword.Focus();
return;
}
if (_captchaRequired && string.IsNullOrWhiteSpace(txtCaptcha.Text))
{
ShowError("请输入验证码");
txtCaptcha.Focus();
return;
}
await DoLoginAsync();
}
/// <summary>
/// 执行登录
/// </summary>
private async Task DoLoginAsync()
{
SetControlsEnabled(false);
lblStatus.Text = "正在登录...";
lblStatus.ForeColor = Color.Blue;
try
{
var serverUrl = txtServerUrl.Text.Trim();
var username = txtUsername.Text.Trim();
var password = txtPassword.Text;
var captchaCode = txtCaptcha.Text.Trim();
_apiService.SetBaseUrl(serverUrl);
var (success, message, data) = await _apiService.LoginAsync(
username, password, captchaCode, _captchaUuid);
if (success && data != null)
{
Token = data.Token;
UserInfo = data.User;
// 保存设置
SaveSettings(serverUrl, username, data.Token);
lblStatus.Text = "登录成功";
lblStatus.ForeColor = Color.Green;
DialogResult = DialogResult.OK;
Close();
}
else
{
ShowError(message);
SetControlsEnabled(true);
// 登录失败后刷新验证码
if (_captchaRequired)
{
txtCaptcha.Text = "";
await LoadCaptchaAsync();
}
}
}
catch (Exception ex)
{
ShowError($"登录异常: {ex.Message}");
SetControlsEnabled(true);
}
}
/// <summary>
/// 保存设置
/// </summary>
private void SaveSettings(string serverUrl, string username, string token)
{
_settings.ServerUrl = serverUrl;
_settings.Username = username;
_settings.RememberMe = chkRememberMe.Checked;
if (chkRememberMe.Checked)
{
_settings.SavedToken = token;
// Token 有效期设为 7 天
_settings.TokenExpireTime = DateTime.Now.AddDays(7);
// 同时保存到 ConfigService加密存储
_configService?.SaveCredentials(serverUrl, token, username);
}
else
{
_settings.SavedToken = "";
_settings.TokenExpireTime = null;
_configService?.ClearCredentials();
}
_settings.Save();
}
/// <summary>
/// 显示错误信息
/// </summary>
private void ShowError(string message)
{
lblStatus.Text = message;
lblStatus.ForeColor = Color.Red;
}
/// <summary>
/// 设置控件启用状态
/// </summary>
private void SetControlsEnabled(bool enabled)
{
txtServerUrl.Enabled = enabled;
txtUsername.Enabled = enabled;
txtPassword.Enabled = enabled;
txtCaptcha.Enabled = enabled;
picCaptcha.Enabled = enabled;
chkRememberMe.Enabled = enabled;
btnLogin.Enabled = enabled;
btnCancel.Enabled = enabled;
}
/// <summary>
/// 取消按钮点击事件
/// </summary>
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
/// <summary>
/// 验证码输入框按键事件 - 支持回车登录
/// </summary>
private void txtCaptcha_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.SuppressKeyPress = true;
btnLogin_Click(sender, e);
}
}
/// <summary>
/// 窗体加载事件
/// </summary>
private async void LoginForm_Load(object sender, EventArgs e)
{
// 设置焦点
if (string.IsNullOrEmpty(txtServerUrl.Text))
{
txtServerUrl.Focus();
}
else if (string.IsNullOrEmpty(txtUsername.Text))
{
txtUsername.Focus();
}
else
{
txtPassword.Focus();
}
// 如果已有服务器地址,加载验证码
if (!string.IsNullOrWhiteSpace(txtServerUrl.Text))
{
await LoadCaptchaAsync();
}
}
}
}