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

593 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using HandyControl.Data;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using YY.Admin.Core;
using YY.Admin.Core.Const;
using YY.Admin.Core.EventBus;
using YY.Admin.Core.Session;
using YY.Admin.Services.Service.Auth;
using YY.Admin.Views;
namespace YY.Admin.ViewModels
{
public class BaseViewModel : BindableBase, IDestructible
{
protected bool _isLoading;
private string _title = string.Empty;
// 添加Token检查定时器
private static DispatcherTimer? _tokenCheckTimer;
/// <summary>
///加载
/// </summary>
public bool IsLoading
{
get => _isLoading;
set
{
if (SetProperty(ref _isLoading, value))
{
// 触发一个虚拟方法,让派生类可以响应
OnIsLoadingChanged();
}
}
}
public bool IsNotLoading => !IsLoading;
/// <summary>
///标题
/// </summary>
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
private readonly IDialogService _dialogService;
protected readonly IRegionManager _regionManager;
/// <summary>
/// 日志对象
/// </summary>
protected ILoggerService _logger;
/// <summary>
/// 依赖注入容器
/// </summary>
protected IContainerExtension _container { get; }
/// <summary>
/// 事件汇总器,用于发布或订阅事件
/// </summary>
protected IEventAggregator _eventAggregator;
/// <summary>
/// 当前已登录用户信息
/// </summary>
protected static UserContext? _userContext { get; set; }
private SkinType _skinType;
public SkinType SkinType { get => _skinType; set => SetProperty(ref _skinType, value); }
private SubscriptionToken? _themeChangedEventToken;
protected BaseViewModel(IContainerExtension container, IRegionManager regionManager)
{
_container = container;
_logger = container.Resolve<ILoggerService>();
_eventAggregator = container.Resolve<IEventAggregator>();
this._dialogService = container.Resolve<IDialogService>();
this._regionManager = regionManager;
_themeChangedEventToken = _eventAggregator.GetEvent<ThemeChangedEvent>().Subscribe(skinType => SkinType = skinType);
}
#region
// 启动定时器的方法
public static void StartTokenCheckTimer()
{
if (_tokenCheckTimer == null)
{
_tokenCheckTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMinutes(1)
};
_tokenCheckTimer.Tick += CheckTokenExpiration;
// 捕获全局用户输入事件
EventManager.RegisterClassHandler(typeof(Window),
UIElement.PreviewMouseDownEvent,
new MouseButtonEventHandler(OnUserActivity));
EventManager.RegisterClassHandler(typeof(Window),
UIElement.PreviewKeyDownEvent,
new KeyEventHandler(OnUserActivity));
}
if (!_tokenCheckTimer.IsEnabled)
{
_tokenCheckTimer.Start();
}
}
// 停止定时器的方法
public static void StopTokenCheckTimer()
{
if (_tokenCheckTimer != null && _tokenCheckTimer.IsEnabled)
{
_tokenCheckTimer.Stop();
}
}
private static void OnUserActivity(object sender, EventArgs e)
{
var authService = ContainerLocator.Current.Resolve<ISysAuthService>();
authService.RefreshToken(UserContext?.Token?.AccessToken);
}
/// <summary>
///定时器检查方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void CheckTokenExpiration(object? sender, EventArgs e)
{
// 确保在主线程执行
Application.Current.Dispatcher.Invoke(() =>
{
if (UserContext == null || UserContext.Token == null)
{
// 如果没有用户信息,停止定时器
StopTokenCheckTimer();
return;
}
var authService = ContainerLocator.Current.Resolve<ISysAuthService>();
if (!authService.ValidateToken(UserContext.Token.AccessToken))
{
// 停止定时器防止重复触发
StopTokenCheckTimer();
// 显示过期提示
Application.Current.Dispatcher.Invoke(() =>
{
var currentWindow = Application.Current.MainWindow;
if (currentWindow != null)
{
var viewModel = currentWindow.DataContext as BaseViewModel;
viewModel?.ForceLogout("您的登录已过期,请重新登录");
}
});
}
});
}
/// <summary>
/// 强制退出方法
/// </summary>
/// <param name="message"></param>
public async void ForceLogout(string message)
{
// 先显示对话框
await ShowAlertAsync(message);
// 发布登出事件
_eventAggregator.GetEvent<SysUserEvents.LoginOutEvent>().Publish(AppSession.CurrentUser!);
Logout();
}
/// <summary>
/// 登出
/// </summary>
public void Logout()
{
// 停止定时器
StopTokenCheckTimer();
// 清除用户上下文
ClearUserContext();
// 再执行退出操作
var authService = _container.Resolve<ISysAuthService>();
authService.LogoutAsync();
// 当前窗口
var mainWindow = Application.Current.MainWindow;
// 跳转到登录页
var loginWindow = _container.Resolve<LoginWindow>();
Application.Current.MainWindow = loginWindow;
loginWindow.Show();
mainWindow.Close();
// 移除所有区域
RemoveAllRegion();
}
public static UserContext? UserContext
{
get => _userContext;
private set
{
if (_userContext != value)
{
_userContext = value;
// 通知静态属性变化(需要额外实现)
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(nameof(UserContext)));
}
}
}
// 静态属性变更通知事件
public static event EventHandler<PropertyChangedEventArgs>? StaticPropertyChanged;
/// <summary>
/// 设置用户上下文
/// </summary>
/// <param name="user"></param>
/// <param name="token"></param>
public static void SetUserContext(SysUser user, UserToken token)
{
UserContext = new UserContext
{
UserId = user.Id,
TenantId = user.TenantId!.Value,
Account = user.Account,
AccountType = user.AccountType,
RealName = user.RealName,
IsSuperAdmin = user.AccountType == AccountTypeEnum.SuperAdmin,
OrgId = user.OrgId,
Token = token
};
}
/// <summary>
/// 清除用户上下文
/// </summary>
public static void ClearUserContext()
{
UserContext = null;
}
#endregion
#region
/// <summary>
/// 导航到指定Page
/// </summary>
/// <param name="regionName">区域名称</param>
/// <param name="target">目标Page名称</param>
/// <param name="navigationCallback">导航回调函数</param>
protected void RequestNavigate(string regionName, string target, Action<NavigationResult> navigationCallback = null)
{
IRegion region = _regionManager.Regions[regionName];
if (region == null) return;
region.RemoveAll();
if (navigationCallback != null)
region.RequestNavigate(target, navigationCallback);
else
region.RequestNavigate(target);
}
protected void SetRegionManager(DependencyObject target)
{
RegionManager.SetRegionManager(target, _regionManager);
}
/// <summary>
/// 安全导航到指定视图
/// </summary>
protected void SafeNavigate(string regionName, string targetView, NavigationParameters parameters = null)
{
ExecuteWithExceptionHandling(() =>
{
// 导航页面
_regionManager.RequestNavigate(regionName, targetView, parameters);
});
}
protected void ExecuteWithExceptionHandling(Action action)
{
try
{
action();
}
catch (Exception ex)
{
HandleException(ex);
}
}
private void HandleException(Exception ex)
{
LogError("操作发生异常", ex);
// ShowErrorDialog($"操作异常: {ex.Message}");
}
#endregion
#region
/// <summary>
/// 弹框提示
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="callback">回调函数</param>
protected void Alert(string message, Action<IDialogResult>? callback = null)
{
_dialogService.ShowDialog("AlertDialog", new DialogParameters($"message={message}"), callback);
}
protected Task ShowAlertAsync(string message)
{
var tcs = new TaskCompletionSource<bool>();
Alert(message,result => tcs.SetResult(true));
return tcs.Task;
}
/// <summary>
/// 弹出消息提示框1秒钟自动关闭
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="messageType">消息类型</param>
/// <param name="callback">回调函数</param>
protected void AlertPopup(string message, MessageTypeEnum messageType = MessageTypeEnum.Success, Action<IDialogResult> callback = null)
{
switch (messageType)
{
case MessageTypeEnum.Success:
_dialogService.ShowDialog("SuccessDialog", new DialogParameters($"message={message}"), callback);
break;
case MessageTypeEnum.Error:
_dialogService.ShowDialog("ErrorDialog", new DialogParameters($"message={message}"), callback);
break;
case MessageTypeEnum.Warning:
_dialogService.ShowDialog("WarningDialog", new DialogParameters($"message={message}"), callback);
break;
default:
_dialogService.ShowDialog("SuccessDialog", new DialogParameters($"message={message}"), callback);
break;
}
}
/// <summary>
/// 确认框提示
/// </summary>
/// <param name="message">确认框消息</param>
/// <param name="callback">回调函数</param>
protected void Confirm(string message, Action<IDialogResult>? callback = null)
{
_dialogService.ShowDialog("ConfirmDialog", new DialogParameters($"message={message}"), callback);
}
/// <summary>
/// 异步确认框
/// </summary>
/// <param name="message">确认消息</param>
/// <returns>用户是否确认</returns>
protected Task<bool> ConfirmAsync(string message)
{
var tcs = new TaskCompletionSource<bool>();
Confirm(message, result =>
{
// 从 IDialogResult 中提取用户选择
var userConfirmed = result.Result == ButtonResult.Yes;
tcs.SetResult(userConfirmed);
});
return tcs.Task;
}
#endregion
#region
protected void LogInfo(string message, [CallerMemberName] string caller = "")
=> _logger.Information($"[{GetType().Name}.{caller}] {message}");
protected void LogWarning(string message, [CallerMemberName] string caller = "")
=> _logger.Warning($"[{GetType().Name}.{caller}] {message}");
protected void LogError(string message, Exception ex = null, [CallerMemberName] string caller = "")
=> _logger.Error($"[{GetType().Name}.{caller}] {message}", ex);
#endregion
/// <summary>
/// 异常处理封装
/// </summary>
/// <param name="asyncAction"></param>
/// <param name="onFinally"></param>
/// <returns></returns>
protected async Task ExecuteAsync(Func<Task> asyncAction, Action? onFinally = null)
{
try
{
IsLoading = true;
await asyncAction();
}
catch (Exception ex)
{
HandleException(ex);
}
finally
{
IsLoading = false;
onFinally?.Invoke();
}
}
/// <summary>
/// 窗口管理
/// </summary>
/// <typeparam name="T"></typeparam>
protected void SwitchMainWindow<T>() where T : Window
{
ExecuteWithExceptionHandling(() =>
{
var currentWindow = Application.Current.MainWindow;
var newWindow = _container.Resolve<T>();
Application.Current.MainWindow?.Hide();
Application.Current.MainWindow = newWindow;
newWindow.Show();
currentWindow?.Close();
});
}
/// <summary>
/// 资源管理
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resourceKey"></param>
/// <returns></returns>
/// <exception cref="ResourceNotFoundException"></exception>
protected T GetResource<T>(string resourceKey)
{
if (Application.Current.TryFindResource(resourceKey) is T resource)
{
return resource;
}
throw new ResourceNotFoundException($"资源未找到: {resourceKey}");
}
/// <summary>
/// 移除区域,包括清空区域内的内容,导航日志
/// </summary>
/// <param name="regionName">区域名称</param>
protected void RemoveRegion(string regionName)
{
if (string.IsNullOrEmpty(regionName))
{
return;
}
if (!_regionManager.Regions.ContainsRegionWithName(regionName))
{
return;
}
var region = _regionManager.Regions[regionName];
RemoveRegion(region);
}
/// <summary>
/// 移除区域,包括清空区域内的内容,导航日志
/// </summary>
/// <param name="region"></param>
protected void RemoveRegion(IRegion region)
{
if (region == null)
{
return;
}
// 清空区域内容
region.RemoveAll();
// 清空导航历史
region.NavigationService?.Journal?.Clear();
// 从区域管理器中移除区域
_regionManager.Regions.Remove(region.Name);
_logger.Debug($"移除区域{region.Name}");
}
/// <summary>
/// 移除所有区域
/// </summary>
protected void RemoveAllRegion()
{
foreach (var region in _regionManager.Regions.ToList())
{
RemoveRegion(region);
}
_logger.Debug($"移除所有区域");
}
/// <summary>
/// 移除区域视图
/// </summary>
/// <param name="regionName">区域名称</param>
/// <param name="viewName">视图名称</param>
protected void RemoveView(string regionName, string? viewName)
{
if (string.IsNullOrEmpty(regionName) || string.IsNullOrEmpty(viewName))
{
return;
}
if (!_regionManager.Regions.ContainsRegionWithName(regionName))
{
return;
}
var region = _regionManager.Regions[regionName];
// 查找并移除指定的视图
var viewToRemove = region.Views.FirstOrDefault(v =>
{
// 根据视图名称或类型来匹配
var viewType = v.GetType();
return viewType.Name == viewName || viewType.Name == viewName + "View";
});
if (viewToRemove != null)
{
region.Remove(viewToRemove);
_logger.Debug($"从区域{regionName}移除视图{viewName}");
}
}
/// <summary>
/// 移除区域ContentRegion视图
/// </summary>
/// <param name="viewName"></param>
protected void RemoveContentRegionView(string? viewName)
{
RemoveView(CommonConst.ContentRegion, viewName);
}
/// <summary>
/// 移除区域所有视图
/// </summary>
/// <param name="regionName">区域名称</param>
protected void RemoveAllView(string regionName)
{
if (string.IsNullOrEmpty(regionName))
{
return;
}
if (!_regionManager.Regions.ContainsRegionWithName(regionName))
{
return;
}
var region = _regionManager.Regions[regionName];
region.RemoveAll();
_logger.Debug($"移除区域{regionName}所有视图");
}
/// <summary>
/// 当 IsLoading 变化时的回调方法
/// </summary>
protected virtual void OnIsLoadingChanged()
{
RaisePropertyChanged(nameof(IsNotLoading));
}
/// <summary>
/// 清理资源
/// </summary>
protected virtual void CleanUp()
{
}
/// <summary>
/// 清空资源
/// 执行时机从Region移除View 或 导航到另一个View时
/// </summary>
public void Destroy()
{
if (_themeChangedEventToken != null)
{
_eventAggregator.GetEvent<ThemeChangedEvent>().Unsubscribe(_themeChangedEventToken);
_themeChangedEventToken = null;
}
// 清理派生类的资源
CleanUp();
}
}
// 自定义异常类型
public class ResourceNotFoundException : Exception
{
public ResourceNotFoundException(string message) : base(message) { }
}
}