using System.Collections.ObjectModel; using System.Linq; using HandyControl.Controls; using Prism.Commands; using Prism.Mvvm; using YY.Admin.Core; using YY.Admin.Core.Extension; using YY.Admin.Event; using YY.Admin.Services.Service.Menu; namespace YY.Admin.ViewModels.SysManage; /// /// 左侧列表一行(树形扁平展示) /// public sealed class MenuFlatRow { public SysMenu Menu { get; } public int Depth { get; } public string IndentTitle { get; } public MenuFlatRow(SysMenu menu, int depth) { Menu = menu; Depth = depth; var pad = new string(' ', depth); var tag = menu.Type == MenuTypeEnum.Dir ? "[目录] " : menu.Type == MenuTypeEnum.Btn ? "[按钮] " : "[菜单] "; IndentTitle = pad + tag + menu.Title; } } /// /// 可编辑字段(与界面绑定) /// public class MenuEditorModel : BindableBase { private long _id; private long _pid; private MenuTypeEnum _type = MenuTypeEnum.Menu; private string? _name; private string? _path; private string? _component; private string? _redirect; private string? _permission; private string _title = string.Empty; private string? _icon; private bool _isIframe; private string? _outLink; private bool _isHide; private bool _isKeepAlive = true; private bool _isAffix; private int _orderNo = 100; private StatusEnum _status = StatusEnum.Enable; private string? _remark; public long Id { get => _id; set => SetProperty(ref _id, value); } public long Pid { get => _pid; set => SetProperty(ref _pid, value); } public MenuTypeEnum Type { get => _type; set => SetProperty(ref _type, value); } public string? Name { get => _name; set => SetProperty(ref _name, value); } public string? Path { get => _path; set => SetProperty(ref _path, value); } public string? Component { get => _component; set => SetProperty(ref _component, value); } public string? Redirect { get => _redirect; set => SetProperty(ref _redirect, value); } public string? Permission { get => _permission; set => SetProperty(ref _permission, value); } public string Title { get => _title; set => SetProperty(ref _title, value); } public string? Icon { get => _icon; set => SetProperty(ref _icon, value); } public bool IsIframe { get => _isIframe; set => SetProperty(ref _isIframe, value); } public string? OutLink { get => _outLink; set => SetProperty(ref _outLink, value); } public bool IsHide { get => _isHide; set => SetProperty(ref _isHide, value); } public bool IsKeepAlive { get => _isKeepAlive; set => SetProperty(ref _isKeepAlive, value); } public bool IsAffix { get => _isAffix; set => SetProperty(ref _isAffix, value); } public int OrderNo { get => _orderNo; set => SetProperty(ref _orderNo, value); } public StatusEnum Status { get => _status; set => SetProperty(ref _status, value); } public string? Remark { get => _remark; set => SetProperty(ref _remark, value); } public bool IsNew => Id == 0; public void LoadFrom(SysMenu m) { Id = m.Id; Pid = m.Pid; Type = m.Type; Name = m.Name; Path = m.Path; Component = m.Component; Redirect = m.Redirect; Permission = m.Permission; Title = m.Title; Icon = m.Icon; IsIframe = m.IsIframe; OutLink = m.OutLink; IsHide = m.IsHide; IsKeepAlive = m.IsKeepAlive; IsAffix = m.IsAffix; OrderNo = m.OrderNo; Status = m.Status; Remark = m.Remark; } public SysMenu ToSysMenu() { return new SysMenu { Id = Id, Pid = Pid, Type = Type, Name = Name, Path = Path, Component = Component, Redirect = Redirect, Permission = Permission, Title = Title.Trim(), Icon = Icon, IsIframe = IsIframe, OutLink = OutLink, IsHide = IsHide, IsKeepAlive = IsKeepAlive, IsAffix = IsAffix, OrderNo = OrderNo, Status = Status, Remark = Remark }; } public void ResetForNew(long pid, MenuTypeEnum type) { Id = 0; Pid = pid; Type = type; Name = null; Path = null; Component = null; Redirect = null; Permission = null; Title = type == MenuTypeEnum.Dir ? "新目录" : type == MenuTypeEnum.Menu ? "新菜单" : "新按钮"; Icon = ""; IsIframe = false; OutLink = null; IsHide = false; IsKeepAlive = true; IsAffix = false; OrderNo = 100; Status = StatusEnum.Enable; Remark = null; } } public class MenuManagementViewModel : BaseViewModel { private readonly ISysMenuService _menuService; public ObservableCollection FlatRows { get; } = new(); public ObservableCollection> ParentOptions { get; } = new(); private MenuFlatRow? _selectedRow; public MenuFlatRow? SelectedRow { get => _selectedRow; set { if (SetProperty(ref _selectedRow, value)) { if (value == null) { Editor = null; RaisePropertyChanged(nameof(Editor)); RaisePropertyChanged(nameof(HasEditor)); } else { Editor ??= new MenuEditorModel(); Editor.LoadFrom(value.Menu); RaisePropertyChanged(nameof(Editor)); RaisePropertyChanged(nameof(HasEditor)); } } } } private MenuEditorModel? _editor; public MenuEditorModel? Editor { get => _editor; set { if (SetProperty(ref _editor, value)) { RaisePropertyChanged(nameof(HasEditor)); } } } public bool HasEditor => Editor != null; public List> StatusList => Enum.GetValues(typeof(StatusEnum)) .Cast() .Select(e => new KeyValuePair(e.GetDescription(), (int)e)) .ToList(); public List> MenuTypeList => Enum.GetValues(typeof(MenuTypeEnum)) .Cast() .Select(e => new KeyValuePair(e.GetDescription(), (int)e)) .ToList(); public DelegateCommand RefreshCommand { get; } public DelegateCommand AddRootCommand { get; } public DelegateCommand AddChildCommand { get; } public DelegateCommand SaveCommand { get; } public DelegateCommand DeleteCommand { get; } public MenuManagementViewModel( ISysMenuService menuService, IContainerExtension container, IRegionManager regionManager) : base(container, regionManager) { _menuService = menuService; RefreshCommand = new DelegateCommand(async () => await RefreshAsync()); AddRootCommand = new DelegateCommand(() => BeginNew(0, MenuTypeEnum.Dir)); AddChildCommand = new DelegateCommand(BeginNewChild, () => SelectedRow != null) .ObservesProperty(() => SelectedRow); SaveCommand = new DelegateCommand(async () => await SaveAsync(), () => Editor != null) .ObservesProperty(() => Editor); DeleteCommand = new DelegateCommand(async () => await DeleteAsync(), () => SelectedRow != null && SelectedRow.Menu.Id != 0) .ObservesProperty(() => SelectedRow); _ = RefreshAsync(); } private async Task RefreshAsync() { try { IsLoading = true; var all = await _menuService.GetAllMenusForManageAsync(); RebuildFlat(all); RebuildParentOptions(all); } catch (Exception ex) { Growl.Error($"加载菜单失败:{ex.Message}"); } finally { IsLoading = false; } } private void RebuildFlat(List all) { FlatRows.Clear(); void Walk(long pid, int depth) { foreach (var m in all.Where(x => x.Pid == pid).OrderBy(x => x.OrderNo).ThenBy(x => x.Id)) { FlatRows.Add(new MenuFlatRow(m, depth)); Walk(m.Id, depth + 1); } } Walk(0, 0); } private void RebuildParentOptions(List all) { ParentOptions.Clear(); ParentOptions.Add(new KeyValuePair(0, "(根目录)")); void Walk(long pid, int depth) { foreach (var m in all.Where(x => x.Pid == pid).OrderBy(x => x.OrderNo).ThenBy(x => x.Id)) { var indent = new string(' ', depth); ParentOptions.Add(new KeyValuePair(m.Id, indent + m.Title)); Walk(m.Id, depth + 1); } } Walk(0, 0); } private void BeginNew(long pid, MenuTypeEnum type) { // 先取消列表选中,避免 setter 把新建的 Editor 清空 _selectedRow = null; RaisePropertyChanged(nameof(SelectedRow)); Editor = new MenuEditorModel(); Editor.ResetForNew(pid, type); RaisePropertyChanged(nameof(Editor)); RaisePropertyChanged(nameof(HasEditor)); } private void BeginNewChild() { if (SelectedRow == null) return; BeginNew(SelectedRow.Menu.Id, MenuTypeEnum.Menu); } private async Task SaveAsync() { if (Editor == null) return; try { IsLoading = true; var entity = Editor.ToSysMenu(); if (Editor.IsNew) { var (ok, msg, id) = await _menuService.CreateMenuAsync(entity); if (ok) { Growl.Success(msg); _eventAggregator.GetEvent().Publish(true); await RefreshAsync(); SelectedRow = FlatRows.FirstOrDefault(r => r.Menu.Id == id); } else { Growl.Warning(msg); } } else { var (ok, msg) = await _menuService.UpdateMenuAsync(entity); if (ok) { Growl.Success(msg); _eventAggregator.GetEvent().Publish(true); var keepId = entity.Id; await RefreshAsync(); SelectedRow = FlatRows.FirstOrDefault(r => r.Menu.Id == keepId); } else { Growl.Warning(msg); } } } catch (Exception ex) { Growl.Error($"保存失败:{ex.Message}"); } finally { IsLoading = false; } } private async Task DeleteAsync() { if (SelectedRow == null) return; var r = System.Windows.MessageBox.Show( $"确定删除「{SelectedRow.Menu.Title}」?若存在子菜单将无法删除。", "确认删除", System.Windows.MessageBoxButton.OKCancel, System.Windows.MessageBoxImage.Question); if (r != System.Windows.MessageBoxResult.OK) return; try { IsLoading = true; var (ok, msg) = await _menuService.DeleteMenuAsync(SelectedRow.Menu.Id); if (ok) { Growl.Success(msg); _eventAggregator.GetEvent().Publish(true); Editor = null; SelectedRow = null; await RefreshAsync(); } else { Growl.Warning(msg); } } catch (Exception ex) { Growl.Error($"删除失败:{ex.Message}"); } finally { IsLoading = false; } } }