404 lines
12 KiB
C#
404 lines
12 KiB
C#
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();
|
||
}
|
||
}
|
||
}
|
||
}
|