更新项目配置,新增设备同步模块,优化WebSocket和Swagger配置,增强SCADA系统的免登录接口,支持数据字典项和登录日志的免登录查询与记录。调整Java编译设置,确保更好的开发体验。
This commit is contained in:
257
yy-admin-master/YY.Admin/ViewModels/AppSettingsViewModel.cs
Normal file
257
yy-admin-master/YY.Admin/ViewModels/AppSettingsViewModel.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
using HandyControl.Themes;
|
||||
using HandyControl.Tools;
|
||||
using Mapster;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Const;
|
||||
using YY.Admin.Core.EventBus;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Core.Model;
|
||||
using YY.Admin.Core.Session;
|
||||
using HcSkinType = HandyControl.Data.SkinType;
|
||||
|
||||
namespace YY.Admin.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统设置
|
||||
/// </summary>
|
||||
public class AppSettingsViewModel : BindableBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 皮肤类型
|
||||
/// </summary>
|
||||
private HcSkinType? _skinType;
|
||||
public HcSkinType? SkinType
|
||||
{
|
||||
get => _skinType ??= GetDefaultSkinType();
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _skinType, value) && value != null)
|
||||
{
|
||||
if (!SyncWithSystem)
|
||||
{
|
||||
UpdateSkin(value.Value);
|
||||
UpdateAppSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 皮肤是否与系统同步
|
||||
/// </summary>
|
||||
private bool _syncWithSystem;
|
||||
public bool SyncWithSystem
|
||||
{
|
||||
get => _syncWithSystem;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _syncWithSystem, value))
|
||||
{
|
||||
RaisePropertyChanged(nameof(IsNotSyncWithSystem));
|
||||
if (value)
|
||||
{
|
||||
SkinType = GetSkinTypeBySystem();
|
||||
UpdateSkin(SkinType.Value);
|
||||
}
|
||||
UpdateAppSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNotSyncWithSystem => !SyncWithSystem;
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示TabControl
|
||||
/// </summary>
|
||||
private bool _isTabControlVisible = true;
|
||||
|
||||
public bool IsTabControlVisible
|
||||
{
|
||||
get => _isTabControlVisible;
|
||||
set {
|
||||
if (SetProperty(ref _isTabControlVisible, value))
|
||||
{
|
||||
UpdateAppSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取默认皮肤类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private HcSkinType GetDefaultSkinType()
|
||||
{
|
||||
return SyncWithSystem ? GetSkinTypeBySystem() : (HcSkinType)Properties.AppSettings.Default.SkinType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据系统主题配置获取对应皮肤类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static HcSkinType GetSkinTypeBySystem()
|
||||
{
|
||||
return SystemHelper.DetermineIfInLightThemeMode()
|
||||
? HcSkinType.Default
|
||||
: HcSkinType.Dark;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从ResourceDictionary中获取皮肤类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static HcSkinType GetSkinType()
|
||||
{
|
||||
var _theme = Application.Current.Resources.MergedDictionaries.OfType<Theme>().FirstOrDefault();
|
||||
if (_theme != null)
|
||||
{
|
||||
return _theme?.Skin ?? HcSkinType.Default;
|
||||
}
|
||||
var skin = Application.Current.Resources.MergedDictionaries
|
||||
.Where(it => it.Source.OriginalString.StartsWith("pack://application:,,,/HandyControl;component/Themes/Skin"))
|
||||
.FirstOrDefault();
|
||||
if (skin != null)
|
||||
{
|
||||
string OriginalString = skin.Source.OriginalString;
|
||||
// 4:Skin的长度
|
||||
int skinStart = OriginalString.IndexOf("Skin") + 4;
|
||||
int dotIndex = OriginalString.LastIndexOf('.');
|
||||
string skinName = OriginalString.Substring(skinStart, dotIndex - skinStart);
|
||||
return (HcSkinType)Enum.Parse(typeof(HcSkinType), skinName);
|
||||
}
|
||||
return HcSkinType.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新皮肤
|
||||
/// </summary>
|
||||
/// <param name="skinType"></param>
|
||||
public static void UpdateSkin(HcSkinType skinType)
|
||||
{
|
||||
// 配置方式:<hc:Theme />
|
||||
var theme = Application.Current.Resources.MergedDictionaries.OfType<Theme>().FirstOrDefault();
|
||||
if (theme != null)
|
||||
{
|
||||
//theme.Skin = skin;
|
||||
theme.MergedDictionaries[0].Source = ResourceHelper.GetSkin(skinType).Source;
|
||||
theme.MergedDictionaries[1].Source = ResourceHelper.GetStandaloneTheme().Source;
|
||||
AddResourceDictionary(skinType);
|
||||
return;
|
||||
}
|
||||
|
||||
// 配置方式:<ResourceDictionary />
|
||||
var skins = Application.Current.Resources.MergedDictionaries
|
||||
.Where(it => it.Source.OriginalString.StartsWith("pack://application:,,,/HandyControl;component/Themes/"))
|
||||
.ToList();
|
||||
if (skins == null || skins.Count < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
skins[0].Source = ResourceHelper.GetSkin(skinType).Source;
|
||||
skins[1].Source = ResourceHelper.GetStandaloneTheme().Source;
|
||||
|
||||
// 添加除HandyControl之外的资源字典
|
||||
AddResourceDictionary(skinType);
|
||||
|
||||
ContainerLocator.Container.Resolve<IEventAggregator>().GetEvent<ThemeChangedEvent>().Publish(skinType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件完整路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetFilePath()
|
||||
{
|
||||
string filePathSuffix = string.Format(CommonConst.AppSettingsFilePath, AppSession.CurrentUser!.Account);
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePathSuffix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新系统设置
|
||||
/// 默认配置由MainWindowViewModel保存
|
||||
/// </summary>
|
||||
/// <param name="appSettingsViewModel">系统设置VM</param>
|
||||
public void UpdateAppSettings()
|
||||
{
|
||||
var filePath = GetFilePath();
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SaveAppSettings(filePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存系统设置
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
public void SaveAppSettings(string? filePath = null)
|
||||
{
|
||||
filePath ??= GetFilePath();
|
||||
var directory = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory!);
|
||||
}
|
||||
var appSettings = this.Adapt<AppSettings>();
|
||||
var json = JsonConvert.SerializeObject(appSettings, Formatting.Indented);
|
||||
// 保存到文件
|
||||
File.WriteAllText(filePath, json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加资源字典
|
||||
/// </summary>
|
||||
/// <param name="skinType"></param>
|
||||
private static void AddResourceDictionary(HcSkinType skinType)
|
||||
{
|
||||
// 先移除上一次主题添加的资源字典,否则会影响当前主题
|
||||
RemoveResourceDictionaryBySource("/Resources/Styles/HandyControl/Colors.xaml");
|
||||
RemoveResourceDictionaryBySource("/Resources/Styles/HandyControl/ColorsDark.xaml");
|
||||
RemoveResourceDictionaryBySource("/Resources/Styles/HandyControl/ColorsViolet.xaml");
|
||||
RemoveResourceDictionaryBySource("/Resources/Styles/HandyControl/Brushes.xaml");
|
||||
if (skinType == HcSkinType.Default)
|
||||
{
|
||||
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
|
||||
{
|
||||
Source = new Uri("/Resources/Styles/HandyControl/Colors.xaml", UriKind.Relative)
|
||||
});
|
||||
} else if (skinType == HcSkinType.Dark)
|
||||
{
|
||||
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
|
||||
{
|
||||
Source = new Uri("/Resources/Styles/HandyControl/ColorsDark.xaml", UriKind.Relative)
|
||||
});
|
||||
} else if (skinType == HcSkinType.Violet)
|
||||
{
|
||||
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
|
||||
{
|
||||
Source = new Uri("/Resources/Styles/HandyControl/ColorsViolet.xaml", UriKind.Relative)
|
||||
});
|
||||
}
|
||||
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
|
||||
{
|
||||
Source = new Uri("/Resources/Styles/HandyControl/Brushes.xaml", UriKind.Relative)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除资源字典
|
||||
/// </summary>
|
||||
/// <param name="sourceEndsWith"></param>
|
||||
private static void RemoveResourceDictionaryBySource(string sourceEndsWith)
|
||||
{
|
||||
var dictToRemove = Application.Current.Resources.MergedDictionaries
|
||||
.FirstOrDefault(d => d.Source?.OriginalString?.EndsWith(sourceEndsWith) == true);
|
||||
|
||||
if (dictToRemove != null)
|
||||
{
|
||||
Application.Current.Resources.MergedDictionaries.Remove(dictToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
592
yy-admin-master/YY.Admin/ViewModels/Base/BaseViewModel.cs
Normal file
592
yy-admin-master/YY.Admin/ViewModels/Base/BaseViewModel.cs
Normal file
@@ -0,0 +1,592 @@
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
376
yy-admin-master/YY.Admin/ViewModels/Control/MenuTreeViewModel.cs
Normal file
376
yy-admin-master/YY.Admin/ViewModels/Control/MenuTreeViewModel.cs
Normal file
@@ -0,0 +1,376 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Util;
|
||||
using YY.Admin.Event;
|
||||
using YY.Admin.Module;
|
||||
using YY.Admin.Services;
|
||||
using YY.Admin.Services.Service.Menu;
|
||||
|
||||
namespace YY.Admin.ViewModels.Control
|
||||
{
|
||||
/// <summary>
|
||||
/// 菜单选项
|
||||
/// </summary>
|
||||
public class MenuItem : TabSource
|
||||
{
|
||||
public MenuItem? Parent { get; set; } // 父节点引用
|
||||
public ObservableCollection<MenuItem> Children { get; set; } = [];
|
||||
|
||||
|
||||
private bool _isExpanded;
|
||||
|
||||
public bool IsExpanded
|
||||
{
|
||||
get => _isExpanded;
|
||||
set => SetProperty(ref _isExpanded, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class MenuTreeViewModel : BaseViewModel
|
||||
{
|
||||
private static readonly Dictionary<string, string> RouteToViewMap = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// 已实现页面:仪表盘
|
||||
["DashboardView"] = "DashboardView",
|
||||
["/dashboard"] = "DashboardView",
|
||||
["/dashboard/index"] = "DashboardView",
|
||||
["/home/index"] = "DashboardView",
|
||||
["dashboard"] = "DashboardView",
|
||||
["home"] = "DashboardView",
|
||||
|
||||
// 已实现页面:账号管理
|
||||
["UserManagementView"] = "UserManagementView",
|
||||
["/system/user"] = "UserManagementView",
|
||||
["/system/user/index"] = "UserManagementView",
|
||||
["sysUser"] = "UserManagementView",
|
||||
|
||||
// 已实现页面:数据字典
|
||||
["DataDictionaryManagementView"] = "DataDictionaryManagementView",
|
||||
["/system/dict"] = "DataDictionaryManagementView",
|
||||
["/system/dict/index"] = "DataDictionaryManagementView",
|
||||
["/platform/dict"] = "DataDictionaryManagementView",
|
||||
["sysDict"] = "DataDictionaryManagementView",
|
||||
|
||||
// 已实现页面:角色管理
|
||||
["RoleManagementView"] = "RoleManagementView",
|
||||
["/system/role"] = "RoleManagementView",
|
||||
["/system/role/index"] = "RoleManagementView",
|
||||
["sysRole"] = "RoleManagementView",
|
||||
|
||||
// 已实现页面:租户管理
|
||||
["TenantManagementView"] = "TenantManagementView",
|
||||
["/system/tenant"] = "TenantManagementView",
|
||||
["/system/tenant/index"] = "TenantManagementView",
|
||||
["/platform/tenant"] = "TenantManagementView",
|
||||
["sysTenant"] = "TenantManagementView"
|
||||
};
|
||||
|
||||
private MenuItem? _selectedMenuItem;
|
||||
|
||||
public MenuItem? SelectedMenuItem
|
||||
{
|
||||
get => _selectedMenuItem;
|
||||
set => SetProperty(ref _selectedMenuItem, value);
|
||||
}
|
||||
|
||||
private readonly ISysMenuService _sysMenuService;
|
||||
|
||||
public ObservableCollection<MenuItem> MenuItems { get; } = [];
|
||||
|
||||
public DelegateCommand<MenuItem> NavigateCommand { get; }
|
||||
|
||||
private SubscriptionToken? tabSelectedToken;
|
||||
private SubscriptionToken? tabClosedToken;
|
||||
|
||||
public MenuTreeViewModel(
|
||||
ISysMenuService sysMenuService,
|
||||
IContainerExtension _container,
|
||||
IRegionManager regionManager) : base(_container, regionManager)
|
||||
{
|
||||
_sysMenuService = sysMenuService;
|
||||
|
||||
// 异步初始化菜单
|
||||
LoadMenuAsync();
|
||||
|
||||
NavigateCommand = new DelegateCommand<MenuItem>(OpenOrActivateTab);
|
||||
|
||||
// 订阅事件
|
||||
tabSelectedToken = _eventAggregator.GetEvent<TabSelectedEvent>().Subscribe(OnTabSelected);
|
||||
tabClosedToken = _eventAggregator.GetEvent<TabClosedEvent>().Subscribe(OnTabClosed);
|
||||
}
|
||||
|
||||
public void OpenOrActivateTab(MenuItem menuItem)
|
||||
{
|
||||
// 发布事件
|
||||
_eventAggregator.GetEvent<TabSourceSelectedEvent>().Publish(menuItem);
|
||||
}
|
||||
|
||||
private void OnTabSelected(TabItemModel tab)
|
||||
{
|
||||
if (tab.TabSource is MenuItem menuItem)
|
||||
{
|
||||
// 取消上一个选中菜单选中状态
|
||||
SelectedMenuItem?.IsSelected = false;
|
||||
|
||||
SelectedMenuItem = menuItem;
|
||||
|
||||
// 设置菜单选中
|
||||
SelectedMenuItem.IsSelected = true;
|
||||
|
||||
// 展开父节点
|
||||
ToggleParents(SelectedMenuItem, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTabClosed(TabItemModel tab)
|
||||
{
|
||||
if (tab.TabSource is MenuItem menuItem)
|
||||
{
|
||||
// 折叠父节点
|
||||
ToggleParents(menuItem, false);
|
||||
|
||||
// 取消菜单选中
|
||||
menuItem?.IsSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载菜单【后续使用缓存以及权限管理】
|
||||
/// </summary>
|
||||
private async void LoadMenuAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 异步获取菜单数据
|
||||
var menuTree = await _sysMenuService.GetLoginMenuTree();
|
||||
// 转换菜单数据
|
||||
ConvertMenuTreeToViewModel(menuTree);
|
||||
// 默认导航
|
||||
ScheduleDefaultNavigation();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"菜单加载失败: {ex.Message}", ex);
|
||||
// 显示错误菜单项
|
||||
MenuItems.Add(new MenuItem
|
||||
{
|
||||
Name = "菜单加载失败",
|
||||
Icon = "ErrorOutline",
|
||||
ViewName = "ErrorView",
|
||||
Children = { new MenuItem { Name = "点击重试", Icon = "Refresh" } }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将服务层菜单树转换为视图模型
|
||||
/// </summary>
|
||||
private void ConvertMenuTreeToViewModel(List<MenuOutput> menuTree)
|
||||
{
|
||||
// 过滤并排序菜单项:只包含目录和菜单类型,排除按钮类型,并按排序号排序
|
||||
var rootMenus = menuTree
|
||||
.Where(m => m.Type == MenuTypeEnum.Dir || m.Type == MenuTypeEnum.Menu)
|
||||
.Where(m => m.Status == StatusEnum.Enable) // 只包含启用状态的菜单
|
||||
.Where(m => !(m?.IsHide ?? false)) // 排除隐藏的菜单
|
||||
.OrderBy(m => m.OrderNo)
|
||||
.ToList();
|
||||
// 递归转换菜单项
|
||||
void ConvertMenu(MenuOutput source, MenuItem target, MenuItem? parent = null)
|
||||
{
|
||||
target.Name = source?.Title ?? source?.Name;
|
||||
target.Icon = ConvertHtmlEntityToUnicode(source?.Icon ?? "");
|
||||
target.ViewName = ResolveViewName(source); // 将菜单路由映射到已注册的WPF视图
|
||||
target.Parent = parent; // 设置父节点
|
||||
|
||||
// 添加子菜单(如果有)
|
||||
if (source.Children != null && source.Children.Any())
|
||||
{
|
||||
// 过滤并排序子菜单
|
||||
var childMenus = source.Children
|
||||
.Where(c => c.Type == MenuTypeEnum.Dir || c.Type == MenuTypeEnum.Menu) // 子菜单支持目录和菜单两种类型
|
||||
.Where(c => c.Status == StatusEnum.Enable)
|
||||
.Where(c => !(c?.IsHide ?? false))
|
||||
.OrderBy(c => c.OrderNo)
|
||||
.ToList();
|
||||
|
||||
foreach (var child in childMenus)
|
||||
{
|
||||
var childItem = new MenuItem();
|
||||
ConvertMenu(child, childItem, target);
|
||||
target.Children.Add(childItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理每个根菜单
|
||||
foreach (var root in rootMenus)
|
||||
{
|
||||
var rootItem = new MenuItem();
|
||||
ConvertMenu(root, rootItem);
|
||||
|
||||
// 如果根菜单是目录但没有子菜单,则不显示
|
||||
if (root.Type == MenuTypeEnum.Dir && !rootItem.Children.Any())
|
||||
continue;
|
||||
|
||||
MenuItems.Add(rootItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析菜单对应的视图名称
|
||||
/// </summary>
|
||||
private string? ResolveViewName(MenuOutput? menu)
|
||||
{
|
||||
if (menu == null)
|
||||
return null;
|
||||
|
||||
// 目录节点不参与内容区导航
|
||||
if (menu.Type == MenuTypeEnum.Dir)
|
||||
return null;
|
||||
|
||||
// 依次尝试 Path / Component / Name / Title,兼容不同来源的菜单数据
|
||||
var candidates = new[]
|
||||
{
|
||||
menu.Path,
|
||||
menu.Component,
|
||||
menu.Name,
|
||||
menu.Title
|
||||
};
|
||||
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(candidate))
|
||||
continue;
|
||||
|
||||
if (RouteToViewMap.TryGetValue(candidate.Trim(), out var viewName))
|
||||
return viewName;
|
||||
}
|
||||
|
||||
// 保留原始Path,若未注册将统一展示NotFoundView
|
||||
return menu.Path;
|
||||
}
|
||||
|
||||
private string ConvertHtmlEntityToUnicode(string htmlEntity)
|
||||
{
|
||||
if (string.IsNullOrEmpty(htmlEntity))
|
||||
return "\ue7c6"; // 默认图标
|
||||
|
||||
return StringUtil.ConvertHtmlEntityToUnicode(htmlEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安排默认导航
|
||||
/// </summary>
|
||||
private void ScheduleDefaultNavigation()
|
||||
{
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 默认菜单
|
||||
var defaultMenuItem = GetFirstLeaf(MenuItems.FirstOrDefault());
|
||||
if (defaultMenuItem != null)
|
||||
{
|
||||
// Tab不允许关闭
|
||||
defaultMenuItem.IsClosable = false;
|
||||
|
||||
// 导航菜单
|
||||
OpenOrActivateTab(defaultMenuItem);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"默认导航失败: {ex.Message}", ex);
|
||||
}
|
||||
}), DispatcherPriority.ApplicationIdle);
|
||||
}
|
||||
|
||||
private MenuItem? GetFirstLeaf(MenuItem? menu)
|
||||
{
|
||||
if (menu == null)
|
||||
return null;
|
||||
|
||||
if (menu.Children == null || menu.Children.Count == 0)
|
||||
return menu; // 自己就是叶子节点
|
||||
|
||||
// 递归向下找第一个叶子
|
||||
return GetFirstLeaf(menu.Children.FirstOrDefault());
|
||||
}
|
||||
|
||||
public void ToggleParents(MenuItem? item, bool IsExpanded)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var parent = item.Parent;
|
||||
while (parent != null)
|
||||
{
|
||||
parent.IsExpanded = IsExpanded; // 展开父节点
|
||||
if (!IsExpanded)
|
||||
{
|
||||
parent.IsSelected = IsExpanded;
|
||||
}
|
||||
parent = parent.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空资源
|
||||
/// </summary>
|
||||
protected override void CleanUp()
|
||||
{
|
||||
if (tabSelectedToken != null)
|
||||
{
|
||||
_eventAggregator
|
||||
.GetEvent<TabSelectedEvent>()
|
||||
.Unsubscribe(tabSelectedToken);
|
||||
tabSelectedToken = null;
|
||||
}
|
||||
|
||||
if (tabClosedToken != null)
|
||||
{
|
||||
_eventAggregator
|
||||
.GetEvent<TabClosedEvent>()
|
||||
.Unsubscribe(tabClosedToken);
|
||||
tabClosedToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据ViewName同步选中对应的菜单项
|
||||
/// </summary>
|
||||
//private MenuItem? GetSelectedMenuItem(string? viewName)
|
||||
//{
|
||||
// if (string.IsNullOrEmpty(viewName))
|
||||
// return null;
|
||||
|
||||
// // 递归查找匹配的菜单项
|
||||
// return FindMenuItemByViewName(MenuItems, viewName);
|
||||
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 递归查找匹配ViewName的菜单项
|
||||
/// </summary>
|
||||
//private MenuItem? FindMenuItemByViewName(ObservableCollection<MenuItem> menuItems, string viewName)
|
||||
//{
|
||||
// foreach (var menuItem in menuItems)
|
||||
// {
|
||||
// // 如果当前菜单项匹配
|
||||
// if (menuItem.ViewName == viewName)
|
||||
// return menuItem;
|
||||
|
||||
// // 递归查找子菜单
|
||||
// if (menuItem.Children?.Count > 0)
|
||||
// {
|
||||
// var found = FindMenuItemByViewName(menuItem.Children, viewName);
|
||||
// if (found != null)
|
||||
// return found;
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YY.Admin.ViewModels.Control
|
||||
{
|
||||
public class PaginationDataGridViewModel<T> : BindableBase
|
||||
{
|
||||
private int _pageIndex = 1;
|
||||
private int _dataCountPerPage = 10;
|
||||
private int _totalCount;
|
||||
private int _maxPageCount;
|
||||
private ObservableCollection<T> _data;
|
||||
//private string _pageInfo;
|
||||
|
||||
public int MyProperty { get; private set; }
|
||||
|
||||
public int PageIndex
|
||||
{
|
||||
get => _pageIndex;
|
||||
set => SetProperty(ref _pageIndex, value);
|
||||
}
|
||||
|
||||
public int DataCountPerPage
|
||||
{
|
||||
get => _dataCountPerPage;
|
||||
set => SetProperty(ref _dataCountPerPage, value);
|
||||
}
|
||||
|
||||
public int TotalCount
|
||||
{
|
||||
get => _totalCount;
|
||||
set => SetProperty(ref _totalCount, value);
|
||||
}
|
||||
|
||||
public int MaxPageCount
|
||||
{
|
||||
get => _maxPageCount;
|
||||
set => SetProperty(ref _maxPageCount, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<T> Data
|
||||
{
|
||||
get => _data;
|
||||
set => SetProperty(ref _data, value);
|
||||
}
|
||||
|
||||
//public string PageInfo
|
||||
//{
|
||||
// get => _pageInfo;
|
||||
// set => SetProperty(ref _pageInfo, value);
|
||||
//}
|
||||
|
||||
// 页面大小选项
|
||||
public Dictionary<int, string> PageSizes { get; } = new Dictionary<int, string>
|
||||
{
|
||||
{ 10, "10条/页" },
|
||||
{ 20, "20条/页" },
|
||||
{ 30, "30条/页" },
|
||||
{ 40, "40条/页" },
|
||||
{ 50, "50条/页" },
|
||||
{ 100, "100条/页" }
|
||||
};
|
||||
|
||||
public DelegateCommand PageUpdatedCmd { get; private set; }
|
||||
|
||||
public DelegateCommand PageSizeUpdatedCmd { get; private set; }
|
||||
|
||||
private Func<Task<(IEnumerable<T> data, int totalCount)>> _fetchData;
|
||||
|
||||
public PaginationDataGridViewModel(Func<Task<(IEnumerable<T> data, int totalCount)>> fetchData)
|
||||
{
|
||||
_fetchData = fetchData;
|
||||
PageUpdatedCmd = new DelegateCommand(async () => await LoadDataAsync());
|
||||
PageSizeUpdatedCmd = new DelegateCommand(async () => await LoadDataAsync());
|
||||
|
||||
// 初始化时加载数据
|
||||
//_ = LoadData();
|
||||
}
|
||||
|
||||
public async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var (data, totalCount) = await _fetchData();
|
||||
Data = new ObservableCollection<T>(data);
|
||||
TotalCount = totalCount;
|
||||
// 通知分页总数变化
|
||||
//RaisePropertyChanged(nameof(MaxPageCount));
|
||||
MaxPageCount = totalCount == 0 ? 1 : (int)Math.Ceiling((double)totalCount / DataCountPerPage);
|
||||
// 更新分页信息
|
||||
//PageInfo = $"共 {_totalCount} 条";
|
||||
} catch (OperationCanceledException)
|
||||
{
|
||||
// 查询被取消,保持表格不变
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
172
yy-admin-master/YY.Admin/ViewModels/DashboardViewModel.cs
Normal file
172
yy-admin-master/YY.Admin/ViewModels/DashboardViewModel.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using LiveChartsCore;
|
||||
using LiveChartsCore.Drawing;
|
||||
using LiveChartsCore.Kernel;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Input;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Services.Service.User;
|
||||
|
||||
namespace YY.Admin.ViewModels
|
||||
{
|
||||
public class StatisticCard
|
||||
{
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
public string Color { get; set; } = string.Empty;
|
||||
public string Trend { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class RecentActivity
|
||||
{
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public DateTime Time { get; set; }
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class DashboardViewModel : BaseViewModel
|
||||
{
|
||||
private readonly ISysUserService _userService;
|
||||
|
||||
public float[] Values1 { get; set; } = FetchVales(0);
|
||||
public float[] Values2 { get; set; } = FetchVales(-0.15f);
|
||||
|
||||
public ICommand PointMeasuredCommand { get; }
|
||||
|
||||
public DashboardViewModel(
|
||||
ISysUserService userService,
|
||||
IContainerExtension _container,
|
||||
IRegionManager regionManager
|
||||
) : base(_container, regionManager)
|
||||
{
|
||||
// _eventAggregator.GetEvent<ThemeChangedEvent>().Subscribe(ApplyTheme);
|
||||
_userService = userService;
|
||||
|
||||
PointMeasuredCommand = new DelegateCommand<ChartPoint>(OnPointMeasured);
|
||||
|
||||
_ = LoadDashboardDataAsync();
|
||||
|
||||
}
|
||||
|
||||
public ObservableCollection<StatisticCard> StatisticCards { get; } = new();
|
||||
public ObservableCollection<RecentActivity> RecentActivities { get; } = new();
|
||||
|
||||
private async Task LoadDashboardDataAsync()
|
||||
{
|
||||
IsLoading = true;
|
||||
|
||||
try
|
||||
{
|
||||
// 加载统计数据
|
||||
var users = await _userService.GetUsersAsync();
|
||||
|
||||
StatisticCards.Clear();
|
||||
StatisticCards.Add(new StatisticCard
|
||||
{
|
||||
Title = "总用户数",
|
||||
Value = users.Count.ToString(),
|
||||
Icon = "UserOutlined",
|
||||
Color = "#1890ff",
|
||||
Trend = "+12%"
|
||||
});
|
||||
StatisticCards.Add(new StatisticCard
|
||||
{
|
||||
Title = "活跃用户",
|
||||
Value = users.Count(u => u.Status== StatusEnum.Enable).ToString(),
|
||||
Icon = "UserCheckOutlined",
|
||||
Color = "#52c41a",
|
||||
Trend = "+8%"
|
||||
});
|
||||
StatisticCards.Add(new StatisticCard
|
||||
{
|
||||
Title = "今日访问",
|
||||
Value = "1,234",
|
||||
Icon = "EyeOutlined",
|
||||
Color = "#faad14",
|
||||
Trend = "+5%"
|
||||
});
|
||||
StatisticCards.Add(new StatisticCard
|
||||
{
|
||||
Title = "系统消息",
|
||||
Value = "56",
|
||||
Icon = "MessageOutlined",
|
||||
Color = "#f5222d",
|
||||
Trend = "+2"
|
||||
});
|
||||
|
||||
// 加载最近活动
|
||||
RecentActivities.Clear();
|
||||
RecentActivities.Add(new RecentActivity
|
||||
{
|
||||
Title = "用户登录",
|
||||
Description = "管理员 admin 登录系统",
|
||||
Time = DateTime.Now.AddMinutes(-5),
|
||||
Icon = "LoginOutlined"
|
||||
});
|
||||
RecentActivities.Add(new RecentActivity
|
||||
{
|
||||
Title = "数据更新",
|
||||
Description = "用户数据已同步更新",
|
||||
Time = DateTime.Now.AddMinutes(-15),
|
||||
Icon = "SyncOutlined"
|
||||
});
|
||||
RecentActivities.Add(new RecentActivity
|
||||
{
|
||||
Title = "系统备份",
|
||||
Description = "数据库备份已完成",
|
||||
Time = DateTime.Now.AddHours(-2),
|
||||
Icon = "DatabaseOutlined"
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static float[] FetchVales(float offset)
|
||||
{
|
||||
var values = new List<float>();
|
||||
|
||||
// the EasingFunctions.BounceInOut, is just
|
||||
// a function that looks nice!
|
||||
|
||||
var fx = EasingFunctions.BounceInOut;
|
||||
var x = 0f;
|
||||
|
||||
while (x <= 1)
|
||||
{
|
||||
values.Add(fx(x + offset));
|
||||
x += 0.025f;
|
||||
}
|
||||
|
||||
return [.. values];
|
||||
}
|
||||
|
||||
private void OnPointMeasured(ChartPoint point)
|
||||
{
|
||||
// each point will have a different delay depending on its index
|
||||
var index = point.Context.Entity.MetaData!.EntityIndex; // the index of the point in the data source
|
||||
var delay = index / (float)Values1.Length;
|
||||
|
||||
// the animation takes a function, that represents the normalized progress of the animation
|
||||
// the parameter is the normalized time of the animation, it goes from 0 to 1
|
||||
// the function must return a value from 0 to 1, where 0 is the initial state
|
||||
// and 1 is the final state
|
||||
var duration = TimeSpan.FromSeconds(3);
|
||||
var animation = new Animation(t => DelayedEase(t, delay), duration);
|
||||
|
||||
point.Context.Visual?.SetTransition(animation);
|
||||
}
|
||||
|
||||
private static float DelayedEase(float t, float delay)
|
||||
{
|
||||
if (t <= delay) return 0f;
|
||||
|
||||
var remappedT = (t - delay) / (1f - delay);
|
||||
var baseEasing = EasingFunctions.BuildCustomElasticOut(1.5f, 0.60f);
|
||||
return baseEasing(Math.Clamp(remappedT, 0f, 1f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Prism.Dialogs;
|
||||
using Prism.Mvvm;
|
||||
using Prism.Commands;
|
||||
using System;
|
||||
|
||||
namespace YY.Admin.ViewModels.Dialogs
|
||||
{
|
||||
public class AlertDialogViewModel : BindableBase, IDialogAware
|
||||
{
|
||||
private string _message;
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => SetProperty(ref _message, value);
|
||||
}
|
||||
|
||||
public DelegateCommand CloseCommand { get; }
|
||||
|
||||
public AlertDialogViewModel()
|
||||
{
|
||||
CloseCommand = new DelegateCommand(() =>
|
||||
{
|
||||
// 调用 RequestClose 属性触发关闭
|
||||
RequestClose.Invoke(new DialogResult(ButtonResult.OK));
|
||||
});
|
||||
}
|
||||
|
||||
public bool CanCloseDialog() => true;
|
||||
|
||||
public void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
Message = parameters.GetValue<string>("message");
|
||||
}
|
||||
|
||||
public void OnDialogClosed() { }
|
||||
|
||||
public string Title => "提示";
|
||||
|
||||
// 这里实现的是属性,不是 event
|
||||
public DialogCloseListener RequestClose { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YY.Admin.ViewModels.Dialogs
|
||||
{
|
||||
public class ConfirmDialogViewModel : BindableBase, IDialogAware
|
||||
{
|
||||
public string Title => "确认";
|
||||
|
||||
private string _message;
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => SetProperty(ref _message, value);
|
||||
}
|
||||
|
||||
// 这是 Prism.Dialogs 版本的 RequestClose
|
||||
public DialogCloseListener RequestClose { get; private set; }
|
||||
|
||||
public DelegateCommand YesCommand { get; }
|
||||
public DelegateCommand NoCommand { get; }
|
||||
|
||||
public ConfirmDialogViewModel()
|
||||
{
|
||||
YesCommand = new DelegateCommand(() => CloseDialog(ButtonResult.Yes));
|
||||
NoCommand = new DelegateCommand(() => CloseDialog(ButtonResult.No));
|
||||
}
|
||||
|
||||
private void CloseDialog(ButtonResult result)
|
||||
{
|
||||
// 触发关闭
|
||||
RequestClose.Invoke(new DialogResult(result));
|
||||
}
|
||||
|
||||
public bool CanCloseDialog() => true;
|
||||
|
||||
public void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
Message = parameters.GetValue<string>("message");
|
||||
}
|
||||
|
||||
public void OnDialogClosed() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Prism.Dialogs;
|
||||
using Prism.Mvvm;
|
||||
using System;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace YY.Admin.ViewModels.Dialogs
|
||||
{
|
||||
public class ErrorDialogViewModel : BindableBase, IDialogAware
|
||||
{
|
||||
public string Title => "错误";
|
||||
|
||||
private string _message;
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => SetProperty(ref _message, value);
|
||||
}
|
||||
|
||||
// 用属性来实现,而不是 event
|
||||
public DialogCloseListener RequestClose { get; private set; }
|
||||
|
||||
public ErrorDialogViewModel()
|
||||
{
|
||||
// 自动关闭定时器(2秒)
|
||||
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
|
||||
timer.Tick += (s, e) =>
|
||||
{
|
||||
timer.Stop();
|
||||
RequestClose.Invoke(new DialogResult(ButtonResult.OK));
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
public bool CanCloseDialog() => true;
|
||||
|
||||
public void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
Message = parameters.GetValue<string>("message");
|
||||
}
|
||||
|
||||
public void OnDialogClosed()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using YY.Admin.Helper;
|
||||
|
||||
namespace YY.Admin.ViewModels.Dialogs
|
||||
{
|
||||
public class ServerSettingsDialogViewModel : BindableBase, IDialogAware
|
||||
{
|
||||
private const string DefaultWebSocketPath = "/websocket/scada-sync";
|
||||
private string _loadedWebSocketUrl = string.Empty;
|
||||
private string _loadedAutoWebSocketUrl = string.Empty;
|
||||
public string Title => "服务器设置";
|
||||
|
||||
private string _ip = "127.0.0.1";
|
||||
public string Ip
|
||||
{
|
||||
get => _ip;
|
||||
set => SetProperty(ref _ip, value);
|
||||
}
|
||||
|
||||
private int _port = 8080;
|
||||
public int Port
|
||||
{
|
||||
get => _port;
|
||||
set => SetProperty(ref _port, value);
|
||||
}
|
||||
|
||||
private string _webSocketUrl = string.Empty;
|
||||
public string WebSocketUrl
|
||||
{
|
||||
get => _webSocketUrl;
|
||||
set => SetProperty(ref _webSocketUrl, value);
|
||||
}
|
||||
|
||||
private string _basePath = "/jeecg-boot";
|
||||
public string BasePath
|
||||
{
|
||||
get => _basePath;
|
||||
set => SetProperty(ref _basePath, value);
|
||||
}
|
||||
|
||||
private string _errorMessage = string.Empty;
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set => SetProperty(ref _errorMessage, value);
|
||||
}
|
||||
|
||||
public DelegateCommand SaveCommand { get; }
|
||||
public DelegateCommand CancelCommand { get; }
|
||||
public DialogCloseListener RequestClose { get; private set; }
|
||||
|
||||
public ServerSettingsDialogViewModel()
|
||||
{
|
||||
SaveCommand = new DelegateCommand(Save);
|
||||
CancelCommand = new DelegateCommand(() => RequestClose.Invoke(new DialogResult(ButtonResult.Cancel)));
|
||||
}
|
||||
|
||||
public bool CanCloseDialog() => true;
|
||||
public void OnDialogClosed() { }
|
||||
|
||||
public void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
var settings = ServerSettingsStore.Load();
|
||||
_loadedAutoWebSocketUrl = ServerSettingsStore.BuildDefaultWebSocketUrl(settings.BaseScheme, settings.Ip, settings.Port, settings.BasePath, settings.WebSocketPath);
|
||||
Ip = settings.Ip;
|
||||
Port = settings.Port;
|
||||
WebSocketUrl = string.IsNullOrWhiteSpace(settings.WebSocketUrl)
|
||||
? _loadedAutoWebSocketUrl
|
||||
: settings.WebSocketUrl;
|
||||
_loadedWebSocketUrl = WebSocketUrl;
|
||||
BasePath = string.IsNullOrWhiteSpace(settings.BasePath) ? "/jeecg-boot" : settings.BasePath;
|
||||
ErrorMessage = string.Empty;
|
||||
}
|
||||
|
||||
private void Save()
|
||||
{
|
||||
ErrorMessage = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(Ip))
|
||||
{
|
||||
ErrorMessage = "IP 不能为空";
|
||||
return;
|
||||
}
|
||||
if (Port <= 0 || Port > 65535)
|
||||
{
|
||||
ErrorMessage = "端口号必须在 1-65535 之间";
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var basePath = BasePath?.Trim() ?? "/jeecg-boot";
|
||||
var shouldAutoRebuildWs = string.IsNullOrWhiteSpace(WebSocketUrl)
|
||||
|| string.Equals(WebSocketUrl.Trim(), _loadedAutoWebSocketUrl, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(WebSocketUrl.Trim(), _loadedWebSocketUrl, StringComparison.OrdinalIgnoreCase);
|
||||
var finalWsUrl = shouldAutoRebuildWs
|
||||
? ServerSettingsStore.BuildDefaultWebSocketUrl("http", Ip.Trim(), Port, basePath, DefaultWebSocketPath)
|
||||
: WebSocketUrl.Trim();
|
||||
|
||||
ServerSettingsStore.Save(new ServerSettingsStore.ServerSettingsModel
|
||||
{
|
||||
Ip = Ip.Trim(),
|
||||
Port = Port,
|
||||
BaseScheme = "http",
|
||||
BasePath = basePath,
|
||||
WebSocketUrl = finalWsUrl,
|
||||
WebSocketPath = DefaultWebSocketPath
|
||||
});
|
||||
RequestClose.Invoke(new DialogResult(ButtonResult.OK));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = $"保存失败:{ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Prism.Dialogs;
|
||||
using Prism.Mvvm;
|
||||
using System;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace YY.Admin.ViewModels.Dialogs
|
||||
{
|
||||
public class SuccessDialogViewModel : BindableBase, IDialogAware
|
||||
{
|
||||
public string Title => "成功";
|
||||
|
||||
private string _message;
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => SetProperty(ref _message, value);
|
||||
}
|
||||
|
||||
// Prism.Dialogs 版本的 RequestClose 是属性,不是 event
|
||||
public DialogCloseListener RequestClose { get; private set; }
|
||||
|
||||
public SuccessDialogViewModel()
|
||||
{
|
||||
// 自动关闭定时器
|
||||
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1.5) };
|
||||
timer.Tick += (s, e) =>
|
||||
{
|
||||
timer.Stop();
|
||||
RequestClose.Invoke(new DialogResult(ButtonResult.OK));
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
public bool CanCloseDialog() => true;
|
||||
|
||||
public void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
Message = parameters.GetValue<string>("message");
|
||||
}
|
||||
|
||||
public void OnDialogClosed()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Prism.Dialogs;
|
||||
using Prism.Mvvm;
|
||||
using Prism.Commands;
|
||||
using System;
|
||||
|
||||
namespace YY.Admin.ViewModels.Dialogs
|
||||
{
|
||||
public class WarningDialogViewModel : BindableBase, IDialogAware
|
||||
{
|
||||
public string Title => "警告";
|
||||
|
||||
private string _message;
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set => SetProperty(ref _message, value);
|
||||
}
|
||||
|
||||
// Prism.Dialogs 版本的 RequestClose 是属性
|
||||
public DialogCloseListener RequestClose { get; private set; }
|
||||
|
||||
public DelegateCommand CloseCommand { get; }
|
||||
|
||||
public WarningDialogViewModel()
|
||||
{
|
||||
CloseCommand = new DelegateCommand(CloseDialog);
|
||||
}
|
||||
|
||||
private void CloseDialog()
|
||||
{
|
||||
RequestClose.Invoke(new DialogResult(ButtonResult.OK));
|
||||
}
|
||||
|
||||
public bool CanCloseDialog() => true;
|
||||
|
||||
public void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
Message = parameters.GetValue<string>("message");
|
||||
}
|
||||
|
||||
public void OnDialogClosed()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
277
yy-admin-master/YY.Admin/ViewModels/LoginWindowViewModel.cs
Normal file
277
yy-admin-master/YY.Admin/ViewModels/LoginWindowViewModel.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Core.Session;
|
||||
using YY.Admin.FluentValidation;
|
||||
using YY.Admin.Services;
|
||||
using YY.Admin.Services.Service.Auth;
|
||||
using YY.Admin.Services.Service.Jeecg;
|
||||
using YY.Admin.Views;
|
||||
|
||||
namespace YY.Admin.ViewModels
|
||||
{
|
||||
// ViewModels/LoginViewModel.cs
|
||||
public class LoginWindowViewModel : BaseViewModel
|
||||
{
|
||||
private readonly ISysAuthService _authService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IJeecgLoginLogReportService _loginLogReportService;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly HttpClient _httpClient = new();
|
||||
private readonly CancellationTokenSource _connectivityCts = new();
|
||||
private const int ConnectivityCheckIntervalSeconds = 5;
|
||||
public LoginInput LoginInput { get; set; }
|
||||
private string _loginMessage = string.Empty;
|
||||
public DelegateCommand LoginCommand { get; }
|
||||
public DelegateCommand SyncJeecgUsersCommand { get; }
|
||||
public DelegateCommand OpenServerSettingsCommand { get; }
|
||||
public LoginInputValidator LoginInputValidator { get; private set; }
|
||||
|
||||
private bool _isSyncingJeecgUsers;
|
||||
/// <summary>
|
||||
/// 正在从 Jeecg 同步用户到本地库
|
||||
/// </summary>
|
||||
public bool IsSyncingJeecgUsers
|
||||
{
|
||||
get => _isSyncingJeecgUsers;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isSyncingJeecgUsers, value))
|
||||
{
|
||||
SyncJeecgUsersCommand.RaiseCanExecuteChanged();
|
||||
LoginCommand.RaiseCanExecuteChanged();
|
||||
RaisePropertyChanged(nameof(SyncJeecgUsersButtonText));
|
||||
RaisePropertyChanged(nameof(CanInteractWithLogin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SyncJeecgUsersButtonText => IsSyncingJeecgUsers ? "同步中..." : "同步 Jeecg 用户";
|
||||
|
||||
/// <summary>登录与同步互斥,用于禁用登录按钮</summary>
|
||||
public bool CanInteractWithLogin => !IsLoading && !IsSyncingJeecgUsers;
|
||||
|
||||
private bool _isBackendConnected;
|
||||
/// <summary>
|
||||
/// 后端连接状态:true=连接中,false=已断开
|
||||
/// </summary>
|
||||
public bool IsBackendConnected
|
||||
{
|
||||
get => _isBackendConnected;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isBackendConnected, value))
|
||||
{
|
||||
RaisePropertyChanged(nameof(BackendConnectionStatusText));
|
||||
RaisePropertyChanged(nameof(BackendConnectionStatusBrush));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string BackendConnectionStatusText => IsBackendConnected ? "后端连接中" : "后端已断开";
|
||||
public Brush BackendConnectionStatusBrush => IsBackendConnected ? Brushes.LimeGreen : Brushes.Red;
|
||||
|
||||
public LoginWindowViewModel(
|
||||
ISysAuthService authService,
|
||||
IDialogService dialogService,
|
||||
IJeecgLoginLogReportService loginLogReportService,
|
||||
IContainerExtension _container,
|
||||
IRegionManager regionManager
|
||||
) : base(_container, regionManager)
|
||||
{
|
||||
_authService = authService;
|
||||
_dialogService = dialogService;
|
||||
_loginLogReportService = loginLogReportService;
|
||||
_configuration = _container.Resolve<IConfiguration>();
|
||||
_loginLogReportService.StartBackgroundSync();
|
||||
Title = "系统登录";
|
||||
|
||||
LoginInput = new LoginInput()
|
||||
{
|
||||
Username = "admin",
|
||||
Password = "123456"
|
||||
};
|
||||
|
||||
LoginInputValidator = new LoginInputValidator();
|
||||
|
||||
LoginCommand = new DelegateCommand(async () => await LoginAsync(), CanLogin)
|
||||
.ObservesProperty(() => IsLoading)
|
||||
.ObservesProperty(() => IsSyncingJeecgUsers);
|
||||
|
||||
SyncJeecgUsersCommand = new DelegateCommand(async () => await SyncJeecgUsersAsync(), CanSyncJeecgUsers)
|
||||
.ObservesProperty(() => IsLoading)
|
||||
.ObservesProperty(() => IsSyncingJeecgUsers);
|
||||
|
||||
OpenServerSettingsCommand = new DelegateCommand(OpenServerSettings);
|
||||
|
||||
_ = StartBackendConnectivityLoopAsync(_connectivityCts.Token);
|
||||
}
|
||||
|
||||
public string LoginMessage
|
||||
{
|
||||
get => _loginMessage;
|
||||
set => SetProperty(ref _loginMessage, value);
|
||||
}
|
||||
|
||||
public string LoginButtonText => IsLoading ? "登录中..." : "登录";
|
||||
|
||||
protected override void OnIsLoadingChanged()
|
||||
{
|
||||
base.OnIsLoadingChanged();
|
||||
RaisePropertyChanged(nameof(LoginButtonText));
|
||||
RaisePropertyChanged(nameof(CanInteractWithLogin));
|
||||
LoginCommand.RaiseCanExecuteChanged();
|
||||
SyncJeecgUsersCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
private bool CanLogin()
|
||||
{
|
||||
return !IsLoading && !IsSyncingJeecgUsers;
|
||||
}
|
||||
|
||||
private bool CanSyncJeecgUsers()
|
||||
{
|
||||
return !IsLoading && !IsSyncingJeecgUsers;
|
||||
}
|
||||
|
||||
private async Task SyncJeecgUsersAsync()
|
||||
{
|
||||
LoginMessage = string.Empty;
|
||||
IsSyncingJeecgUsers = true;
|
||||
try
|
||||
{
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
var (success, message) = await _authService.SyncJeecgUsersToLocalFromLoginScreenAsync();
|
||||
LoginMessage = message;
|
||||
if (success)
|
||||
{
|
||||
_logger?.Information("登录页一键同步 Jeecg 用户成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.Warning($"登录页一键同步 Jeecg 用户:{message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoginMessage = $"同步出错:{ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsSyncingJeecgUsers = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoginAsync()
|
||||
{
|
||||
IsLoading = true;
|
||||
//LoginMessage = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
// 让出线程,让UI先渲染
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
|
||||
var response = await _authService.LoginAsync(LoginInput);
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
_ = _loginLogReportService.ReportLoginAsync(LoginInput.Username, true, "登录成功");
|
||||
_connectivityCts.Cancel();
|
||||
//设置会话
|
||||
AppSession.CurrentUser = response.User;
|
||||
// 设置用户上下文
|
||||
SetUserContext(response.User!, response.Token);
|
||||
// 启动定时器
|
||||
StartTokenCheckTimer();
|
||||
// 登录成功,打开主窗口
|
||||
var loginWindow = Application.Current.MainWindow;
|
||||
var mainWindow = _container.Resolve<MainWindow>();
|
||||
Application.Current.MainWindow = mainWindow;
|
||||
//把窗口和RegionManager 绑定一下即可
|
||||
SetRegionManager(mainWindow);
|
||||
mainWindow.Show();
|
||||
loginWindow.Close();
|
||||
|
||||
// 不等待异步更新用户登录信息,不阻塞主窗口打开
|
||||
_ = _authService.UpdateUserLoginInfoAsync(response.User!);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoginMessage = response.Message;
|
||||
_ = _loginLogReportService.ReportLoginAsync(LoginInput.Username, false, response.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoginMessage = $"登录出错:{ex.Message}";
|
||||
_ = _loginLogReportService.ReportLoginAsync(LoginInput.Username, false, ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartBackendConnectivityLoopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
bool connected = false;
|
||||
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)
|
||||
{
|
||||
LoginMessage = "服务器配置已保存";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
691
yy-admin-master/YY.Admin/ViewModels/MainWindowViewModel.cs
Normal file
691
yy-admin-master/YY.Admin/ViewModels/MainWindowViewModel.cs
Normal file
@@ -0,0 +1,691 @@
|
||||
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.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;
|
||||
|
||||
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);
|
||||
|
||||
// Jeecg 用户增量同步:定时 + 可选 WebSocket(工控机断网续传)
|
||||
_jeecgUserSyncCoordinator.Start();
|
||||
|
||||
// 主窗口底部连接状态圆点
|
||||
_ = 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;
|
||||
|
||||
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(""),
|
||||
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
|
||||
{
|
||||
_logger.Error($"导航失败: {viewName}");
|
||||
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;
|
||||
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 => { });
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
}
|
||||
27
yy-admin-master/YY.Admin/ViewModels/NotFoundViewModel.cs
Normal file
27
yy-admin-master/YY.Admin/ViewModels/NotFoundViewModel.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using YY.Admin.Core.Const;
|
||||
|
||||
namespace YY.Admin.ViewModels
|
||||
{
|
||||
public class NotFoundViewModel : BindableBase
|
||||
{
|
||||
private readonly IRegionManager _regionManager;
|
||||
|
||||
public NotFoundViewModel(IRegionManager regionManager)
|
||||
{
|
||||
_regionManager = regionManager;
|
||||
GoHomeCommand = new DelegateCommand(GoHome);
|
||||
}
|
||||
|
||||
public DelegateCommand GoHomeCommand { get; }
|
||||
|
||||
private void GoHome()
|
||||
{
|
||||
_regionManager.RequestNavigate(CommonConst.ContentRegion, "DashboardView");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using HandyControl.Controls;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Extension;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Services.Service;
|
||||
using YY.Admin.ViewModels.Control;
|
||||
|
||||
namespace YY.Admin.ViewModels.SysManage;
|
||||
|
||||
public class DataDictionaryManagementViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IJeecgDictSyncService _dictSyncService;
|
||||
|
||||
private PaginationDataGridViewModel<JeecgDictItemOutput> _paginationDataGridViewModel;
|
||||
public PaginationDataGridViewModel<JeecgDictItemOutput> PaginationDataGridViewModel
|
||||
{
|
||||
get => _paginationDataGridViewModel;
|
||||
set => SetProperty(ref _paginationDataGridViewModel, value);
|
||||
}
|
||||
|
||||
private PageJeecgDictItemInput _input;
|
||||
public PageJeecgDictItemInput Input
|
||||
{
|
||||
get => _input;
|
||||
set => SetProperty(ref _input, value);
|
||||
}
|
||||
|
||||
public List<KeyValuePair<string, int>> StatusList =>
|
||||
Enum.GetValues(typeof(StatusEnum))
|
||||
.Cast<StatusEnum>()
|
||||
.Select(e => new KeyValuePair<string, int>(e.GetDescription(), (int)e))
|
||||
.ToList();
|
||||
|
||||
public DelegateCommand SearchCommand { get; }
|
||||
public DelegateCommand ResetCommand { get; }
|
||||
public DelegateCommand SyncCommand { get; }
|
||||
|
||||
public DataDictionaryManagementViewModel(
|
||||
IJeecgDictSyncService dictSyncService,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_dictSyncService = dictSyncService;
|
||||
_paginationDataGridViewModel = new PaginationDataGridViewModel<JeecgDictItemOutput>(FetchAsync);
|
||||
_input = new PageJeecgDictItemInput();
|
||||
|
||||
SearchCommand = new DelegateCommand(async () => await SearchAsync());
|
||||
ResetCommand = new DelegateCommand(async () => await ResetAsync());
|
||||
SyncCommand = new DelegateCommand(async () => await SyncAsync());
|
||||
|
||||
_ = InitializeAsync();
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}
|
||||
|
||||
private async Task<(IEnumerable<JeecgDictItemOutput> data, int totalCount)> FetchAsync()
|
||||
{
|
||||
Input.Page = PaginationDataGridViewModel.PageIndex;
|
||||
Input.PageSize = PaginationDataGridViewModel.DataCountPerPage;
|
||||
var result = await _dictSyncService.PageAsync(Input);
|
||||
return (result.Items, result.Total);
|
||||
}
|
||||
|
||||
private async Task SearchAsync()
|
||||
{
|
||||
PaginationDataGridViewModel.PageIndex = 1;
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}
|
||||
|
||||
private async Task ResetAsync()
|
||||
{
|
||||
Input = new PageJeecgDictItemInput();
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
await SearchAsync();
|
||||
}
|
||||
|
||||
private async Task SyncAsync()
|
||||
{
|
||||
var count = await _dictSyncService.SyncFromJeecgAsync();
|
||||
if (count > 0)
|
||||
{
|
||||
Growl.Success($"同步完成,共处理 {count} 条数据字典项");
|
||||
}
|
||||
else
|
||||
{
|
||||
Growl.Warning("未同步到数据字典,请确认后端可访问");
|
||||
}
|
||||
await SearchAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using YY.Admin.Services.Service.User;
|
||||
using YY.Admin.Services.Service;
|
||||
using YY.Admin.ViewModels.Control;
|
||||
|
||||
namespace YY.Admin.ViewModels.SysManage
|
||||
{
|
||||
public class RoleManagementViewModel : BaseViewModel
|
||||
{
|
||||
public DelegateCommand AlertDialogCommand { get; }
|
||||
public DelegateCommand ConfirmDialogCommand { get; }
|
||||
public DelegateCommand ErrorDialogCommand { get; }
|
||||
public DelegateCommand SuccessDialogCommand { get; }
|
||||
public DelegateCommand WarningDialogCommand { get; }
|
||||
|
||||
public RoleManagementViewModel(
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager
|
||||
) : base(container, regionManager)
|
||||
{
|
||||
AlertDialogCommand = new DelegateCommand( () => AlertDialogAsync());
|
||||
ConfirmDialogCommand = new DelegateCommand( () => ConfirmDialogAsync());
|
||||
ErrorDialogCommand = new DelegateCommand( () => ErrorDialogAsync());
|
||||
SuccessDialogCommand = new DelegateCommand( () => SuccessDialogAsync());
|
||||
WarningDialogCommand = new DelegateCommand( () => WarningDialogAsync());
|
||||
}
|
||||
|
||||
private void WarningDialogAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void SuccessDialogAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void ErrorDialogAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void ConfirmDialogAsync()
|
||||
{
|
||||
base.Confirm("测试Confirm");
|
||||
}
|
||||
|
||||
private void AlertDialogAsync()
|
||||
{
|
||||
base.Alert("测试Alert");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using HandyControl.Controls;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Extension;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Services.Service;
|
||||
using YY.Admin.Services.Service.Tenant;
|
||||
using YY.Admin.ViewModels.Control;
|
||||
|
||||
namespace YY.Admin.ViewModels.SysManage
|
||||
{
|
||||
public class TenantManagementViewModel : BaseViewModel
|
||||
{
|
||||
private readonly ISysTenantSyncService _tenantSyncService;
|
||||
|
||||
private PaginationDataGridViewModel<TenantOutput> _paginationDataGridViewModel;
|
||||
public PaginationDataGridViewModel<TenantOutput> PaginationDataGridViewModel
|
||||
{
|
||||
get => _paginationDataGridViewModel;
|
||||
set => SetProperty(ref _paginationDataGridViewModel, value);
|
||||
}
|
||||
|
||||
private PageTenantInput _input;
|
||||
public PageTenantInput Input
|
||||
{
|
||||
get => _input;
|
||||
set => SetProperty(ref _input, value);
|
||||
}
|
||||
|
||||
public List<KeyValuePair<string, int>> StatusList =>
|
||||
Enum.GetValues(typeof(StatusEnum))
|
||||
.Cast<StatusEnum>()
|
||||
.Select(e => new KeyValuePair<string, int>(e.GetDescription(), (int)e))
|
||||
.ToList();
|
||||
|
||||
public DelegateCommand SearchCommand { get; }
|
||||
public DelegateCommand ResetCommand { get; }
|
||||
public DelegateCommand SyncCommand { get; }
|
||||
|
||||
public TenantManagementViewModel(
|
||||
ISysTenantSyncService tenantSyncService,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_tenantSyncService = tenantSyncService;
|
||||
_paginationDataGridViewModel = new PaginationDataGridViewModel<TenantOutput>(FetchTenantsAsync);
|
||||
_input = new PageTenantInput();
|
||||
|
||||
SearchCommand = new DelegateCommand(async () => await SearchAsync());
|
||||
ResetCommand = new DelegateCommand(async () => await ResetAsync());
|
||||
SyncCommand = new DelegateCommand(async () => await SyncAsync());
|
||||
|
||||
_ = InitializeAsync();
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}
|
||||
|
||||
private async Task<(IEnumerable<TenantOutput> data, int totalCount)> FetchTenantsAsync()
|
||||
{
|
||||
Input.Page = PaginationDataGridViewModel.PageIndex;
|
||||
Input.PageSize = PaginationDataGridViewModel.DataCountPerPage;
|
||||
var result = await _tenantSyncService.PageAsync(Input);
|
||||
return (result.Items, result.Total);
|
||||
}
|
||||
|
||||
private async Task SearchAsync()
|
||||
{
|
||||
PaginationDataGridViewModel.PageIndex = 1;
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}
|
||||
|
||||
private async Task ResetAsync()
|
||||
{
|
||||
Input = new PageTenantInput();
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
await SearchAsync();
|
||||
}
|
||||
|
||||
private async Task SyncAsync()
|
||||
{
|
||||
var count = await _tenantSyncService.SyncFromJeecgAsync();
|
||||
if (count > 0)
|
||||
{
|
||||
Growl.Success($"同步完成,共处理 {count} 条租户数据");
|
||||
}
|
||||
else
|
||||
{
|
||||
Growl.Warning("未同步到租户数据,请确认已使用Jeecg账号登录且后端可访问");
|
||||
}
|
||||
await SearchAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using HandyControl.Controls;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Tools.Extension;
|
||||
using System.Collections.ObjectModel;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.FluentValidation;
|
||||
using YY.Admin.Services.Service;
|
||||
using YY.Admin.Services.Service.User;
|
||||
|
||||
namespace YY.Admin.ViewModels.SysManage
|
||||
{
|
||||
public class UserEditDialogViewModel : BaseViewModel, IDialogResultable<bool>
|
||||
{
|
||||
private SysUser? _sysUser;
|
||||
private readonly ISysUserService _sysUserService;
|
||||
|
||||
public SysUserValidator SysUserValidator { get; private set; }
|
||||
|
||||
public SysUser? SysUser
|
||||
{
|
||||
get => _sysUser;
|
||||
set {
|
||||
SetProperty(ref _sysUser, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAddMode => SysUser?.Id == 0;
|
||||
|
||||
public string DialogTitle => IsAddMode ? "新增用户" : "编辑用户";
|
||||
|
||||
// 性别枚举列表
|
||||
//public List<KeyValuePair<string, int>> GenderList =>
|
||||
// Enum.GetValues(typeof(GenderEnum))
|
||||
// .Cast<GenderEnum>()
|
||||
// .Select(e => new KeyValuePair<string, int>(e.GetDescription(), (int)e))
|
||||
// .ToList();
|
||||
|
||||
public ObservableCollection<GenderEnum> GenderList { get; }
|
||||
= new ObservableCollection<GenderEnum>(Enum.GetValues(typeof(GenderEnum)).Cast<GenderEnum>().ToArray());
|
||||
|
||||
public ObservableCollection<StatusEnum> StatusOptions { get; }
|
||||
= new ObservableCollection<StatusEnum>(Enum.GetValues(typeof(StatusEnum)).Cast<StatusEnum>().ToArray());
|
||||
|
||||
//// 状态枚举列表
|
||||
//public List<KeyValuePair<string, int>> StatusList =>
|
||||
// Enum.GetValues(typeof(StatusEnum))
|
||||
// .Cast<StatusEnum>()
|
||||
// .Select(e => new KeyValuePair<string, int>(e.GetDescription(), (int)e))
|
||||
// .ToList();
|
||||
|
||||
public DelegateCommand SaveCommand { get; private set; }
|
||||
public DelegateCommand CancelCommand { get; private set; }
|
||||
|
||||
public DelegateCommand<object> StatusSelectedCommand { get; }
|
||||
|
||||
private bool _result;
|
||||
public bool Result { get => _result; set => SetProperty(ref _result, value); }
|
||||
public Action? CloseAction { get; set; }
|
||||
|
||||
public UserEditDialogViewModel(
|
||||
ISysUserService sysUserService,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_sysUserService = sysUserService;
|
||||
SysUserValidator = new SysUserValidator(sysUserService);
|
||||
//SysUser = new SysUser();
|
||||
|
||||
StatusSelectedCommand = new DelegateCommand<object>(OnStatusSelected);
|
||||
|
||||
SaveCommand = new DelegateCommand(async () => await SaveUserAsync());
|
||||
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
|
||||
}
|
||||
|
||||
private void OnStatusSelected(object statusObj)
|
||||
{
|
||||
if (statusObj is StatusEnum status)
|
||||
{
|
||||
SysUser?.Status = status;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveUserAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsAddMode)
|
||||
{
|
||||
var result = await _sysUserService.CreateAsync(SysUser!);
|
||||
Result = result > 0;
|
||||
if (Result)
|
||||
{
|
||||
Growl.Success(new GrowlInfo
|
||||
{
|
||||
Message = "新增用户成功!",
|
||||
ShowDateTime = false,
|
||||
WaitTime = 1
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Growl.Error("新增用户失败!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await _sysUserService.UpdateAsync(SysUser!);
|
||||
Result = result > 0;
|
||||
if (Result)
|
||||
{
|
||||
Growl.Success("修改用户成功!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Growl.Error("修改用户失败!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭对话框
|
||||
CloseAction?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Growl.Error($"操作失败:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化编辑数据
|
||||
public void InitializeForEdit(UserOutput user)
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
SysUser = new SysUser
|
||||
{
|
||||
Id = user.Id,
|
||||
//Account = user.Account,
|
||||
RealName = user.RealName,
|
||||
NickName = user.NickName,
|
||||
//Phone = user.Phone,
|
||||
Sex = user.Sex,
|
||||
Birthday = user.Birthday,
|
||||
Age = user.Age,
|
||||
//AccountType = user.AccountType,
|
||||
Status = user.Status
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化新增数据
|
||||
public void InitializeForAdd()
|
||||
{
|
||||
SysUser = new SysUser();
|
||||
//IsAddMode = true;
|
||||
RaisePropertyChanged(nameof(IsAddMode));
|
||||
RaisePropertyChanged(nameof(DialogTitle));
|
||||
}
|
||||
|
||||
//private readonly SysUserValidator _addValidator = new SysUserValidator(true);
|
||||
//private readonly SysUserValidator _editValidator = new SysUserValidator(false);
|
||||
|
||||
//public string this[string columnName]
|
||||
//{
|
||||
// get
|
||||
// {
|
||||
// if (SysUser == null) return string.Empty;
|
||||
|
||||
// var validator = IsAddMode ? _addValidator : _editValidator;
|
||||
// var result = validator.Validate(SysUser);
|
||||
|
||||
// var error = result.Errors.FirstOrDefault(e => e.PropertyName == columnName);
|
||||
// return error?.ErrorMessage;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
using HandyControl.Controls;
|
||||
using HandyControl.Tools.Extension;
|
||||
using SqlSugar;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Extension;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Services.Service;
|
||||
using YY.Admin.Services.Service.User;
|
||||
using YY.Admin.ViewModels.Control;
|
||||
using YY.Admin.Views.SysManage;
|
||||
|
||||
namespace YY.Admin.ViewModels.SysManage
|
||||
{
|
||||
public class UserManagementViewModel : BaseViewModel
|
||||
{
|
||||
private PaginationDataGridViewModel<UserOutput> _paginationDataGridViewModel;
|
||||
|
||||
private PageUserInput _userInput;
|
||||
|
||||
private readonly ISysUserService _sysUserService;
|
||||
|
||||
private readonly IDialogService _dialogService;
|
||||
private SubscriptionToken? _jeecgSyncToken;
|
||||
public PaginationDataGridViewModel<UserOutput> PaginationDataGridViewModel
|
||||
{
|
||||
get => _paginationDataGridViewModel;
|
||||
set => SetProperty(ref _paginationDataGridViewModel, value);
|
||||
}
|
||||
|
||||
|
||||
public PageUserInput UserInput
|
||||
{ get => _userInput;
|
||||
set => SetProperty(ref _userInput, value);
|
||||
}
|
||||
|
||||
// 性别枚举列表属性
|
||||
public List<GenderEnum> GenderList =>
|
||||
[.. Enum.GetValues(typeof(GenderEnum)).Cast<GenderEnum>()];
|
||||
|
||||
// 状态枚举列表属性
|
||||
public List<KeyValuePair<string, int>> StatusList =>
|
||||
Enum.GetValues(typeof(StatusEnum))
|
||||
.Cast<StatusEnum>()
|
||||
.Select(e => new KeyValuePair<string, int>(e.GetDescription(), (int)e))
|
||||
.ToList();
|
||||
|
||||
// 全选
|
||||
//private bool? _isAllSelected = false;
|
||||
//public bool? IsAllSelected
|
||||
//{
|
||||
// get => _isAllSelected;
|
||||
// set
|
||||
// {
|
||||
// if (SetProperty(ref _isAllSelected, value) && value.HasValue)
|
||||
// {
|
||||
// // 只有当值真正改变时才执行全选逻辑
|
||||
// SelectAll(value.Value);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// 选中用户数量
|
||||
private int _selectedCount;
|
||||
public int SelectedCount
|
||||
{
|
||||
get => _selectedCount;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedCount, value))
|
||||
{
|
||||
// 当 SelectedCount 变化时,触发 HasSelectedItems 通知
|
||||
RaisePropertyChanged(nameof(HasSelectedItems));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 是否有选中项
|
||||
public bool HasSelectedItems => SelectedCount > 0;
|
||||
|
||||
// 批量删除
|
||||
public DelegateCommand BatchDeleteCommand { get; private set; }
|
||||
|
||||
// 单个删除
|
||||
public DelegateCommand<long?> DeleteCommand { get; private set; }
|
||||
|
||||
// 查询
|
||||
public DelegateCommand SearchCommand { get; private set; }
|
||||
|
||||
public DelegateCommand ResetCommand { get; private set; }
|
||||
|
||||
public DelegateCommand AddCommand { get; private set; }
|
||||
public DelegateCommand<UserOutput> EditCommand { get; private set; }
|
||||
|
||||
public DelegateCommand<UserOutput> StatusToggleCommand { get; private set; }
|
||||
|
||||
|
||||
// 行选择改变命令
|
||||
//public DelegateCommand<UserOutput> RowSelectionChangedCommand { get; private set; }
|
||||
|
||||
public UserManagementViewModel(
|
||||
ISysUserService sysUserService,
|
||||
IContainerExtension container,
|
||||
IDialogService dialogService,
|
||||
IRegionManager regionManager
|
||||
) : base(container, regionManager)
|
||||
{
|
||||
_sysUserService= sysUserService;
|
||||
_dialogService = dialogService;
|
||||
// 创建分页控件的 ViewModel,传递一个获取数据的委托
|
||||
_paginationDataGridViewModel = new PaginationDataGridViewModel<UserOutput>(FetchUsersAsync);
|
||||
_userInput = new PageUserInput();
|
||||
|
||||
// 初始化批量删除命令
|
||||
BatchDeleteCommand = new DelegateCommand(async () => await BatchDelete(),
|
||||
() => HasSelectedItems);
|
||||
|
||||
DeleteCommand = new DelegateCommand<long?>(async (id) => {
|
||||
if (id.HasValue)
|
||||
{
|
||||
await Delete(id.Value);
|
||||
}
|
||||
});
|
||||
|
||||
SearchCommand = new DelegateCommand(async () => await ReadAsync());
|
||||
|
||||
ResetCommand = new DelegateCommand(async () => await ResetFormAsync());
|
||||
|
||||
AddCommand = new DelegateCommand(async () => await ShowAddDialog());
|
||||
|
||||
EditCommand = new DelegateCommand<UserOutput>(async (user) => await ShowEditDialog(user));
|
||||
|
||||
StatusToggleCommand = new DelegateCommand<UserOutput>(async (user) => await ToggleStatus(user));
|
||||
|
||||
//RowSelectionChangedCommand = new DelegateCommand<UserOutput>(OnRowSelectionChanged);
|
||||
|
||||
// 监听数据变化
|
||||
//PaginationDataGridViewModel.PropertyChanged += OnPaginationDataChanged;
|
||||
//_ = PaginationDataGridViewModel.LoadDataAsync(); // 默认加载第一页数据
|
||||
|
||||
//_dataList = GetDemoDataList(10);
|
||||
|
||||
// 启动异步初始化,但不等待
|
||||
_ = InitializeAsync();
|
||||
|
||||
// Jeecg 同构用户表同步完成后,自动刷新当前列表
|
||||
_jeecgSyncToken = _eventAggregator
|
||||
.GetEvent<SysUserEvents.JeecgMirrorUsersSyncedEvent>()
|
||||
.Subscribe(async _ =>
|
||||
{
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}, ThreadOption.UIThread);
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 不阻塞UI线程,让UI先渲染(效果就是先看到界面,然后再加载表格数据,提升用户体验)
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
|
||||
// 然后异步加载数据
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"初始化失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CleanUp()
|
||||
{
|
||||
base.CleanUp();
|
||||
if (_jeecgSyncToken != null)
|
||||
{
|
||||
_eventAggregator.GetEvent<SysUserEvents.JeecgMirrorUsersSyncedEvent>().Unsubscribe(_jeecgSyncToken);
|
||||
_jeecgSyncToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 行选择改变事件处理
|
||||
//private void OnRowSelectionChanged(UserOutput user)
|
||||
//{
|
||||
// if (user == null) return;
|
||||
|
||||
// user.IsSelected = !user.IsSelected;
|
||||
|
||||
// // 更新选中数量
|
||||
// UpdateSelectedCount();
|
||||
|
||||
// // 更新全选状态
|
||||
// UpdateSelectAllStatus();
|
||||
|
||||
// // 更新批量删除命令状态
|
||||
// BatchDeleteCommand.RaiseCanExecuteChanged();
|
||||
|
||||
// // 调试输出
|
||||
// //System.Diagnostics.Debug.WriteLine($"用户 {user.UserName} 选中状态: {user.IsSelected}");
|
||||
//}
|
||||
|
||||
|
||||
//private void OnPaginationDataChanged(object sender, PropertyChangedEventArgs e)
|
||||
//{
|
||||
// if (e.PropertyName == nameof(PaginationDataGridViewModel.Data))
|
||||
// {
|
||||
// RegisterSelectionEvents();
|
||||
// UpdateSelectedCount();
|
||||
// }
|
||||
//}
|
||||
|
||||
// 注册选择事件监听
|
||||
//private void RegisterSelectionEvents()
|
||||
//{
|
||||
// if (PaginationDataGridViewModel.Data == null) return;
|
||||
|
||||
// foreach (var user in PaginationDataGridViewModel.Data.OfType<UserOutput>())
|
||||
// {
|
||||
// user.SelectionChanged -= OnUserSelectionChanged;
|
||||
// user.SelectionChanged += OnUserSelectionChanged;
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void OnUserSelectionChanged(object sender, EventArgs e)
|
||||
//{
|
||||
// UpdateSelectedCount();
|
||||
// // 更新全选状态
|
||||
// UpdateSelectAllStatus();
|
||||
// BatchDeleteCommand.RaiseCanExecuteChanged();
|
||||
//}
|
||||
|
||||
|
||||
public void UpdateSelectionState()
|
||||
{
|
||||
UpdateSelectedCount();
|
||||
//UpdateSelectAllStatus();
|
||||
BatchDeleteCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
// 更新选中数量
|
||||
private void UpdateSelectedCount()
|
||||
{
|
||||
if (PaginationDataGridViewModel.Data == null)
|
||||
{
|
||||
SelectedCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedCount = PaginationDataGridViewModel.Data.Count(user =>
|
||||
user is UserOutput output && output.IsSelected);
|
||||
}
|
||||
|
||||
// 获取选中的用户ID列表
|
||||
private List<long> GetSelectedUserIds()
|
||||
{
|
||||
if (PaginationDataGridViewModel.Data == null)
|
||||
return new List<long>();
|
||||
|
||||
return PaginationDataGridViewModel.Data
|
||||
.Where(user => user is UserOutput output && output.IsSelected)
|
||||
.Select(user => (user as UserOutput).Id)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// 批量删除执行方法
|
||||
private async Task BatchDelete()
|
||||
{
|
||||
var selectedUserIds = GetSelectedUserIds();
|
||||
if (selectedUserIds.Count == 0) return;
|
||||
|
||||
var messageBoxResult = HandyControl.Controls.MessageBox.Show(
|
||||
$"确定要删除选中的 {selectedUserIds.Count} 个用户吗?此操作不可恢复!",
|
||||
"确认删除",
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (messageBoxResult != MessageBoxResult.OK) return;
|
||||
|
||||
int count = await _sysUserService.BatchDeleteAsync(selectedUserIds);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
PaginationDataGridViewModel.PageIndex = 1;
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}
|
||||
|
||||
// 1. 准备传递给对话框的参数
|
||||
//var parameters = new DialogParameters();
|
||||
//parameters.Add("Title", "操作确认");
|
||||
//parameters.Add("Message", "确定要执行此操作吗?");
|
||||
//parameters.Add("ConfirmButtonText", "确认"); // 可选:自定义确认按钮文本
|
||||
//parameters.Add("CancelButtonText", "取消"); // 可选:自定义取消按钮文本
|
||||
|
||||
// 2. 显示对话框并处理返回结果
|
||||
//_dialogService.ShowDialog(
|
||||
// "ConfirmDialog", // 注册时使用的对话框名称
|
||||
// parameters,
|
||||
// result =>
|
||||
// {
|
||||
// // 3. 根据对话框返回结果执行后续逻辑
|
||||
// if (result.Result == ButtonResult.OK)
|
||||
// {
|
||||
// // 用户点击了确认按钮
|
||||
// //ExecuteConfirmedAction();
|
||||
// }
|
||||
// else if (result.Result == ButtonResult.Cancel)
|
||||
// {
|
||||
// // 用户点击了取消按钮
|
||||
// //ExecuteCancelledAction();
|
||||
// }
|
||||
// }
|
||||
//);
|
||||
}
|
||||
|
||||
// 删除执行方法
|
||||
private async Task Delete(long id)
|
||||
{
|
||||
var messageBoxResult = HandyControl.Controls.MessageBox.Show(
|
||||
$"确定删除ID为 {id} 的用户吗?此操作不可恢复!",
|
||||
"确认删除",
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Question,
|
||||
MessageBoxResult.No);
|
||||
|
||||
if (messageBoxResult != MessageBoxResult.OK) return;
|
||||
|
||||
int count = await _sysUserService.DeleteAsync(id);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
PaginationDataGridViewModel.PageIndex = 1;
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(IEnumerable<UserOutput> data, int totalCount)> FetchUsersAsync()
|
||||
{
|
||||
if (UserInput.EndTime.HasValue && UserInput.EndTime.HasValue && UserInput.EndTime < UserInput.BeginTime)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Error("开始时间不能大于结束时间!");
|
||||
//return (PaginationDataGridViewModel.Data, PaginationDataGridViewModel.TotalCount);
|
||||
throw new OperationCanceledException("时间条件不合法,取消查询");
|
||||
}
|
||||
UserInput.Page = PaginationDataGridViewModel.PageIndex;
|
||||
UserInput.PageSize = PaginationDataGridViewModel.DataCountPerPage;
|
||||
|
||||
// 延迟10秒
|
||||
//await Task.Delay(10000);
|
||||
|
||||
var rlt = await _sysUserService.PageAsync(UserInput);
|
||||
return (rlt.Items, rlt.Total); // 返回分页数据和总条目数
|
||||
}
|
||||
|
||||
private async Task ReadAsync()
|
||||
{
|
||||
PaginationDataGridViewModel.PageIndex = 1;
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
|
||||
}
|
||||
|
||||
public async Task ResetFormAsync()
|
||||
{
|
||||
UserInput = new PageUserInput();
|
||||
|
||||
// 立即更新UI
|
||||
await UIHelper.WaitForRenderAsync();
|
||||
|
||||
await ReadAsync();
|
||||
}
|
||||
|
||||
// 显示新增对话框
|
||||
private async Task ShowAddDialog()
|
||||
{
|
||||
try
|
||||
{
|
||||
//var vm = new UserEditDialogViewModel(_sysUserService, _container);
|
||||
//var view = new UserEditDialogView { DataContext = vm };
|
||||
|
||||
var result = await HandyControl.Controls.Dialog.Show<UserEditDialogView>()
|
||||
.Initialize<UserEditDialogViewModel>(v => v.InitializeForAdd())
|
||||
.GetResultAsync<bool>();
|
||||
// 使用泛型方式,通过 Initialize 方法设置数据
|
||||
//var result = await HandyControl.Controls.Dialog.Show<UserEditDialogView>()
|
||||
// .Initialize<UserEditDialogViewModel>(vm =>
|
||||
// {
|
||||
// vm.InitializeForAdd();
|
||||
// })
|
||||
// .GetResultAsync<bool?>();
|
||||
|
||||
if (result)
|
||||
{
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
//Growl.Success("用户新增成功!");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Growl.Error($"打开对话框失败:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 显示编辑对话框
|
||||
private async Task ShowEditDialog(UserOutput user)
|
||||
{
|
||||
if (user == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var result = await HandyControl.Controls.Dialog.Show<UserEditDialogView>()
|
||||
.Initialize<UserEditDialogViewModel>(vm =>
|
||||
{
|
||||
vm.InitializeForEdit(user);
|
||||
})
|
||||
.GetResultAsync<bool>();
|
||||
|
||||
if (result)
|
||||
{
|
||||
await PaginationDataGridViewModel.LoadDataAsync();
|
||||
//Growl.Success("用户修改成功!");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Growl.Error($"打开对话框失败:{ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ToggleStatus(UserOutput user)
|
||||
{
|
||||
if (user == null) return;
|
||||
|
||||
// 保存原始状态以便回滚
|
||||
var originalStatus = user.Status;
|
||||
try
|
||||
{
|
||||
// 先切换本地状态(提供即时反馈)
|
||||
user.Status = originalStatus == StatusEnum.Enable
|
||||
? StatusEnum.Disable
|
||||
: StatusEnum.Enable;
|
||||
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.Id = user.Id;
|
||||
sysUser.Status = user.Status;
|
||||
|
||||
int count = await _sysUserService.ToggleStatus(sysUser);
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
// 如果服务调用失败,回滚状态
|
||||
user.Status = originalStatus;
|
||||
Growl.Warning("状态切换失败");
|
||||
}
|
||||
} catch (Exception)
|
||||
{
|
||||
user.Status = originalStatus;
|
||||
Growl.Warning("状态切换失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 全选/取消全选方法
|
||||
//private void SelectAll(bool isSelected)
|
||||
//{
|
||||
// if (PaginationDataGridViewModel?.Data == null) return;
|
||||
|
||||
// foreach (var user in PaginationDataGridViewModel.Data.OfType<UserOutput>())
|
||||
// {
|
||||
// user.IsSelected = isSelected;
|
||||
// }
|
||||
|
||||
// // 更新选中数量
|
||||
// UpdateSelectedCount();
|
||||
//}
|
||||
|
||||
// 更新全选状态(根据当前选中情况)
|
||||
//public void UpdateSelectAllStatus()
|
||||
//{
|
||||
// if (PaginationDataGridViewModel?.Data == null || !PaginationDataGridViewModel.Data.Any())
|
||||
// {
|
||||
// IsAllSelected = false;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// var selectedCount = PaginationDataGridViewModel.Data.Count(user =>
|
||||
// user is UserOutput output && output.IsSelected);
|
||||
// var totalCount = PaginationDataGridViewModel.Data.Count;
|
||||
|
||||
// if (selectedCount == 0)
|
||||
// {
|
||||
// IsAllSelected = false;
|
||||
// }
|
||||
// else if (selectedCount == totalCount)
|
||||
// {
|
||||
// IsAllSelected = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// IsAllSelected = null; // 部分选中状态
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
//private string _searchText;
|
||||
//public string SearchText
|
||||
//{
|
||||
// get => _searchText;
|
||||
// set
|
||||
// {
|
||||
// if (SetProperty(ref _searchText, value))
|
||||
// {
|
||||
// Debug.WriteLine($"SearchText Updated: {value}");
|
||||
// FilterItems(value);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private DemoDataModel? _selectedItem;
|
||||
//public DemoDataModel? SelectedItem
|
||||
//{
|
||||
// get => _selectedItem;
|
||||
// set
|
||||
// {
|
||||
// if (SetProperty(ref _selectedItem, value))
|
||||
// {
|
||||
// Debug.WriteLine($"SelectedItem Updated: {value}");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//// 修改 Items 属性为 ManualObservableCollection
|
||||
//private ManualObservableCollection<DemoDataModel> _items = new();
|
||||
//public ManualObservableCollection<DemoDataModel> Items
|
||||
//{
|
||||
// get => _items;
|
||||
// set => SetProperty(ref _items, value);
|
||||
//}
|
||||
|
||||
//private readonly List<DemoDataModel> _dataList;
|
||||
|
||||
//private void FilterItems(string key)
|
||||
//{
|
||||
// //Items.CanNotify = false;
|
||||
|
||||
// Items.Clear();
|
||||
|
||||
// foreach (var data in _dataList)
|
||||
// {
|
||||
// if (data.Name.ToLower().Contains(key.ToLower()))
|
||||
// {
|
||||
// Items.Add(data);
|
||||
// }
|
||||
// }
|
||||
|
||||
// //RaisePropertyChanged(nameof(Items));
|
||||
// //Items.CanNotify = true;
|
||||
//}
|
||||
//List<DemoDataModel> GetDemoDataList(int count)
|
||||
//{
|
||||
// var list = new List<DemoDataModel>();
|
||||
// for (var i = 1; i <= count; i++)
|
||||
// {
|
||||
// var index = i % 6 + 1;
|
||||
// var model = new DemoDataModel
|
||||
// {
|
||||
// Index = i,
|
||||
// IsSelected = i % 2 == 0,
|
||||
// Name = $"Name{i}",
|
||||
// Type = (DemoType)index,
|
||||
// ImgPath = $"/HandyControlDemo;component/Resources/Img/Avatar/avatar{index}.png",
|
||||
// Remark = new string(i.ToString()[0], 10)
|
||||
// };
|
||||
// list.Add(model);
|
||||
// }
|
||||
|
||||
// return list;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
// public class DemoDataModel
|
||||
//{
|
||||
// public int Index { get; set; }
|
||||
|
||||
// public string Name { get; set; } = string.Empty;
|
||||
|
||||
// public bool IsSelected { get; set; }
|
||||
|
||||
// public string Remark { get; set; } = string.Empty;
|
||||
|
||||
// public DemoType Type { get; set; }
|
||||
|
||||
// public string ImgPath { get; set; } = string.Empty;
|
||||
|
||||
// public List<DemoDataModel> DataList { get; set; } = [];
|
||||
//}
|
||||
|
||||
//public enum DemoType
|
||||
//{
|
||||
// Type1 = 1,
|
||||
// Type2,
|
||||
// Type3,
|
||||
// Type4,
|
||||
// Type5,
|
||||
// Type6
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user