修复手机号登录:改为状态机流程,解决下一步无响应
This commit is contained in:
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)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user