2026-04-28 10:23:58 +08:00
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
|
using System.Windows;
|
|
|
|
|
|
using System.Windows.Threading;
|
|
|
|
|
|
using YY.Admin.Core;
|
|
|
|
|
|
using YY.Admin.Core.Util;
|
|
|
|
|
|
using YY.Admin.Event;
|
2026-04-30 15:28:20 +08:00
|
|
|
|
using Prism.Events;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
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",
|
|
|
|
|
|
|
2026-05-07 09:47:39 +08:00
|
|
|
|
// 已实现页面:分类字典
|
|
|
|
|
|
["CategoryDictionaryManagementView"] = "CategoryDictionaryManagementView",
|
|
|
|
|
|
["/system/category"] = "CategoryDictionaryManagementView",
|
|
|
|
|
|
["/system/category/index"] = "CategoryDictionaryManagementView",
|
|
|
|
|
|
["/platform/category"] = "CategoryDictionaryManagementView",
|
|
|
|
|
|
["sysCategory"] = "CategoryDictionaryManagementView",
|
|
|
|
|
|
|
2026-04-28 10:23:58 +08:00
|
|
|
|
// 已实现页面:角色管理
|
|
|
|
|
|
["RoleManagementView"] = "RoleManagementView",
|
|
|
|
|
|
["/system/role"] = "RoleManagementView",
|
|
|
|
|
|
["/system/role/index"] = "RoleManagementView",
|
|
|
|
|
|
["sysRole"] = "RoleManagementView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:租户管理
|
|
|
|
|
|
["TenantManagementView"] = "TenantManagementView",
|
|
|
|
|
|
["/system/tenant"] = "TenantManagementView",
|
|
|
|
|
|
["/system/tenant/index"] = "TenantManagementView",
|
|
|
|
|
|
["/platform/tenant"] = "TenantManagementView",
|
2026-04-30 15:28:20 +08:00
|
|
|
|
["sysTenant"] = "TenantManagementView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:菜单管理
|
|
|
|
|
|
["MenuManagementView"] = "MenuManagementView",
|
|
|
|
|
|
["/platform/menu"] = "MenuManagementView",
|
|
|
|
|
|
["/system/menu/index"] = "MenuManagementView",
|
|
|
|
|
|
["sysMenu"] = "MenuManagementView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:登录设置
|
|
|
|
|
|
["LoginSettingsView"] = "LoginSettingsView",
|
|
|
|
|
|
["/system/loginSettings"] = "LoginSettingsView",
|
|
|
|
|
|
["/system/login-setting"] = "LoginSettingsView",
|
|
|
|
|
|
["/system/login-settings"] = "LoginSettingsView",
|
|
|
|
|
|
["loginSettings"] = "LoginSettingsView",
|
|
|
|
|
|
["登录设置"] = "LoginSettingsView",
|
|
|
|
|
|
["登陆设置"] = "LoginSettingsView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:车辆管理
|
|
|
|
|
|
["VehicleListView"] = "VehicleListView",
|
|
|
|
|
|
["/xslmes/mesXslVehicle"] = "VehicleListView",
|
|
|
|
|
|
["/xslmes/vehicle"] = "VehicleListView",
|
|
|
|
|
|
["mesXslVehicle"] = "VehicleListView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:客户管理
|
|
|
|
|
|
["CustomerListView"] = "CustomerListView",
|
|
|
|
|
|
["/xslmes/mesXslCustomer"] = "CustomerListView",
|
|
|
|
|
|
["/xslmes/customer"] = "CustomerListView",
|
|
|
|
|
|
["mesXslCustomer"] = "CustomerListView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:供应商管理
|
|
|
|
|
|
["SupplierListView"] = "SupplierListView",
|
|
|
|
|
|
["/xslmes/mesXslSupplier"] = "SupplierListView",
|
|
|
|
|
|
["/xslmes/supplier"] = "SupplierListView",
|
2026-05-06 15:30:31 +08:00
|
|
|
|
["mesXslSupplier"] = "SupplierListView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:磅单记录管理
|
|
|
|
|
|
["WeightRecordListView"] = "WeightRecordListView",
|
|
|
|
|
|
["/xslmes/mesXslWeightRecord"] = "WeightRecordListView",
|
|
|
|
|
|
["mesXslWeightRecord"] = "WeightRecordListView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:地磅称重操作
|
|
|
|
|
|
["WeightRecordOperationView"] = "WeightRecordOperationView",
|
|
|
|
|
|
["/xslmes/weightRecordOperation"] = "WeightRecordOperationView",
|
2026-05-07 09:47:39 +08:00
|
|
|
|
["weightRecordOperation"] = "WeightRecordOperationView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:密炼物料信息
|
|
|
|
|
|
["MixerMaterialListView"] = "MixerMaterialListView",
|
|
|
|
|
|
["/xslmes/mesMixerMaterial"] = "MixerMaterialListView",
|
2026-05-09 15:55:11 +08:00
|
|
|
|
["mesMixerMaterial"] = "MixerMaterialListView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:原料入场记录
|
|
|
|
|
|
["RawMaterialEntryListView"] = "RawMaterialEntryListView",
|
|
|
|
|
|
["/xslmes/mesXslRawMaterialEntry"] = "RawMaterialEntryListView",
|
2026-05-09 18:25:34 +08:00
|
|
|
|
["mesXslRawMaterialEntry"] = "RawMaterialEntryListView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:新增原料入场记录
|
|
|
|
|
|
["RawMaterialEntryOperationView"] = "RawMaterialEntryOperationView",
|
|
|
|
|
|
["/xslmes/rawMaterialEntryOperation"] = "RawMaterialEntryOperationView",
|
2026-05-11 14:32:44 +08:00
|
|
|
|
["rawMaterialEntryOperation"] = "RawMaterialEntryOperationView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:原材料卡片
|
|
|
|
|
|
["RawMaterialCardListView"] = "RawMaterialCardListView",
|
|
|
|
|
|
["/xslmes/mesXslRawMaterialCard"] = "RawMaterialCardListView",
|
2026-05-12 14:06:07 +08:00
|
|
|
|
["mesXslRawMaterialCard"] = "RawMaterialCardListView",
|
|
|
|
|
|
|
|
|
|
|
|
// 已实现页面:库区管理
|
|
|
|
|
|
["WarehouseAreaListView"] = "WarehouseAreaListView",
|
|
|
|
|
|
["/xslmes/mesXslWarehouseArea"] = "WarehouseAreaListView",
|
|
|
|
|
|
["mesXslWarehouseArea"] = "WarehouseAreaListView"
|
2026-04-28 10:23:58 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2026-04-30 15:28:20 +08:00
|
|
|
|
private SubscriptionToken? _menuStructureToken;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_menuStructureToken = _eventAggregator.GetEvent<MenuStructureChangedEvent>()
|
|
|
|
|
|
.Subscribe(async _ => await ReloadMenusAsync(), ThreadOption.UIThread);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 菜单数据变更后刷新左侧树(不重复执行默认页签导航)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task ReloadMenusAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var menuTree = await _sysMenuService.GetLoginMenuTree();
|
|
|
|
|
|
MenuItems.Clear();
|
|
|
|
|
|
ConvertMenuTreeToViewModel(menuTree);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.Error($"菜单重新加载失败: {ex.Message}", ex);
|
|
|
|
|
|
}
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void OpenOrActivateTab(MenuItem menuItem)
|
|
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_logger.Debug($"菜单点击触发: Name={menuItem?.Name}, ViewName={menuItem?.ViewName}, ChildrenCount={menuItem?.Children?.Count ?? 0}");
|
2026-04-28 10:23:58 +08:00
|
|
|
|
// 发布事件
|
|
|
|
|
|
_eventAggregator.GetEvent<TabSourceSelectedEvent>().Publish(menuItem);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnTabSelected(TabItemModel tab)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tab.TabSource is MenuItem menuItem)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 取消上一个选中菜单选中状态
|
2026-05-15 11:34:12 +08:00
|
|
|
|
if (SelectedMenuItem != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelectedMenuItem.IsSelected = false;
|
|
|
|
|
|
}
|
2026-04-28 10:23:58 +08:00
|
|
|
|
|
|
|
|
|
|
SelectedMenuItem = menuItem;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置菜单选中
|
|
|
|
|
|
SelectedMenuItem.IsSelected = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 展开父节点
|
|
|
|
|
|
ToggleParents(SelectedMenuItem, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnTabClosed(TabItemModel tab)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tab.TabSource is MenuItem menuItem)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 折叠父节点
|
|
|
|
|
|
ToggleParents(menuItem, false);
|
|
|
|
|
|
|
|
|
|
|
|
// 取消菜单选中
|
2026-05-15 11:34:12 +08:00
|
|
|
|
menuItem.IsSelected = false;
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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)
|
|
|
|
|
|
{
|
2026-04-30 15:28:20 +08:00
|
|
|
|
MenuItems.Clear();
|
2026-04-28 10:23:58 +08:00
|
|
|
|
// 过滤并排序菜单项:只包含目录和菜单类型,排除按钮类型,并按排序号排序
|
|
|
|
|
|
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))
|
2026-04-30 15:28:20 +08:00
|
|
|
|
{
|
|
|
|
|
|
_logger.Debug($"菜单路由命中: Title={menu.Title}, Candidate={candidate}, View={viewName}");
|
2026-04-28 10:23:58 +08:00
|
|
|
|
return viewName;
|
2026-04-30 15:28:20 +08:00
|
|
|
|
}
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保留原始Path,若未注册将统一展示NotFoundView
|
2026-04-30 15:28:20 +08:00
|
|
|
|
_logger.Warning($"菜单路由未命中映射: Title={menu.Title}, Path={menu.Path}, Component={menu.Component}, Name={menu.Name}");
|
2026-04-28 10:23:58 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2026-04-30 15:28:20 +08:00
|
|
|
|
|
|
|
|
|
|
if (_menuStructureToken != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_eventAggregator.GetEvent<MenuStructureChangedEvent>().Unsubscribe(_menuStructureToken);
|
|
|
|
|
|
_menuStructureToken = null;
|
|
|
|
|
|
}
|
2026-04-28 10:23:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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;
|
|
|
|
|
|
//}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|