603 lines
22 KiB
Plaintext
603 lines
22 KiB
Plaintext
@page "/channels"
|
|
@inject ChannelManagementService ChannelManagement
|
|
@inject ChannelGroupManagementService ChannelGroupManagement
|
|
@inject AccountManagementService AccountManagement
|
|
@inject TelegramPanel.Web.Services.DataSyncService DataSync
|
|
@inject IChannelService ChannelService
|
|
@inject ISnackbar Snackbar
|
|
@inject IDialogService DialogService
|
|
@inject NavigationManager Navigation
|
|
@inject IJSRuntime JS
|
|
@using TelegramPanel.Web.Components.Dialogs
|
|
@using TelegramPanel.Data.Entities
|
|
|
|
<PageTitle>频道列表 - Telegram Panel</PageTitle>
|
|
|
|
<MudText Typo="Typo.h4" Class="mb-4">频道列表</MudText>
|
|
|
|
<MudPaper Class="pa-4 mb-4" Elevation="0" Outlined="true">
|
|
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center" Style="flex-wrap: wrap;">
|
|
<MudSelect @bind-Value="filterAccount" @bind-Value:after="OnFiltersChanged" Label="筛选账号" Variant="Variant.Outlined" Dense="true"
|
|
Class="tp-input-full-xs" Style="min-width: 200px; flex: 1; max-width: 400px;">
|
|
<MudSelectItem Value="0">全部账号</MudSelectItem>
|
|
@foreach (var account in accounts)
|
|
{
|
|
<MudSelectItem Value="@account.Id">@FormatAccountLabel(account)</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
<MudSelect @bind-Value="filterType" @bind-Value:after="OnFiltersChanged" Label="频道类型" Variant="Variant.Outlined" Dense="true"
|
|
Class="tp-input-full-xs" Style="min-width: 120px; flex: 0 1 150px;">
|
|
<MudSelectItem Value='@("all")'>全部</MudSelectItem>
|
|
<MudSelectItem Value='@("public")'>公开</MudSelectItem>
|
|
<MudSelectItem Value='@("private")'>私密</MudSelectItem>
|
|
</MudSelect>
|
|
<MudTextField @bind-Value="searchString" Placeholder="搜索频道..." Adornment="Adornment.Start"
|
|
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0 tp-input-full-xs"
|
|
Style="min-width: 200px; flex: 1; max-width: 400px;"
|
|
Immediate="true" @bind-Value:after="OnSearchChanged" />
|
|
</MudStack>
|
|
</MudPaper>
|
|
|
|
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center" Class="mb-2" Style="flex-wrap: wrap;">
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@SelectionToggleIcon"
|
|
Disabled="@(loading || channels.Count == 0)" OnClick="CycleSelectionToggle" Size="Size.Small">
|
|
@SelectionToggleText
|
|
</MudButton>
|
|
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Info" StartIcon="@Icons.Material.Filled.Sync"
|
|
Disabled="@(loading || filterAccount <= 0)" OnClick="SyncCurrentAccountChannels" Size="Size.Small">
|
|
同步当前
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Info" StartIcon="@Icons.Material.Filled.Sync"
|
|
Disabled="@loading" OnClick="SyncAllAccountsChannels" Size="Size.Small">
|
|
同步全部
|
|
</MudButton>
|
|
|
|
<MudMenu Label="批量操作" Variant="Variant.Outlined" Color="Color.Default" StartIcon="@Icons.Material.Filled.MoreHoriz"
|
|
Size="Size.Small" Disabled="@loading" Dense="true" EndIcon="@Icons.Material.Filled.KeyboardArrowDown">
|
|
<MudMenuItem Disabled="@(selectedChannels.Count == 0)" OnClick="OpenBatchInviteSelected">批量邀请(已选)</MudMenuItem>
|
|
<MudMenuItem Disabled="@(selectedChannels.Count == 0)" OnClick="OpenBatchSetAdminsSelected">批量设置管理员(已选)</MudMenuItem>
|
|
<MudMenuItem Disabled="@(selectedChannels.Count == 0)" OnClick="OpenBatchSetGroupSelected">批量修改分组(已选)</MudMenuItem>
|
|
</MudMenu>
|
|
|
|
@if (selectedChannels.Count > 0)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Info" Class="ml-2">已选 @selectedChannels.Count</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="ml-2">频道 (@totalCount)</MudText>
|
|
}
|
|
</MudStack>
|
|
|
|
<MudCard>
|
|
<MudCardContent>
|
|
<MudTable ServerData="LoadChannelsServerData" Hover="true" Breakpoint="Breakpoint.Sm" Loading="@loading"
|
|
Class="tp-table"
|
|
MultiSelection="true" @bind-SelectedItems="selectedChannels" @ref="_table">
|
|
<HeaderContent>
|
|
<MudTh>频道名称</MudTh>
|
|
<MudTh>用户名</MudTh>
|
|
<MudTh>类型</MudTh>
|
|
<MudTh>成员数</MudTh>
|
|
<MudTh>创建账号</MudTh>
|
|
<MudTh>最后同步</MudTh>
|
|
<MudTh>操作</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd DataLabel="频道名称">
|
|
<div class="d-flex align-center">
|
|
<MudAvatar Color="Color.Primary" Size="Size.Small" Class="mr-2">@(string.IsNullOrWhiteSpace(context.Title) ? "?" : context.Title[0])</MudAvatar>
|
|
<div>
|
|
<MudText Typo="Typo.body2"><strong>@context.Title</strong></MudText>
|
|
@if (!string.IsNullOrEmpty(context.About))
|
|
{
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary">@(context.About.Length > 50 ? context.About.Substring(0, 50) + "..." : context.About)</MudText>
|
|
}
|
|
</div>
|
|
</div>
|
|
</MudTd>
|
|
<MudTd DataLabel="用户名">
|
|
@if (!string.IsNullOrEmpty(context.Username))
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Icon="@Icons.Material.Filled.AlternateEmail">@context.Username</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<span>-</span>
|
|
}
|
|
</MudTd>
|
|
<MudTd DataLabel="类型">
|
|
<MudChip T="string" Size="Size.Small" Color="@(string.IsNullOrEmpty(context.Username) ? Color.Warning : Color.Success)">
|
|
@(string.IsNullOrEmpty(context.Username) ? "私密" : "公开")
|
|
</MudChip>
|
|
</MudTd>
|
|
<MudTd DataLabel="成员数">@context.MemberCount</MudTd>
|
|
<MudTd DataLabel="创建账号">
|
|
@if (context.CreatorAccount != null)
|
|
{
|
|
<span>@context.CreatorAccount.DisplayPhone</span>
|
|
}
|
|
else if (context.CreatorAccountId != null)
|
|
{
|
|
<span>账号 @context.CreatorAccountId</span>
|
|
}
|
|
else
|
|
{
|
|
<span>-</span>
|
|
}
|
|
</MudTd>
|
|
<MudTd DataLabel="最后同步"><PanelTime Value="context.SyncedAt" /></MudTd>
|
|
<MudTd DataLabel="操作">
|
|
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center" Style="flex-wrap: wrap;">
|
|
<MudIconButton Icon="@Icons.Material.Filled.Info" Size="Size.Small" Color="Color.Info"
|
|
OnClick="@(async () => await ShowDetails(context))" Title="查看详情" />
|
|
<MudIconButton Icon="@Icons.Material.Filled.Edit" Size="Size.Small" Color="Color.Primary"
|
|
OnClick="@(async () => await EditChannel(context))" Title="编辑频道" />
|
|
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Size="Size.Small" Dense="true">
|
|
<MudMenuItem Icon="@Icons.Material.Filled.PersonAdd" OnClick="@(async () => await OpenInvite(context))">邀请成员</MudMenuItem>
|
|
<MudMenuItem Icon="@Icons.Material.Filled.AdminPanelSettings" OnClick="@(async () => await OpenSetAdmins(context))">设置管理员</MudMenuItem>
|
|
<MudMenuItem Icon="@Icons.Material.Filled.PersonRemove" OnClick="@(async () => await KickUser(context))">踢人</MudMenuItem>
|
|
<MudMenuItem Icon="@Icons.Material.Filled.Link" OnClick="@(async () => await CopyLinkAsync(context))">复制链接</MudMenuItem>
|
|
<MudDivider />
|
|
<MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error" OnClick="@(() => DeleteChannel(context.Id))">删除频道</MudMenuItem>
|
|
</MudMenu>
|
|
</MudStack>
|
|
</MudTd>
|
|
</RowTemplate>
|
|
<NoRecordsContent>
|
|
<MudText>暂无频道数据</MudText>
|
|
</NoRecordsContent>
|
|
<LoadingContent>
|
|
<MudProgressCircular Indeterminate="true" />
|
|
</LoadingContent>
|
|
<PagerContent>
|
|
<MudTablePager PageSizeOptions="new int[] { 20, 50, 100, 200 }" />
|
|
</PagerContent>
|
|
</MudTable>
|
|
</MudCardContent>
|
|
</MudCard>
|
|
|
|
@code {
|
|
private MudTable<Channel>? _table;
|
|
private List<Channel> channels = new();
|
|
private List<Account> accounts = new();
|
|
private List<ChannelGroup> groups = new();
|
|
private int totalCount;
|
|
private bool loading = true;
|
|
private string searchString = "";
|
|
private int filterAccount = 0;
|
|
private string filterType = "all";
|
|
private HashSet<Channel> selectedChannels = new();
|
|
private CancellationTokenSource? _searchDebounceCts;
|
|
|
|
private SelectionToggleMode _selectionToggleMode = SelectionToggleMode.SelectAll;
|
|
private string SelectionToggleText => _selectionToggleMode switch
|
|
{
|
|
SelectionToggleMode.SelectAll => "全选本页",
|
|
SelectionToggleMode.Invert => "反选本页",
|
|
SelectionToggleMode.Clear => "清空选择",
|
|
_ => "全选本页"
|
|
};
|
|
|
|
private string SelectionToggleIcon => _selectionToggleMode switch
|
|
{
|
|
SelectionToggleMode.SelectAll => Icons.Material.Filled.SelectAll,
|
|
SelectionToggleMode.Invert => Icons.Material.Filled.Flip,
|
|
SelectionToggleMode.Clear => Icons.Material.Filled.Clear,
|
|
_ => Icons.Material.Filled.SelectAll
|
|
};
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await LoadAccounts();
|
|
await LoadGroups();
|
|
}
|
|
|
|
private async Task LoadAccounts()
|
|
{
|
|
try
|
|
{
|
|
var accountList = await AccountManagement.GetAllAccountsAsync();
|
|
accounts = accountList
|
|
.Where(a => a.Category?.ExcludeFromOperations != true)
|
|
.ToList();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"加载账号列表失败:{ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private async Task LoadGroups()
|
|
{
|
|
try
|
|
{
|
|
var list = await ChannelGroupManagement.GetAllGroupsAsync();
|
|
groups = list.ToList();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"加载分组失败:{ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private async Task<TableData<Channel>> LoadChannelsServerData(TableState state, CancellationToken cancellationToken)
|
|
{
|
|
loading = true;
|
|
try
|
|
{
|
|
var (items, total) = await ChannelManagement.QueryChannelsForViewPagedAsync(
|
|
creatorAccountId: filterAccount,
|
|
filterType: filterType,
|
|
search: searchString,
|
|
pageIndex: state.Page,
|
|
pageSize: state.PageSize,
|
|
cancellationToken: cancellationToken);
|
|
|
|
channels = items.ToList();
|
|
totalCount = total;
|
|
selectedChannels.Clear();
|
|
|
|
return new TableData<Channel> { Items = channels, TotalItems = totalCount };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"加载失败:{ex.Message}", Severity.Error);
|
|
return new TableData<Channel> { Items = Array.Empty<Channel>(), TotalItems = 0 };
|
|
}
|
|
finally
|
|
{
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
private async Task ReloadTable()
|
|
{
|
|
selectedChannels.Clear();
|
|
if (_table != null)
|
|
await _table.ReloadServerData();
|
|
}
|
|
|
|
private Task SelectAllFiltered()
|
|
{
|
|
selectedChannels = channels.ToHashSet();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task InvertSelection()
|
|
{
|
|
var targets = channels.ToList();
|
|
foreach (var c in targets)
|
|
{
|
|
if (selectedChannels.Contains(c))
|
|
selectedChannels.Remove(c);
|
|
else
|
|
selectedChannels.Add(c);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task ClearSelection()
|
|
{
|
|
selectedChannels.Clear();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task OnFiltersChanged()
|
|
{
|
|
await ReloadTable();
|
|
}
|
|
|
|
private async Task OnSearchChanged()
|
|
{
|
|
_searchDebounceCts?.Cancel();
|
|
_searchDebounceCts?.Dispose();
|
|
_searchDebounceCts = new CancellationTokenSource();
|
|
|
|
var token = _searchDebounceCts.Token;
|
|
try
|
|
{
|
|
await Task.Delay(300, token);
|
|
await ReloadTable();
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
private async Task CycleSelectionToggle()
|
|
{
|
|
switch (_selectionToggleMode)
|
|
{
|
|
case SelectionToggleMode.SelectAll:
|
|
await SelectAllFiltered();
|
|
_selectionToggleMode = SelectionToggleMode.Invert;
|
|
break;
|
|
case SelectionToggleMode.Invert:
|
|
await InvertSelection();
|
|
_selectionToggleMode = SelectionToggleMode.Clear;
|
|
break;
|
|
case SelectionToggleMode.Clear:
|
|
await ClearSelection();
|
|
_selectionToggleMode = SelectionToggleMode.SelectAll;
|
|
break;
|
|
default:
|
|
_selectionToggleMode = SelectionToggleMode.SelectAll;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private enum SelectionToggleMode
|
|
{
|
|
SelectAll = 0,
|
|
Invert = 1,
|
|
Clear = 2
|
|
}
|
|
|
|
private static string FormatAccountLabel(Account account)
|
|
{
|
|
var nickname = string.IsNullOrWhiteSpace(account.Nickname) ? null : account.Nickname.Trim();
|
|
var username = string.IsNullOrWhiteSpace(account.Username) ? null : account.Username.Trim();
|
|
|
|
var namePart = nickname ?? account.Phone;
|
|
if (!string.IsNullOrWhiteSpace(username))
|
|
return $"{namePart} (@{username})";
|
|
return namePart;
|
|
}
|
|
|
|
private async Task OpenBatchInviteSelected()
|
|
{
|
|
var targets = selectedChannels.ToList();
|
|
if (targets.Count == 0)
|
|
{
|
|
Snackbar.Add("未选择任何频道", Severity.Info);
|
|
return;
|
|
}
|
|
|
|
await OpenInvite(targets);
|
|
}
|
|
|
|
private async Task OpenBatchSetAdminsSelected()
|
|
{
|
|
var targets = selectedChannels.ToList();
|
|
if (targets.Count == 0)
|
|
{
|
|
Snackbar.Add("未选择任何频道", Severity.Info);
|
|
return;
|
|
}
|
|
|
|
await OpenSetAdmins(targets);
|
|
}
|
|
|
|
private async Task OpenInvite(Channel channel)
|
|
{
|
|
await OpenInvite(new List<Channel> { channel });
|
|
}
|
|
|
|
private async Task OpenInvite(List<Channel> targets)
|
|
{
|
|
var defaultAccountId = GuessDefaultExecuteAccountId(targets);
|
|
var parameters = new DialogParameters
|
|
{
|
|
["Channels"] = targets,
|
|
["DefaultAccountId"] = defaultAccountId
|
|
};
|
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large, FullWidth = true };
|
|
var dialog = DialogService.Show<ChannelBatchInviteDialog>("邀请成员", parameters, options);
|
|
await dialog.Result;
|
|
}
|
|
|
|
private async Task OpenSetAdmins(Channel channel)
|
|
{
|
|
await OpenSetAdmins(new List<Channel> { channel });
|
|
}
|
|
|
|
private async Task OpenSetAdmins(List<Channel> targets)
|
|
{
|
|
var defaultAccountId = GuessDefaultExecuteAccountId(targets);
|
|
var parameters = new DialogParameters
|
|
{
|
|
["Channels"] = targets,
|
|
["DefaultAccountId"] = defaultAccountId
|
|
};
|
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large, FullWidth = true };
|
|
var dialog = DialogService.Show<ChannelBatchAdminsDialog>("设置管理员", parameters, options);
|
|
await dialog.Result;
|
|
}
|
|
|
|
private int GuessDefaultExecuteAccountId(IReadOnlyList<Channel> targets)
|
|
{
|
|
if (targets.Count == 1)
|
|
{
|
|
var cid = targets[0].CreatorAccountId;
|
|
if (cid != null && cid.Value > 0)
|
|
return cid.Value;
|
|
}
|
|
|
|
if (filterAccount > 0)
|
|
return filterAccount;
|
|
|
|
var ids = targets.Select(x => x.CreatorAccountId).Where(x => x != null && x.Value > 0).Select(x => x!.Value).Distinct().ToList();
|
|
return ids.Count == 1 ? ids[0] : 0;
|
|
}
|
|
|
|
private async Task SyncCurrentAccountChannels()
|
|
{
|
|
if (loading)
|
|
return;
|
|
|
|
if (filterAccount <= 0)
|
|
{
|
|
Snackbar.Add("请先选择一个账号再同步", Severity.Info);
|
|
return;
|
|
}
|
|
|
|
loading = true;
|
|
try
|
|
{
|
|
var summary = await DataSync.SyncAccountAsync(filterAccount, CancellationToken.None);
|
|
|
|
foreach (var failure in summary.AccountFailures)
|
|
Snackbar.Add($"账号 {failure.Phone} 同步失败:{failure.Error}", Severity.Warning);
|
|
|
|
Snackbar.Add($"同步完成:{summary.TotalChannelsSynced} 个频道,{summary.TotalGroupsSynced} 个群组", summary.AccountFailures.Count == 0 ? Severity.Success : Severity.Warning);
|
|
|
|
await ReloadTable();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"同步失败:{ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
private async Task SyncAllAccountsChannels()
|
|
{
|
|
if (loading)
|
|
return;
|
|
|
|
loading = true;
|
|
try
|
|
{
|
|
var summary = await DataSync.SyncAllActiveAccountsAsync(CancellationToken.None);
|
|
|
|
foreach (var failure in summary.AccountFailures)
|
|
Snackbar.Add($"账号 {failure.Phone} 同步失败:{failure.Error}", Severity.Warning);
|
|
|
|
Snackbar.Add($"同步完成:{summary.TotalChannelsSynced} 个频道,{summary.TotalGroupsSynced} 个群组", summary.AccountFailures.Count == 0 ? Severity.Success : Severity.Warning);
|
|
|
|
await ReloadTable();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"同步失败:{ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
private async Task ShowDetails(Channel channel)
|
|
{
|
|
var parameters = new DialogParameters { ["ChannelDbId"] = channel.Id };
|
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Large, FullWidth = true };
|
|
DialogService.Show<ChannelDetailsDialog>("频道详情", parameters, options);
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
private async Task EditChannel(Channel channel)
|
|
{
|
|
var parameters = new DialogParameters { ["ChannelDbId"] = channel.Id };
|
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Medium, FullWidth = true };
|
|
var dialog = DialogService.Show<ChannelEditDialog>("编辑频道", parameters, options);
|
|
var result = await dialog.Result;
|
|
if (!result.Canceled)
|
|
await ReloadTable();
|
|
}
|
|
|
|
private async Task OpenBatchSetGroupSelected()
|
|
{
|
|
var targets = selectedChannels.ToList();
|
|
if (targets.Count == 0)
|
|
{
|
|
Snackbar.Add("未选择任何频道", Severity.Info);
|
|
return;
|
|
}
|
|
|
|
if (groups.Count == 0)
|
|
await LoadGroups();
|
|
|
|
var parameters = new DialogParameters
|
|
{
|
|
["Groups"] = groups,
|
|
["SelectedCount"] = targets.Count
|
|
};
|
|
var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
|
|
var dialog = DialogService.Show<BatchSetChannelGroupDialog>("批量修改分组", parameters, options);
|
|
var result = await dialog.Result;
|
|
if (result is not { Canceled: false })
|
|
return;
|
|
|
|
var groupIdRaw = result.Data is int x ? x : 0;
|
|
int? groupId = groupIdRaw <= 0 ? null : groupIdRaw;
|
|
|
|
loading = true;
|
|
try
|
|
{
|
|
foreach (var c in targets)
|
|
await ChannelManagement.UpdateChannelGroupAsync(c.Id, groupId);
|
|
|
|
selectedChannels.Clear();
|
|
await ReloadTable();
|
|
Snackbar.Add($"分组已更新:{targets.Count} 个频道", Severity.Success);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"修改分组失败:{ex.Message}", Severity.Error);
|
|
}
|
|
finally
|
|
{
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
private async Task KickUser(Channel channel)
|
|
{
|
|
// 复用详情弹窗里的“踢人”能力,减少重复 UI
|
|
await ShowDetails(channel);
|
|
}
|
|
|
|
private async Task CopyLinkAsync(Channel channel)
|
|
{
|
|
try
|
|
{
|
|
var executeAccountId = channel.CreatorAccountId ?? filterAccount;
|
|
if (executeAccountId <= 0)
|
|
{
|
|
Snackbar.Add("无法导出链接:请先选择账号或确保频道有创建账号", Severity.Warning);
|
|
return;
|
|
}
|
|
|
|
var link = await ChannelService.ExportJoinLinkAsync(executeAccountId, channel.TelegramId);
|
|
|
|
await JS.InvokeVoidAsync("telegramPanel.copyText", link);
|
|
Snackbar.Add("已复制链接", Severity.Success);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"复制失败:{ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
|
|
private async Task DeleteChannel(int id)
|
|
{
|
|
var channel = channels.FirstOrDefault(c => c.Id == id);
|
|
if (channel == null) return;
|
|
|
|
bool? result = await DialogService.ShowMessageBox(
|
|
"确认删除",
|
|
$"确定要删除频道 {channel.Title} 吗?此操作不可恢复。",
|
|
yesText: "删除", cancelText: "取消");
|
|
|
|
if (result == true)
|
|
{
|
|
try
|
|
{
|
|
await ChannelManagement.DeleteChannelAsync(id);
|
|
await ReloadTable();
|
|
Snackbar.Add("删除成功", Severity.Success);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Snackbar.Add($"删除失败:{ex.Message}", Severity.Error);
|
|
}
|
|
}
|
|
}
|
|
}
|