修复手机号登录:改为状态机流程,解决下一步无响应

This commit is contained in:
meoacgx committed 2025-12-19 16:55:01 +08:00
1 parent 3af9535ec3
commit ec96545f10
1 file changed
+151 -148
@@ -12,15 +12,18 @@
<MudGrid>
<MudItem xs="12" md="6" Class="mx-auto">
<MudCard>
<MudCard>
<MudCardContent>
<MudStepper @ref="stepper" Color="Color.Primary">
<ChildContent>
<MudStep Title="API 配置">
<MudStack Spacing="2">
<MudText Typo="Typo.subtitle2">@StepTitle</MudText>
<MudDivider />
@if (currentStep == LoginStep.Phone)
{
@if (!IsApiConfigured)
{
<MudAlert Severity="Severity.Warning" Class="mb-3">
<MudText Typo="Typo.body2">未配置全局 Telegram API(ApiId/ApiHash),无法登录。</MudText>
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="true" Class="mb-2">
未配置全局 Telegram API(ApiId/ApiHash),无法登录。
</MudAlert>
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Href="/settings">
去系统设置配置
@@ -28,102 +31,89 @@
}
else
{
<MudAlert Severity="Severity.Info" Class="mb-3">
<MudText Typo="Typo.body2">使用全局 Telegram API:ApiId=@(Configuration["Telegram:ApiId"] ?? "(未配置)")</MudText>
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true" Class="mb-2">
使用全局 Telegram API:ApiId=@(Configuration["Telegram:ApiId"] ?? "(未配置)")
</MudAlert>
}
<MudTextField @bind-Value="phoneNumber" Label="手机号" Variant="Variant.Outlined" Required="true"
Placeholder="+86xxxxxxxxxx" Class="mt-3" />
<MudText Typo="Typo.caption" Class="mt-2" Color="Color.Info">
Placeholder="+86xxxxxxxxxx" Class="mt-2" />
<MudText Typo="Typo.caption" Class="mt-1" Color="Color.Info">
* 手机号需包含国家代码,例如:+8613800138000
</MudText>
</MudStep>
<MudStep Title="验证码" IsResultStep="false">
<MudText Typo="Typo.body1" Class="mb-3">
}
else if (currentStep == LoginStep.Code)
{
<MudText Typo="Typo.body1">
验证码已发送到 <strong>@phoneNumber</strong>
</MudText>
<MudTextField @bind-Value="verificationCode" Label="验证码" Variant="Variant.Outlined" Required="true"
Placeholder="12345" />
<MudText Typo="Typo.caption" Class="mt-2" Color="Color.Info">
Placeholder="12345" Class="mt-2" />
<MudText Typo="Typo.caption" Class="mt-1" Color="Color.Info">
说明:验证码通常优先发送到已登录设备的 Telegram「系统通知(777000)」;没有可用设备时才可能发送短信/电话。
</MudText>
@if (accountId == 0)
{
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="true" Class="mt-3">
当前并未成功发送验证码(可能是页面刚打开尚未连接、或你跳过了“发送验证码”步骤)。请回到上一步点击“发送验证码”。
</MudAlert>
}
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" Class="mt-3"
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" Class="mt-2"
Disabled="@(logging || accountId == 0)" OnClick="@(async (MouseEventArgs _) => await ResendCodeAsync())">
重新发送验证码(尝试切换通道)
</MudButton>
</MudStep>
<MudStep Title="密码验证" Optional="true">
<MudText Typo="Typo.body1" Class="mb-3">
}
else if (currentStep == LoginStep.Password)
{
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true">
此账号启用了两步验证,请输入密码
</MudText>
<MudTextField @bind-Value="password" Label="两步验证密码" Variant="Variant.Outlined"
InputType="InputType.Password" Required="true" />
</MudStep>
<MudStep Title="完成" IsResultStep="true">
<MudAlert Severity="Severity.Success" Class="mb-3">
<MudText Typo="Typo.body1">登录成功并保存到数据库!</MudText>
</MudAlert>
<MudText Typo="Typo.body2"><strong>手机号:</strong>@phoneNumber</MudText>
<MudTextField @bind-Value="password" Label="两步验证密码" Variant="Variant.Outlined"
InputType="InputType.Password" Required="true" Class="mt-2" />
}
else if (currentStep == LoginStep.Done)
{
<MudAlert Severity="Severity.Success" Variant="Variant.Outlined" Dense="true">
登录成功并保存到数据库!
</MudAlert>
<MudText Typo="Typo.body2" Class="mt-2"><strong>手机号:</strong>@phoneNumber</MudText>
<MudText Typo="Typo.body2"><strong>用户名:</strong>@(username ?? "无")</MudText>
<MudText Typo="Typo.body2"><strong>用户ID:</strong>@userId</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" Href="/accounts" Class="mt-4">
<MudButton Variant="Variant.Filled" Color="Color.Primary" Href="/accounts" Class="mt-3">
查看账号列表
</MudButton>
</MudStep>
</ChildContent>
<ActionContent>
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="w-100">
<MudButton Variant="Variant.Text" Color="Color.Primary"
OnClick="@(async (MouseEventArgs _) => await ResetStepperAsync())">
重置
</MudButton>
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
@if ((stepper?.ActiveIndex ?? 0) > 0 && !IsFinished)
{
<MudButton Variant="Variant.Text" Color="Color.Primary"
OnClick="@(async (MouseEventArgs _) => await PreviousStepAsync())">
上一步
</MudButton>
}
@if (!IsFinished)
{
<MudButton Variant="Variant.Filled" Color="Color.Primary"
Disabled="@(logging || !stepperReady)"
OnClick="@(async (MouseEventArgs _) => await NextStep())">
@if (logging)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
<span>处理中...</span>
}
else
{
<span>@NextButtonText</span>
}
</MudButton>
}
</MudStack>
</MudStack>
</ActionContent>
</MudStepper>
}
</MudStack>
</MudCardContent>
<MudCardActions>
<MudButton Variant="Variant.Text" Color="Color.Primary" Disabled="@logging"
OnClick="@(async (MouseEventArgs _) => await ResetAsync())">
重置
</MudButton>
<MudSpacer />
@if (currentStep != LoginStep.Phone && currentStep != LoginStep.Done)
{
<MudButton Variant="Variant.Text" Color="Color.Primary" Disabled="@logging"
OnClick="@(async (MouseEventArgs _) => await PreviousAsync())">
上一步
</MudButton>
}
@if (currentStep != LoginStep.Done)
{
<MudButton Variant="Variant.Filled" Color="Color.Primary"
Disabled="@logging"
OnClick="@(async (MouseEventArgs _) => await NextAsync())">
@if (logging)
{
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
<span>处理中...</span>
}
else
{
<span>@PrimaryButtonText</span>
}
</MudButton>
}
</MudCardActions>
</MudCard>
</MudItem>
</MudGrid>
@code {
private MudStepper? stepper;
private bool stepperReady;
private bool logging = false;
private int telegramApiId;
@@ -136,6 +126,7 @@
private string? username = "";
private long userId = 0;
private int accountId = 0;
private LoginStep currentStep = LoginStep.Phone;
private bool TryGetTelegramApi(out int apiId, out string apiHash, out string configuredSessionsPath)
{
@@ -148,59 +139,53 @@
private bool IsApiConfigured => TryGetTelegramApi(out _, out _, out _);
private bool IsFinished =>
stepper != null && stepper.ActiveIndex >= stepper.Steps.Count - 1;
private bool IsLoginActionStep =>
stepper != null && stepper.ActiveIndex == stepper.Steps.Count - 2;
protected override Task OnAfterRenderAsync(bool firstRender)
private string StepTitle => currentStep switch
{
// @ref 在首次渲染后才会赋值;需要触发一次刷新,否则按钮会一直保持“Disabled”,看起来像“点了没反应”
if (firstRender && !stepperReady && stepper != null)
{
stepperReady = true;
return InvokeAsync(StateHasChanged);
}
LoginStep.Phone => "步骤 1/3:输入手机号并发送验证码",
LoginStep.Code => "步骤 2/3:输入验证码",
LoginStep.Password => "步骤 3/3:输入两步验证密码",
LoginStep.Done => "完成",
_ => "手机号登录"
};
return Task.CompletedTask;
}
private string NextButtonText => stepper?.ActiveIndex switch
private string PrimaryButtonText => currentStep switch
{
0 => "发送验证码",
1 => "验证验证码",
2 => "验证密码",
LoginStep.Phone => "发送验证码",
LoginStep.Code => "验证验证码",
LoginStep.Password => "验证密码",
_ => "下一步"
};
private async Task ResetStepperAsync()
private Task ResetAsync()
{
if (stepper == null) return;
accountId = 0;
phoneNumber = "";
verificationCode = "";
password = "";
await stepper.ResetAsync(true);
username = "";
userId = 0;
currentStep = LoginStep.Phone;
return Task.CompletedTask;
}
private async Task PreviousStepAsync()
private Task PreviousAsync()
{
if (stepper == null) return;
await stepper.PreviousStepAsync();
currentStep = currentStep switch
{
LoginStep.Code => LoginStep.Phone,
LoginStep.Password => LoginStep.Code,
_ => currentStep
};
return Task.CompletedTask;
}
private async Task NextStep()
private async Task NextAsync()
{
if (stepper == null) return;
var currentStep = stepper.ActiveIndex;
logging = true;
try
{
if (currentStep == 0)
if (currentStep == LoginStep.Phone)
{
// 步骤1:检查全局配置并发送验证码
if (!TryGetTelegramApi(out telegramApiId, out telegramApiHash, out sessionsPath))
{
Snackbar.Add("请先在【系统设置】中配置全局 Telegram API(ApiId/ApiHash)", Severity.Error);
@@ -209,39 +194,46 @@
if (string.IsNullOrWhiteSpace(phoneNumber))
{
Snackbar.Add("请填写完整信息", Severity.Error);
Snackbar.Add("请输入手机号(包含国家代码)", Severity.Error);
return;
}
// 生成临时账号ID(用于跟踪登录过程)
accountId = Random.Shared.Next(1, int.MaxValue);
var result = await AccountService.StartLoginAsync(accountId, phoneNumber);
if (result.Success)
{
await SaveAccount(result.Account!);
return;
}
if (result.NextStep == "code")
{
Snackbar.Add($"验证码已发送到 {phoneNumber}", Severity.Info);
await stepper.NextStepAsync();
currentStep = LoginStep.Code;
return;
}
else if (result.NextStep == "signup")
if (result.NextStep == "password")
{
Snackbar.Add("此号码未注册Telegram,请先注册", Severity.Error);
}
else if (result.NextStep == "password")
{
// 极少数情况下可能直接进入 2FA(例如会话已可恢复)
Snackbar.Add("需要两步验证密码", Severity.Info);
await stepper.NextStepAsync();
await stepper.NextStepAsync();
}
else
{
Snackbar.Add(result.Message ?? "发送验证码失败", Severity.Error);
currentStep = LoginStep.Password;
return;
}
Snackbar.Add(result.Message ?? "发送验证码失败", Severity.Error);
return;
}
else if (currentStep == 1)
if (currentStep == LoginStep.Code)
{
// 步骤2:验证验证码
if (accountId == 0)
{
Snackbar.Add("请先发送验证码", Severity.Warning);
currentStep = LoginStep.Phone;
return;
}
if (string.IsNullOrWhiteSpace(verificationCode))
{
Snackbar.Add("请输入验证码", Severity.Error);
@@ -249,41 +241,47 @@
}
var result = await AccountService.SubmitCodeAsync(accountId, verificationCode);
if (result.Success)
{
// 登录成功,保存到数据库
await SaveAccount(result.Account!);
return;
}
else if (result.NextStep == "password")
if (result.NextStep == "password")
{
Snackbar.Add("需要两步验证密码", Severity.Info);
await stepper.NextStepAsync();
}
else
{
Snackbar.Add(result.Message ?? "验证码错误", Severity.Error);
currentStep = LoginStep.Password;
return;
}
Snackbar.Add(result.Message ?? "验证码错误或已过期", Severity.Error);
return;
}
else if (currentStep == 2)
if (currentStep == LoginStep.Password)
{
// 步骤3:验证密码
if (accountId == 0)
{
Snackbar.Add("请先发送验证码", Severity.Warning);
currentStep = LoginStep.Phone;
return;
}
if (string.IsNullOrWhiteSpace(password))
{
Snackbar.Add("请输入密码", Severity.Error);
Snackbar.Add("请输入两步验证密码", Severity.Error);
return;
}
var result = await AccountService.SubmitPasswordAsync(accountId, password);
if (result.Success)
{
await SaveAccount(result.Account!);
return;
}
else
{
Snackbar.Add(result.Message ?? "密码错误", Severity.Error);
}
Snackbar.Add(result.Message ?? "密码错误", Severity.Error);
return;
}
}
catch (Exception ex)
@@ -321,6 +319,14 @@
}
}
private enum LoginStep
{
Phone = 0,
Code = 1,
Password = 2,
Done = 3
}
private async Task SaveAccount(TelegramPanel.Core.Models.AccountInfo accountInfo)
{
try
@@ -367,10 +373,7 @@
userId = accountInfo.TelegramUserId;
// 跳转到完成步骤
if (stepper != null)
{
await stepper.NextStepAsync();
}
currentStep = LoginStep.Done;
}
catch (Exception ex)
{