Files
qhmes/yy-admin-master/YY.Admin/ViewModels/MainWindowViewModel.cs

757 lines
27 KiB
C#
Raw Normal View History

using Mapster;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using SqlSugar;
using System.Collections.ObjectModel;
using System.IO;
using System.Net.Http;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using YY.Admin.Core;
using YY.Admin.Core.Const;
using YY.Admin.Core.Model;
using YY.Admin.Core.Services;
using YY.Admin.Core.Session;
using YY.Admin.Core.Util;
using YY.Admin.Event;
using YY.Admin.Module;
using YY.Admin.Services.Service.Auth;
using YY.Admin.Services.Service.Jeecg;
using YY.Admin.Services.Service.Menu;
using YY.Admin.ViewModels.Control;
using YY.Admin.Helper;
namespace YY.Admin.ViewModels
{
public class MainWindowViewModel : BaseViewModel
{
private readonly ISysAuthService _authService;
private readonly IDialogService _dialogService;
private readonly IJeecgLoginLogReportService _loginLogReportService;
private readonly IJeecgUserSyncCoordinator _jeecgUserSyncCoordinator;
private readonly IConfiguration _configuration;
private readonly HttpClient _httpClient = new();
private readonly CancellationTokenSource _backendConnectivityCts = new();
private const int ConnectivityCheckIntervalSeconds = 5;
private SysUser? _currentUser;
private bool _isBackendConnected;
#region
public ObservableCollection<TabItemModel> OpenTabs { get; } = new ObservableCollection<TabItemModel>();
private CancellationTokenSource? _navigateCts;
private bool _isTabClosing;
private TabItemModel? _selectedTab;
public TabItemModel? SelectedTab
{
get => _selectedTab;
set
{
if (SetProperty(ref _selectedTab, value) && value != null)
{
// 发布事件
_eventAggregator.GetEvent<TabSelectedEvent>().Publish(value);
bool isNotMenuTreeView = _regionManager.Regions[CommonConst.MenuRegion].ActiveViews.Where(it => it.GetType().Name != "MenuTreeView").Any();
if (value.TabSource is MenuItem menuItem && !isNotMenuTreeView)
{
var navItem = NavItems?.Where(it => it.ViewName == "MenuTreeView").FirstOrDefault();
if (navItem != null)
{
if (navItem.AlignBottom)
{
SelectedBottomNavItem = navItem;
}
else
{
SelectedTopNavItem = navItem;
}
}
} else if (value.TabSource is NavItem navItem)
{
if (navItem.AlignBottom)
{
SelectedBottomNavItem = navItem;
} else
{
SelectedTopNavItem = navItem;
}
}
// 取消上一次延迟导航
// 防止拖拽Tab时由于两次导航出现视觉上闪烁问题
// TabA拖动到TabB过程TabA -> TabB -> TabA
_navigateCts?.Cancel();
_navigateCts = new CancellationTokenSource();
// 延迟 100ms 执行导航(过滤临时选中)
_ = Task.Delay(100, _navigateCts.Token)
.ContinueWith(_ =>
{
Application.Current.Dispatcher.Invoke(() =>
{
_ = NavigateToViewAsync(CommonConst.ContentRegion, value.ViewName, value.TabSource?.NavigationParameter);
});
},
TaskContinuationOptions.OnlyOnRanToCompletion
);
}
}
}
#endregion
# region
public ObservableCollection<NavItem>? NavItems { get; set; }
public ObservableCollection<NavItem>? TopNavItems { get; set; }
public ObservableCollection<NavItem>? BottomNavItems { get; set; }
private NavItem? _selectedNavItem;
public NavItem? SelectedNavItem
{
get => _selectedNavItem;
set {
if (SetProperty(ref _selectedNavItem, value) && value != null)
{
// 执行命令(如果有)
if (value.Command?.CanExecute(value) == true)
{
value.Command.Execute(value);
}
}
}
}
private NavItem? _selectedTopNavItem;
public NavItem? SelectedTopNavItem
{
get => _selectedTopNavItem;
set
{
if (SetProperty(ref _selectedTopNavItem, value) && value != null)
{
// 取消底部选中 互斥
SelectedBottomNavItem = null;
SelectedNavItem = value;
}
}
}
private NavItem? _selectedBottomNavItem;
public NavItem? SelectedBottomNavItem
{
get => _selectedBottomNavItem;
set
{
if (SetProperty(ref _selectedBottomNavItem, value) && value != null)
{
// 取消顶部选中 互斥
SelectedTopNavItem = null;
SelectedNavItem = value;
}
}
}
#endregion
#region
private bool _isAppSettingsOpen;
public bool IsAppSettingsOpen
{
get => _isAppSettingsOpen;
set => SetProperty(ref _isAppSettingsOpen, value);
}
private AppSettingsViewModel? _appSettingsViewModel;
public AppSettingsViewModel? AppSettingsViewModel {
get => _appSettingsViewModel;
set => SetProperty(ref _appSettingsViewModel, value);
}
// 系统设置命令
public ICommand ResetAppSettingsCommand { get; }
public ICommand OpenAppSettingsCommand { get; }
public ICommand OpenServerSettingsCommand { get; }
#endregion
private SubscriptionToken? _openOrActivateTabToken;
private SubscriptionToken? _tabClosedToken;
private SubscriptionToken? _refreshTabToken;
private SubscriptionToken? _loginOutToken;
public MainWindowViewModel(
ISysAuthService authService,
IDialogService dialogService,
IJeecgLoginLogReportService loginLogReportService,
IJeecgUserSyncCoordinator jeecgUserSyncCoordinator,
ISysMenuService sysMenuService,
IContainerExtension _container,
IRegionManager regionManager
) : base(_container, regionManager)
{
// 加载系统设置
LoadAppSettings();
_authService = authService;
_dialogService = dialogService;
_loginLogReportService = loginLogReportService;
_jeecgUserSyncCoordinator = jeecgUserSyncCoordinator;
_configuration = _container.Resolve<IConfiguration>();
_loginLogReportService.StartBackgroundSync();
// 订阅用户变更事件
_currentUser = _authService.CurrentUser;
_authService.UserChanged += OnUserChanged;
// 登出命令
LogoutCommand = new DelegateCommand(LogoutAsync);
// 系统设置命令
OpenAppSettingsCommand = new DelegateCommand(OpenAppSettings);
ResetAppSettingsCommand = new DelegateCommand(ResetAppSettings);
OpenServerSettingsCommand = new DelegateCommand(OpenServerSettings);
// 初始化Sidebar数据
InitNavItems();
// 订阅事件
_openOrActivateTabToken = _eventAggregator.GetEvent<TabSourceSelectedEvent>().Subscribe(OnOpenOrActivateTab);
_tabClosedToken = _eventAggregator.GetEvent<TabClosedEvent>().Subscribe(OnTabClosed);
_refreshTabToken = _eventAggregator.GetEvent<TabRefreshEvent>().Subscribe(OnRefreshTab);
_loginOutToken = _eventAggregator.GetEvent<SysUserEvents.LoginOutEvent>().Subscribe(Destroy);
// 首次按默认连接;后续按本地保存状态恢复
var settings = ServerSettingsStore.Load();
IsServerConnectionEnabled = !settings.DisconnectConnection;
// 主窗口底部连接状态圆点
_ = StartBackendConnectivityLoopAsync(_backendConnectivityCts.Token);
}
public SysUser? CurrentUser
{
get => _currentUser;
set => SetProperty(ref _currentUser, value);
}
/// <summary>
/// 后端连接状态true=连接中false=已断开
/// </summary>
public bool IsBackendConnected
{
get => _isBackendConnected;
set
{
if (SetProperty(ref _isBackendConnected, value))
{
RaisePropertyChanged(nameof(BackendConnectionStatusBrush));
}
}
}
public Brush BackendConnectionStatusBrush => IsBackendConnected ? Brushes.LimeGreen : Brushes.Red;
private bool _isServerConnectionEnabled = true;
/// <summary>
/// 是否启用服务器连接(默认启用)
/// </summary>
public bool IsServerConnectionEnabled
{
get => _isServerConnectionEnabled;
set
{
if (SetProperty(ref _isServerConnectionEnabled, value))
{
if (!value)
{
IsBackendConnected = false;
_jeecgUserSyncCoordinator.Stop();
}
else
{
_jeecgUserSyncCoordinator.Start();
}
}
}
}
public DelegateCommand LogoutCommand { get; }
private void InitNavItems()
{
NavItems = new ObservableCollection<NavItem>
{
new NavItem {
Icon = "FileTreeOutline",
Name = "功能菜单",
ViewName = "MenuTreeView",
Command = new DelegateCommand<NavItem>(it => _ = NavigateToViewAsync(CommonConst.MenuRegion, it.ViewName))
},
new NavItem {
Icon = "GamepadVariantOutline",
Name = "菜单区域",
Command = new DelegateCommand<NavItem>(it => _ = NavigateToViewAsync(CommonConst.MenuRegion, it.ViewName))
},
new NavItem {
Icon = "FoodAppleOutline",
Name = "Tab区域",
Command = new DelegateCommand<NavItem>(OnOpenOrActivateTab)
},
new NavItem {
Icon = "Server",
Name = "服务器设置",
AlignBottom = true,
IsActive = false,
Command = OpenServerSettingsCommand
},
new NavItem {
Icon = "AccountCircleOutline",
Name = "个人中心",
AlignBottom = true,
Command = new DelegateCommand<NavItem>(OnOpenOrActivateTab)
},
new NavItem {
Icon = StringUtil.ConvertHtmlEntityToUnicode("&#xe7a3;"),
IconType = IconTypeEnum.AntDesign,
Name = "系统设置",
AlignBottom = true,
IsActive = false,
Command = OpenAppSettingsCommand
},
new NavItem {
Icon = "Power",
Name = "退出登录",
AlignBottom = true,
IsActive = false,
Command = LogoutCommand
}
};
TopNavItems = new(NavItems.Where(t => !t.AlignBottom));
BottomNavItems = new(NavItems.Where(t => t.AlignBottom));
SelectedTopNavItem = TopNavItems.FirstOrDefault();
}
private void OnUserChanged(object? sender, SysUser? user)
{
CurrentUser = user;
//// 用户变更时刷新菜单
//LoadMenuAsync();
}
/// <summary>
/// 导航
/// </summary>
/// <param name="regionName">区域名称</param>
/// <param name="viewName">视图名称</param>
/// <returns></returns>
private async Task NavigateToViewAsync(string regionName, string? viewName, INavigationParameters? parameters = null)
{
try
{
if (string.IsNullOrEmpty(regionName))
return;
var tcs = new TaskCompletionSource<bool>();
// 视图为空 或者 视图未在容器中注册
if (string.IsNullOrEmpty(viewName) || !(_container as IContainerProvider).IsRegistered<object>(viewName))
{
_logger.Error($"视图未注册: {viewName}");
_regionManager.RequestNavigate(regionName, "NotFoundView");
tcs.SetResult(false);
return;
}
_logger.Debug($"开始后台异步导航到: {viewName}");
// 在UI线程执行导航但不在属性设置的调用栈中
await Application.Current.Dispatcher.InvokeAsync(() =>
{
_regionManager.RequestNavigate(regionName, viewName,
result =>
{
if (result.Success)
{
_logger.Debug($"导航成功: {viewName}");
tcs.SetResult(true);
}
else
{
var exMsg = result.Exception?.Message;
_logger.Error($"导航失败: {viewName}, Region={regionName}, Exception={exMsg}");
tcs.SetResult(false);
}
}, parameters);
}, DispatcherPriority.Background); // 使用较低优先级
await tcs.Task;
}
catch (Exception ex)
{
_logger.Error($"导航异常: {ex.Message}");
}
}
/// <summary>
/// 打开或激活一个标签页(若已打开则激活,否则新建)
/// </summary>
private void OnOpenOrActivateTab(TabSource tabSource)
{
if (tabSource == null) {
_logger.Debug("tabSource为空");
return;
}
if (tabSource is MenuItem menuItem)
{
// 检查是否是父级菜单(有子菜单)
if (menuItem.Children?.Count > 0)
{
_logger.Debug($"跳过父级菜单: {menuItem.Name},它有 {menuItem.Children.Count} 个子菜单");
return;
}
// 检查是否有有效的 ViewName
//if (string.IsNullOrEmpty(menuItem.ViewName))
//{
// _logger.Debug($"菜单没有 ViewName: {menuItem.Name}");
// return;
//}
_logger.Debug($"点击菜单: {menuItem.Name}, ViewName: {menuItem.ViewName}");
}
// 检查标签是否已打开
var existingTab = OpenTabs.FirstOrDefault(t => t.TabSource == tabSource);
if (existingTab != null)
{
_logger.Debug($"切换到已存在标签: {tabSource.ViewName}");
SelectedTab = existingTab;
return;
}
// 创建新标签
var newTab = new TabItemModel
{
Header = tabSource.Name,
Icon = tabSource.Icon,
IconType = tabSource.IconType,
ViewName = tabSource.ViewName,
IsClosable = tabSource.IsClosable,
TabSource = tabSource,
OpenTabs = OpenTabs,
EventAggregator = _eventAggregator
};
_logger.Debug($"创建新标签: {tabSource.ViewName}");
OpenTabs.Add(newTab);
SelectedTab = newTab;
}
/// <summary>
/// 刷新Tab
/// </summary>
/// <param name="tabItemModel"></param>
private void OnRefreshTab(TabItemModel tabItemModel)
{
if (tabItemModel?.TabSource == null)
{
return;
}
// 当前Tab选中
SelectedTab = tabItemModel;
// 从Region中移除现有视图避免缓存问题
RemoveContentRegionView(tabItemModel.ViewName);
// 重新导航,确保加载新的视图
_ = NavigateToViewAsync(CommonConst.ContentRegion, tabItemModel.ViewName, tabItemModel.TabSource.NavigationParameter);
}
/// <summary>
/// TabItem关闭回调
/// </summary>
private void OnTabClosed(TabItemModel tabModel)
{
if (tabModel != null)
{
_logger.Debug($"标签已关闭: {tabModel.Header}");
//var viewName = OpenTabs.Count <= 1 ? null : tabModel.ViewName;
var viewName = tabModel.ViewName;
RemoveContentRegionView(viewName);
// 当前Tab是最后一个
if (OpenTabs.Count <= 1)
{
var region = _regionManager.Regions[CommonConst.MenuRegion];
var activeView = region.ActiveViews.FirstOrDefault();
if (activeView != null)
{
var navItem = NavItems?.Where(it => it.ViewName == activeView.GetType().Name).FirstOrDefault();
if (navItem != null)
{
if (navItem.AlignBottom)
{
SelectedBottomNavItem = navItem;
}
else
{
SelectedTopNavItem = navItem;
}
}
}
} else if (tabModel.TabSource is NavItem navItem)
{
if (navItem == SelectedTopNavItem)
{
SelectedTopNavItem = null;
SelectedNavItem = null;
}
else if (navItem == SelectedBottomNavItem)
{
SelectedBottomNavItem = null;
SelectedNavItem = null;
}
}
// 如果关闭的不是当前选中的Tab
if (SelectedTab != null && SelectedTab != tabModel)
{
// 发布事件强制刷新当前选中因为右键关闭未切换不会触发SelectedTab属性setter方法
_eventAggregator.GetEvent<TabSelectedEvent>().Publish(SelectedTab);
}
}
}
private async void LogoutAsync()
{
// 使用异步版本
var confirmed = await ConfirmAsync("确定退出登录吗?");
if (confirmed)
{
var account = AppSession.CurrentUser?.Account ?? CurrentUser?.Account ?? string.Empty;
_ = _loginLogReportService.ReportLogAsync("LOGIN", "退出登录", account, true);
// 发布登出事件
_eventAggregator.GetEvent<SysUserEvents.LoginOutEvent>().Publish(AppSession.CurrentUser!);
Logout();
}
// var messageBoxResult = HandyControl.Controls.MessageBox.Show(
// $"确定退出登录吗?",
// "确认登出",
//MessageBoxButton.OKCancel,
//MessageBoxImage.Warning);
// if (messageBoxResult != MessageBoxResult.OK) return;
// // 发布登出事件
// _eventAggregator.GetEvent<SysUserEvents.LoginOutEvent>().Publish(AppSession.CurrentUser!);
// Logout();
}
#region
/// <summary>
/// 打开系统设置
/// </summary>
private void OpenAppSettings()
{
IsAppSettingsOpen = true;
}
/// <summary>
/// 重置系统设置
/// </summary>
private void ResetAppSettings()
{
// 重置为默认值
// new AppSettingsViewModel().Adapt(AppSettingsViewModel);
AppSettingsViewModel = new AppSettingsViewModel();
AppSettingsViewModel.UpdateSkin(AppSettingsViewModel.SkinType!.Value);
AppSettingsViewModel.UpdateAppSettings();
}
/// <summary>
/// 加载系统设置
/// </summary>
private void LoadAppSettings()
{
try
{
var filePath = AppSettingsViewModel.GetFilePath();
if (!File.Exists(filePath))
{
AppSettingsViewModel = new AppSettingsViewModel();
AppSettingsViewModel.UpdateSkin(AppSettingsViewModel.SkinType!.Value);
AppSettingsViewModel.SaveAppSettings();
return;
}
var json = File.ReadAllText(filePath);
var appSettings = JsonConvert.DeserializeObject<AppSettings>(json);
AppSettingsViewModel = appSettings.Adapt<AppSettingsViewModel>();
AppSettingsViewModel.SkinType = AppSettingsViewModel.SyncWithSystem ? AppSettingsViewModel.GetSkinTypeBySystem() : AppSettingsViewModel.SkinType;
AppSettingsViewModel.UpdateSkin(AppSettingsViewModel.SkinType!.Value);
AppSettingsViewModel.UpdateAppSettings();
}
catch (Exception ex)
{
_logger.Error($"加载系统设置失败: {ex.Message}", ex);
}
}
#endregion
private async Task StartBackendConnectivityLoopAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
bool connected = false;
if (!IsServerConnectionEnabled)
{
try
{
await Application.Current.Dispatcher.InvokeAsync(() => { IsBackendConnected = false; });
}
catch
{
}
try
{
await Task.Delay(TimeSpan.FromSeconds(ConnectivityCheckIntervalSeconds), cancellationToken);
}
catch (OperationCanceledException)
{
break;
}
continue;
}
try
{
// 每轮都重新读取配置,保存服务器设置后可即时生效
var baseUrl = _configuration.GetValue<string>("JeecgIntegration:BaseUrl")?.TrimEnd('/');
var userListPath = _configuration.GetValue<string>("JeecgIntegration:UserListPath") ?? "/sys/user/scada/queryUser";
var probeUrl = string.IsNullOrWhiteSpace(baseUrl)
? string.Empty
: $"{baseUrl}{userListPath}?pageNo=1&pageSize=1&includeDetail=false";
if (!string.IsNullOrWhiteSpace(probeUrl))
{
using var req = new HttpRequestMessage(HttpMethod.Get, probeUrl);
using var resp = await _httpClient.SendAsync(req, cancellationToken);
connected = resp.IsSuccessStatusCode;
}
}
catch
{
connected = false;
}
try
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
IsBackendConnected = connected;
});
}
catch
{
// 忽略窗口关闭后的调度异常
}
try
{
await Task.Delay(TimeSpan.FromSeconds(ConnectivityCheckIntervalSeconds), cancellationToken);
}
catch (OperationCanceledException)
{
break;
}
}
}
private void OpenServerSettings()
{
_dialogService.ShowDialog("ServerSettingsDialog", r =>
{
if (r.Result == ButtonResult.OK)
{
var settings = ServerSettingsStore.Load();
IsServerConnectionEnabled = !settings.DisconnectConnection;
if (settings.DisconnectConnection)
{
var signalService = _container.Resolve<ISignalRService>();
_ = signalService.DisconnectAsync();
}
else
{
var signalService = _container.Resolve<ISignalRService>();
_ = signalService.ConnectUnifiedDeviceChannelAsync(CancellationToken.None);
}
}
});
}
/// <summary>
/// 清空资源
/// </summary>
private void Destroy(SysUser? sysUser = null)
{
if (_openOrActivateTabToken != null)
{
_eventAggregator
.GetEvent<TabSourceSelectedEvent>()
.Unsubscribe(_openOrActivateTabToken);
_openOrActivateTabToken = null;
}
if (_tabClosedToken != null)
{
_eventAggregator
.GetEvent<TabClosedEvent>()
.Unsubscribe(_tabClosedToken);
_tabClosedToken = null;
}
if (_refreshTabToken != null)
{
_eventAggregator
.GetEvent<TabRefreshEvent>()
.Unsubscribe(_refreshTabToken);
_refreshTabToken = null;
}
if (_loginOutToken != null)
{
_eventAggregator
.GetEvent<SysUserEvents.LoginOutEvent>()
.Unsubscribe(_loginOutToken);
_loginOutToken = null;
}
if (_authService != null)
{
_authService.UserChanged -= OnUserChanged;
}
if (!_backendConnectivityCts.IsCancellationRequested)
{
_backendConnectivityCts.Cancel();
}
_jeecgUserSyncCoordinator.Stop();
}
}
}