You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
477 lines
17 KiB
477 lines
17 KiB
using Microsoft.Extensions.Logging;
|
|
using MyAvaloniaApp.Services;
|
|
using MyAvaloniaApp.ViewModels.Base;
|
|
using MyAvaloniaApp.ViewModels.Pages;
|
|
using ReactiveUI;
|
|
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Reactive;
|
|
using System.Reactive.Linq;
|
|
|
|
namespace MyAvaloniaApp.ViewModels;
|
|
|
|
/// <summary>
|
|
/// 主窗口的 ViewModel,使用完整的 ReactiveUI 路由系统
|
|
/// </summary>
|
|
public class MainWindowViewModel : ReactiveObject
|
|
{
|
|
private string _title = "My Avalonia App - ReactiveUI 路由";
|
|
private string _headerTitle = "系统管理平台";
|
|
private string _headerSubtitle = "欢迎使用现代化管理界面";
|
|
private ObservableCollection<NavigationItem> _navigationItems = new();
|
|
private ObservableCollection<TabItem> _tabs = new();
|
|
private NavigationItem? _selectedNavigationItem;
|
|
private TabItem? _selectedTab;
|
|
|
|
private readonly ILogger<MainWindowViewModel>? _logger;
|
|
private readonly IDataService? _dataService;
|
|
private readonly IResourceService? _resourceService;
|
|
private readonly IScreen _screen;
|
|
|
|
public string Title
|
|
{
|
|
get => _title;
|
|
set => this.RaiseAndSetIfChanged(ref _title, value);
|
|
}
|
|
|
|
public string HeaderTitle
|
|
{
|
|
get => _headerTitle;
|
|
set => this.RaiseAndSetIfChanged(ref _headerTitle, value);
|
|
}
|
|
|
|
public string HeaderSubtitle
|
|
{
|
|
get => _headerSubtitle;
|
|
set => this.RaiseAndSetIfChanged(ref _headerSubtitle, value);
|
|
}
|
|
|
|
public ObservableCollection<NavigationItem> NavigationItems
|
|
{
|
|
get => _navigationItems;
|
|
set => this.RaiseAndSetIfChanged(ref _navigationItems, value);
|
|
}
|
|
|
|
public ObservableCollection<TabItem> Tabs
|
|
{
|
|
get => _tabs;
|
|
set => this.RaiseAndSetIfChanged(ref _tabs, value);
|
|
}
|
|
|
|
public NavigationItem? SelectedNavigationItem
|
|
{
|
|
get => _selectedNavigationItem;
|
|
set => this.RaiseAndSetIfChanged(ref _selectedNavigationItem, value);
|
|
}
|
|
|
|
public TabItem? SelectedTab
|
|
{
|
|
get => _selectedTab;
|
|
set => this.RaiseAndSetIfChanged(ref _selectedTab, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 路由状态(暴露给 UI 使用)
|
|
/// </summary>
|
|
public RoutingState Router => _screen.Router;
|
|
|
|
// 响应式命令
|
|
public ReactiveCommand<NavigationItem, Unit> NavigateCommand { get; }
|
|
public ReactiveCommand<TabItem, Unit> CloseTabCommand { get; }
|
|
public ReactiveCommand<TabItem, Unit> SelectTabCommand { get; }
|
|
|
|
public MainWindowViewModel(IScreen screen, ILogger<MainWindowViewModel>? logger = null, IDataService? dataService = null, IResourceService? resourceService = null)
|
|
{
|
|
_screen = screen;
|
|
_logger = logger;
|
|
_dataService = dataService;
|
|
_resourceService = resourceService;
|
|
|
|
_logger?.LogInformation("MainWindowViewModel 已创建");
|
|
|
|
// 初始化导航项(需要在订阅 Router 之前)
|
|
InitializeNavigationItems();
|
|
|
|
// 创建导航命令
|
|
NavigateCommand = ReactiveCommand.Create<NavigationItem>(NavigateToPage);
|
|
|
|
// 创建关闭标签页命令
|
|
CloseTabCommand = ReactiveCommand.Create<TabItem>(CloseTab);
|
|
|
|
// 创建选择标签页命令
|
|
SelectTabCommand = ReactiveCommand.Create<TabItem>(SelectTab);
|
|
|
|
// 监听导航项选择变化
|
|
// 注意:这些订阅会在 ViewModel 的整个生命周期内保持活跃
|
|
// 由于 MainWindowViewModel 是单例或长生命周期对象,这是可以接受的
|
|
// 如果需要更精细的订阅管理,可以在 View 的 WhenActivated 中使用
|
|
this.WhenAnyValue(x => x.SelectedNavigationItem)
|
|
.Where(item => item != null)
|
|
.Subscribe(item => _logger?.LogInformation("导航到: {Title}", item!.Title));
|
|
|
|
// 监听标签页选择变化
|
|
this.WhenAnyValue(x => x.SelectedTab)
|
|
.Where(tab => tab != null)
|
|
.Subscribe(tab => _logger?.LogInformation("切换到标签页: {Title}", tab!.Title));
|
|
|
|
// 监听 Router.CurrentViewModel 变化,同步标签页
|
|
// 这个订阅对于路由系统正常工作至关重要,必须保持活跃
|
|
_screen.Router
|
|
.CurrentViewModel
|
|
.Subscribe(viewModel =>
|
|
{
|
|
OnRouterViewModelChanged(viewModel);
|
|
});
|
|
}
|
|
|
|
private void InitializeNavigationItems()
|
|
{
|
|
_navigationItems = new ObservableCollection<NavigationItem>
|
|
{
|
|
new NavigationItem
|
|
{
|
|
Id = "dashboard",
|
|
Title = _resourceService?.GetString("NavDashboard") ?? "仪表板",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.Home,
|
|
ViewModel = new DashboardPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "users",
|
|
Title = _resourceService?.GetString("NavUsers") ?? "用户管理",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.Users,
|
|
Children = new ObservableCollection<NavigationItem>
|
|
{
|
|
new NavigationItem
|
|
{
|
|
Id = "users-list",
|
|
Title = "用户列表",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.UserGroup,
|
|
ViewModel = new UsersPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "users-roles",
|
|
Title = "角色管理",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.ShieldCheck,
|
|
ViewModel = new UsersPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "users-permissions",
|
|
Title = "权限设置",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.Key,
|
|
ViewModel = new UsersPageViewModel(_screen)
|
|
}
|
|
}
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "settings",
|
|
Title = _resourceService?.GetString("NavSettings") ?? "系统设置",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.Cog6Tooth,
|
|
Children = new ObservableCollection<NavigationItem>
|
|
{
|
|
new NavigationItem
|
|
{
|
|
Id = "settings-general",
|
|
Title = "常规设置",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.Cog,
|
|
ViewModel = new SettingsPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "settings-security",
|
|
Title = "安全设置",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.LockClosed,
|
|
ViewModel = new SettingsPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "settings-backup",
|
|
Title = "备份恢复",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.CloudArrowUp,
|
|
ViewModel = new SettingsPageViewModel(_screen)
|
|
}
|
|
}
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "reports",
|
|
Title = _resourceService?.GetString("NavReports") ?? "报表统计",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.ChartBar,
|
|
ViewModel = new ReportsPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "help",
|
|
Title = _resourceService?.GetString("NavHelp") ?? "帮助中心",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.QuestionMarkCircle,
|
|
ViewModel = new HelpPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "dialog-host",
|
|
Title = "对话框示例",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.ChatBubbleLeftRight,
|
|
ViewModel = new DialogHostPageViewModel(_screen)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "icons",
|
|
Title = _resourceService?.GetString("NavIcons") ?? "图标库",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.Sparkles,
|
|
ViewModel = new IconsPageViewModel(_screen, _logger as Microsoft.Extensions.Logging.ILogger<IconsPageViewModel>)
|
|
},
|
|
new NavigationItem
|
|
{
|
|
Id = "editor",
|
|
Title = "代码编辑器",
|
|
IconType = HeroIconsAvalonia.Enums.IconType.CodeBracket,
|
|
ViewModel = new EditorPageViewModel(_screen, _logger as Microsoft.Extensions.Logging.ILogger<EditorPageViewModel>)
|
|
}
|
|
};
|
|
|
|
// 默认选中第一个导航项并导航
|
|
if (_navigationItems.Count > 0 && _navigationItems[0].ViewModel != null)
|
|
{
|
|
_navigationItems[0].IsSelected = true;
|
|
SelectedNavigationItem = _navigationItems[0];
|
|
// 初始导航到仪表板
|
|
var initialViewModel = _navigationItems[0].ViewModel;
|
|
if (initialViewModel != null)
|
|
{
|
|
_screen.Router.Navigate.Execute(initialViewModel);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void NavigateToPage(NavigationItem navigationItem)
|
|
{
|
|
if (navigationItem == null) return;
|
|
|
|
// 检查是否是子级菜单项
|
|
bool isChildItem = _navigationItems.Any(parent => parent.Children.Contains(navigationItem));
|
|
|
|
if (isChildItem)
|
|
{
|
|
// 子级菜单项:处理导航
|
|
if (navigationItem.ViewModel == null) return;
|
|
|
|
// 取消所有导航项的选中状态
|
|
foreach (var item in _navigationItems)
|
|
{
|
|
item.IsSelected = false;
|
|
foreach (var child in item.Children)
|
|
{
|
|
child.IsSelected = false;
|
|
}
|
|
}
|
|
|
|
// 设置当前子项为选中状态
|
|
navigationItem.IsSelected = true;
|
|
SelectedNavigationItem = navigationItem;
|
|
|
|
// 使用 ReactiveUI 路由导航
|
|
_screen.Router.Navigate.Execute(navigationItem.ViewModel);
|
|
|
|
_logger?.LogInformation("导航到子页面: {Title}", navigationItem.Title);
|
|
}
|
|
else if (navigationItem.HasChildren)
|
|
{
|
|
// 父级菜单项:处理展开/收起逻辑
|
|
if (navigationItem.IsExpanded)
|
|
{
|
|
// 如果已经展开,则收起
|
|
navigationItem.IsExpanded = false;
|
|
|
|
// 取消所有导航项的选中状态
|
|
foreach (var item in _navigationItems)
|
|
{
|
|
item.IsSelected = false;
|
|
foreach (var child in item.Children)
|
|
{
|
|
child.IsSelected = false;
|
|
}
|
|
}
|
|
|
|
SelectedNavigationItem = null;
|
|
|
|
_logger?.LogInformation("收起导航项: {Title}", navigationItem.Title);
|
|
}
|
|
else
|
|
{
|
|
// 如果未展开,则展开并选中第一个子项
|
|
foreach (var item in _navigationItems)
|
|
{
|
|
if (item != navigationItem && item.HasChildren)
|
|
{
|
|
item.IsExpanded = false;
|
|
item.IsSelected = false;
|
|
foreach (var child in item.Children)
|
|
{
|
|
child.IsSelected = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
navigationItem.IsExpanded = true;
|
|
|
|
// 选中第一个子项并导航
|
|
if (navigationItem.Children.Count > 0)
|
|
{
|
|
var firstChild = navigationItem.Children[0];
|
|
firstChild.IsSelected = true;
|
|
SelectedNavigationItem = firstChild;
|
|
|
|
if (firstChild.ViewModel != null)
|
|
{
|
|
_screen.Router.Navigate.Execute(firstChild.ViewModel);
|
|
}
|
|
}
|
|
|
|
_logger?.LogInformation("展开导航项: {Title}", navigationItem.Title);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 无子项的父级导航项,直接选中并导航
|
|
foreach (var item in _navigationItems)
|
|
{
|
|
if (item.HasChildren)
|
|
{
|
|
item.IsExpanded = false;
|
|
}
|
|
item.IsSelected = false;
|
|
foreach (var child in item.Children)
|
|
{
|
|
child.IsSelected = false;
|
|
}
|
|
}
|
|
|
|
navigationItem.IsSelected = true;
|
|
SelectedNavigationItem = navigationItem;
|
|
|
|
// 使用 ReactiveUI 路由导航
|
|
if (navigationItem.ViewModel != null)
|
|
{
|
|
_screen.Router.Navigate.Execute(navigationItem.ViewModel);
|
|
}
|
|
|
|
_logger?.LogInformation("导航到页面: {Title}", navigationItem.Title);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Router.CurrentViewModel 变化时的处理
|
|
/// </summary>
|
|
private void OnRouterViewModelChanged(IRoutableViewModel? viewModel)
|
|
{
|
|
if (viewModel == null) return;
|
|
|
|
// 查找对应的导航项
|
|
var navigationItem = FindNavigationItemByViewModel(viewModel);
|
|
if (navigationItem == null) return;
|
|
|
|
// 创建或更新标签页
|
|
var existingTab = _tabs.FirstOrDefault(t => t.Id == navigationItem.Id);
|
|
if (existingTab != null)
|
|
{
|
|
foreach (var tab in _tabs)
|
|
{
|
|
tab.IsSelected = false;
|
|
}
|
|
existingTab.IsSelected = true;
|
|
SelectedTab = existingTab;
|
|
}
|
|
else
|
|
{
|
|
var newTab = new TabItem
|
|
{
|
|
Id = navigationItem.Id,
|
|
Title = navigationItem.Title,
|
|
IconType = navigationItem.IconType,
|
|
ViewModel = viewModel,
|
|
IsSelected = true,
|
|
CanClose = navigationItem.Id != "dashboard"
|
|
};
|
|
|
|
foreach (var tab in _tabs)
|
|
{
|
|
tab.IsSelected = false;
|
|
}
|
|
|
|
_tabs.Add(newTab);
|
|
SelectedTab = newTab;
|
|
}
|
|
|
|
_logger?.LogInformation("Router 导航到: {Title}", navigationItem.Title);
|
|
}
|
|
|
|
private NavigationItem? FindNavigationItemByViewModel(IRoutableViewModel viewModel)
|
|
{
|
|
foreach (var item in _navigationItems)
|
|
{
|
|
if (item.ViewModel == viewModel)
|
|
{
|
|
return item;
|
|
}
|
|
|
|
foreach (var child in item.Children)
|
|
{
|
|
if (child.ViewModel == viewModel)
|
|
{
|
|
return child;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void SelectTab(TabItem tab)
|
|
{
|
|
if (tab == null) return;
|
|
|
|
foreach (var t in _tabs)
|
|
{
|
|
t.IsSelected = false;
|
|
}
|
|
|
|
tab.IsSelected = true;
|
|
SelectedTab = tab;
|
|
|
|
// 使用 Router 导航到对应的 ViewModel
|
|
if (tab.ViewModel != null)
|
|
{
|
|
_screen.Router.Navigate.Execute(tab.ViewModel);
|
|
}
|
|
|
|
_logger?.LogInformation("选择标签页: {Title}", tab.Title);
|
|
}
|
|
|
|
private void CloseTab(TabItem tab)
|
|
{
|
|
if (tab == null || !tab.CanClose) return;
|
|
|
|
var tabIndex = _tabs.IndexOf(tab);
|
|
_tabs.Remove(tab);
|
|
|
|
if (tab.IsSelected && _tabs.Count > 0)
|
|
{
|
|
var newSelectedIndex = Math.Min(tabIndex, _tabs.Count - 1);
|
|
_tabs[newSelectedIndex].IsSelected = true;
|
|
SelectedTab = _tabs[newSelectedIndex];
|
|
|
|
// 导航到新选中的标签页
|
|
var newSelectedViewModel = _tabs[newSelectedIndex].ViewModel;
|
|
if (newSelectedViewModel != null)
|
|
{
|
|
_screen.Router.Navigate.Execute(newSelectedViewModel);
|
|
}
|
|
}
|
|
|
|
_logger?.LogInformation("关闭标签页: {Title}", tab.Title);
|
|
}
|
|
}
|
|
|