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;
}
}
}