From 00c2275dcf43ddad03fd1cc46d79d0eaaf4d59df Mon Sep 17 00:00:00 2001 From: meoacgx Date: Fri, 19 Dec 2025 14:29:57 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E5=88=97=E8=A1=A8=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=88=B7=E6=96=B0=E7=8A=B6=E6=80=81/=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E7=B3=BB=E7=BB=9F=E9=80=9A=E7=9F=A5/=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=9C=A8=E7=BA=BF=E8=AE=BE=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dialogs/OnlineDevicesDialog.razor | 192 ++++++++++++++++++ .../Dialogs/SystemInboxDialog.razor | 90 ++++++++ .../Components/Pages/Accounts.razor | 122 +++++++++++ .../Components/_Imports.razor | 1 + 4 files changed, 405 insertions(+) create mode 100644 src/TelegramPanel.Web/Components/Dialogs/OnlineDevicesDialog.razor create mode 100644 src/TelegramPanel.Web/Components/Dialogs/SystemInboxDialog.razor diff --git a/src/TelegramPanel.Web/Components/Dialogs/OnlineDevicesDialog.razor b/src/TelegramPanel.Web/Components/Dialogs/OnlineDevicesDialog.razor new file mode 100644 index 0000000..d36e938 --- /dev/null +++ b/src/TelegramPanel.Web/Components/Dialogs/OnlineDevicesDialog.razor @@ -0,0 +1,192 @@ +@namespace TelegramPanel.Web.Components.Dialogs +@inject AccountTelegramToolsService AccountTelegramTools +@inject IDialogService DialogService +@inject ISnackbar Snackbar + + + + + 账号:@Phone(ID:@AccountId) + + + 这里显示账号当前在线设备/会话,可对“非当前设备”执行踢出登录。 + + + + + 刷新 + + + 踢出所有其他设备 + + + + @if (loading) + { + + } + else if (devices.Count == 0) + { + 暂无设备信息 + } + else + { + + + 当前 + 应用/设备 + 系统 + IP/地区 + 最近活跃 + 操作 + + + + + @(context.Current ? "是" : "否") + + + + @context.Title + ApiId=@context.ApiId @context.AppVersion + + + @($"{context.Platform} {context.SystemVersion}".Trim()) + + + @context.Ip + @($"{context.Country} {context.Region}".Trim()) + + + @(context.LastActiveAtUtc?.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss") ?? "-") + + + + 踢出 + + + + + } + + + + 关闭 + + + +@code +{ + [CascadingParameter] private MudDialogInstance MudDialog { get; set; } = default!; + [Parameter] public int AccountId { get; set; } + [Parameter] public string Phone { get; set; } = ""; + + private bool loading; + private List devices = new(); + + protected override async Task OnInitializedAsync() + { + await LoadDevices(); + } + + private async Task LoadDevices() + { + loading = true; + try + { + var list = await AccountTelegramTools.GetAuthorizationsAsync(AccountId); + devices = list.ToList(); + } + catch (Exception ex) + { + Snackbar.Add($"加载设备失败:{ex.Message}", Severity.Error); + } + finally + { + loading = false; + } + } + + private async Task KickOne(TelegramAuthorizationInfo auth) + { + if (auth.Current) + { + Snackbar.Add("不能踢出当前设备", Severity.Warning); + return; + } + + bool? result = await DialogService.ShowMessageBox( + "确认踢出", + $"确定要踢出该设备登录吗?\n{auth.Title}\nIP:{auth.Ip}\n最近活跃:{auth.LastActiveAtUtc?.ToLocalTime():yyyy-MM-dd HH:mm:ss}", + yesText: "踢出", cancelText: "取消"); + + if (result != true) + return; + + loading = true; + try + { + var ok = await AccountTelegramTools.KickAuthorizationAsync(AccountId, auth.Hash); + if (ok) + { + Snackbar.Add("已踢出该设备", Severity.Success); + await LoadDevices(); + } + else + { + Snackbar.Add("踢出失败(返回 false)", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"踢出失败:{ex.Message}", Severity.Error); + } + finally + { + loading = false; + } + } + + private async Task KickAllOthers() + { + bool? result = await DialogService.ShowMessageBox( + "确认踢出", + "确定要踢出所有其他设备吗?(当前设备会保留)", + yesText: "踢出", cancelText: "取消"); + + if (result != true) + return; + + loading = true; + try + { + var ok = await AccountTelegramTools.KickAllOtherAuthorizationsAsync(AccountId); + if (ok) + { + Snackbar.Add("已踢出所有其他设备", Severity.Success); + await LoadDevices(); + } + else + { + Snackbar.Add("踢出失败(返回 false)", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"踢出失败:{ex.Message}", Severity.Error); + } + finally + { + loading = false; + } + } + + private void Close() + { + MudDialog.Close(); + } +} + diff --git a/src/TelegramPanel.Web/Components/Dialogs/SystemInboxDialog.razor b/src/TelegramPanel.Web/Components/Dialogs/SystemInboxDialog.razor new file mode 100644 index 0000000..a79a149 --- /dev/null +++ b/src/TelegramPanel.Web/Components/Dialogs/SystemInboxDialog.razor @@ -0,0 +1,90 @@ +@namespace TelegramPanel.Web.Components.Dialogs +@inject AccountTelegramToolsService AccountTelegramTools +@inject ISnackbar Snackbar + + + + + 账号:@Phone(ID:@AccountId) + + + 这里显示 Telegram 系统通知(通常用于接收登录验证码)。若列表为空,可能是该账号从未在 Telegram 内收到系统消息。 + + + + + 刷新 + + @if (lastLoadedAtUtc.HasValue) + { + 最后刷新:@lastLoadedAtUtc.Value.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss") + } + + + @if (loading) + { + + } + else if (messages.Count == 0) + { + 暂无系统通知 + } + else + { + + @foreach (var m in messages) + { + + @((m.DateUtc ?? DateTime.MinValue).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")) + @m.Text + + } + + } + + + + 关闭 + + + +@code +{ + [CascadingParameter] private MudDialogInstance MudDialog { get; set; } = default!; + [Parameter] public int AccountId { get; set; } + [Parameter] public string Phone { get; set; } = ""; + + private bool loading; + private DateTime? lastLoadedAtUtc; + private List messages = new(); + + protected override async Task OnInitializedAsync() + { + await LoadMessages(); + } + + private async Task LoadMessages() + { + loading = true; + try + { + var list = await AccountTelegramTools.GetLatestSystemMessagesAsync(AccountId, limit: 30); + messages = list.ToList(); + lastLoadedAtUtc = DateTime.UtcNow; + } + catch (Exception ex) + { + Snackbar.Add($"加载系统通知失败:{ex.Message}", Severity.Error); + } + finally + { + loading = false; + } + } + + private void Close() + { + MudDialog.Close(); + } +} diff --git a/src/TelegramPanel.Web/Components/Pages/Accounts.razor b/src/TelegramPanel.Web/Components/Pages/Accounts.razor index a599350..c6cd22d 100644 --- a/src/TelegramPanel.Web/Components/Pages/Accounts.razor +++ b/src/TelegramPanel.Web/Components/Pages/Accounts.razor @@ -1,6 +1,7 @@ @page "/accounts" @inject AccountManagementService AccountManagement @inject AccountCategoryManagementService CategoryManagement +@inject AccountTelegramToolsService AccountTelegramTools @inject ISnackbar Snackbar @inject IDialogService DialogService @using TelegramPanel.Data.Entities @@ -25,6 +26,10 @@ + + 批量刷新状态 + 刷新 @@ -36,6 +41,7 @@ 分类 频道/群组 状态 + Telegram 状态 最后同步 操作 @@ -64,10 +70,28 @@ @(context.IsActive ? "活跃" : "停用") + + @if (telegramStatus.TryGetValue(context.Id, out var s)) + { + + @s.Summary + + } + else + { + 未检测 + } + @context.LastSyncAt.ToString("yyyy-MM-dd HH:mm") + + + telegramStatus = new(); private IEnumerable filteredAccounts => accounts.Where(a => @@ -134,6 +159,10 @@ { var accountList = await AccountManagement.GetAllAccountsAsync(); accounts = accountList.ToList(); + // 刷新账号列表后,清理不存在账号的状态缓存 + var ids = new HashSet(accounts.Select(a => a.Id)); + foreach (var key in telegramStatus.Keys.Where(k => !ids.Contains(k)).ToList()) + telegramStatus.Remove(key); await LoadCategories(); } catch (Exception ex) @@ -153,6 +182,99 @@ await DialogService.ShowMessageBox("账号详情", message, "关闭"); } + private async Task RefreshTelegramStatus(Account account) + { + try + { + loading = true; + var result = await AccountTelegramTools.RefreshAccountStatusAsync(account.Id); + telegramStatus[account.Id] = result; + + // 资料刷新后重新加载(昵称/用户名/用户ID 可能更新) + await LoadAccounts(); + + Snackbar.Add($"账号 {account.Phone} 状态:{result.Summary}", result.Ok ? Severity.Success : Severity.Warning); + } + catch (Exception ex) + { + Snackbar.Add($"刷新失败:{ex.Message}", Severity.Error); + } + finally + { + loading = false; + } + } + + private async Task BatchRefreshTelegramStatus() + { + if (accounts.Count == 0) + { + Snackbar.Add("暂无账号可刷新", Severity.Info); + return; + } + + loading = true; + try + { + var targets = filteredAccounts.ToList(); + foreach (var a in targets) + { + try + { + var result = await AccountTelegramTools.RefreshAccountStatusAsync(a.Id); + telegramStatus[a.Id] = result; + } + catch (Exception ex) + { + telegramStatus[a.Id] = new TelegramAccountStatusResult( + Ok: false, + Summary: "刷新失败", + Details: ex.Message, + CheckedAtUtc: DateTime.UtcNow); + } + + StateHasChanged(); + } + + await LoadAccounts(); + Snackbar.Add($"批量刷新完成:{targets.Count} 个账号", Severity.Success); + } + catch (Exception ex) + { + Snackbar.Add($"批量刷新失败:{ex.Message}", Severity.Error); + } + finally + { + loading = false; + } + } + + private async Task OpenSystemInbox(Account account) + { + var parameters = new DialogParameters + { + ["AccountId"] = account.Id, + ["Phone"] = account.Phone + }; + + var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Medium, FullWidth = true }; + DialogService.Show("系统通知(验证码)", parameters, options); + await Task.CompletedTask; + } + + private async Task OpenDevices(Account account) + { + var parameters = new DialogParameters + { + ["AccountId"] = account.Id, + ["Phone"] = account.Phone + }; + + var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large, FullWidth = true }; + DialogService.Show("在线设备", parameters, options); + await Task.CompletedTask; + } + private async Task ToggleActive(Account account) { try diff --git a/src/TelegramPanel.Web/Components/_Imports.razor b/src/TelegramPanel.Web/Components/_Imports.razor index e08bc5a..ab1c6e5 100644 --- a/src/TelegramPanel.Web/Components/_Imports.razor +++ b/src/TelegramPanel.Web/Components/_Imports.razor @@ -13,5 +13,6 @@ @using TelegramPanel.Core.Interfaces @using TelegramPanel.Core.Services @using TelegramPanel.Core.Services.Telegram +@using TelegramPanel.Core.Models @using TelegramPanel.Web.Components @using TelegramPanel.Web.Components.Layout