Files
Yi.Admin/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Entities/MenuAggregateRoot.cs

502 lines
17 KiB
C#
Raw Normal View History

using System.Text.RegularExpressions;
using System.Web;
2024-09-07 13:43:28 +08:00
using NUglify.Helpers;
2024-09-07 02:17:07 +08:00
using SqlSugar;
2023-12-11 09:55:12 +08:00
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
2023-12-11 18:43:36 +08:00
using Yi.Framework.Core.Data;
2023-12-11 09:55:12 +08:00
using Yi.Framework.Core.Helper;
using Yi.Framework.Rbac.Domain.Shared.Dtos;
using Yi.Framework.Rbac.Domain.Shared.Enums;
namespace Yi.Framework.Rbac.Domain.Entities
2023-04-13 21:12:06 +08:00
{
/// <summary>
/// 菜单表
///</summary>
[SugarTable("Menu")]
2024-05-22 14:35:08 +08:00
public partial class MenuAggregateRoot : AggregateRoot<Guid>, ISoftDelete, IAuditedObject, IOrderNum, IState
2023-04-13 21:12:06 +08:00
{
2024-09-06 18:23:11 +08:00
public MenuAggregateRoot()
{
}
public MenuAggregateRoot(Guid id)
{
Id = id;
ParentId = Guid.Empty;
}
public MenuAggregateRoot(Guid id, Guid parentId)
{
Id = id;
ParentId = parentId;
}
2023-12-11 09:55:12 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
/// 主键
/// </summary>
[SugarColumn(IsPrimaryKey = true)]
2023-12-11 09:55:12 +08:00
public override Guid Id { get; protected set; }
2023-04-13 21:12:06 +08:00
/// <summary>
/// 逻辑删除
/// </summary>
public bool IsDeleted { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreationTime { get; set; } = DateTime.Now;
/// <summary>
/// 创建者
/// </summary>
2023-12-11 09:55:12 +08:00
public Guid? CreatorId { get; set; }
2023-04-13 21:12:06 +08:00
/// <summary>
/// 最后修改者
/// </summary>
2023-12-11 09:55:12 +08:00
public Guid? LastModifierId { get; set; }
2023-04-13 21:12:06 +08:00
/// <summary>
/// 最后修改时间
/// </summary>
public DateTime? LastModificationTime { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNum { get; set; } = 0;
/// <summary>
/// 状态
/// </summary>
public bool State { get; set; }
/// <summary>
/// 菜单名
/// </summary>
2024-09-07 02:17:07 +08:00
public string MenuName { get; set; }
/// <summary>
/// 路由名称
/// </summary>
public string? RouterName { get; set; }
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "MenuType")]
public MenuTypeEnum MenuType { get; set; } = MenuTypeEnum.Menu;
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "PermissionCode")]
public string? PermissionCode { get; set; }
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
///
///</summary>
[SugarColumn(ColumnName = "ParentId")]
2023-12-11 09:55:12 +08:00
public Guid ParentId { get; set; }
2023-04-13 21:12:06 +08:00
/// <summary>
/// 菜单图标
///</summary>
[SugarColumn(ColumnName = "MenuIcon")]
public string? MenuIcon { get; set; }
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
/// 菜单组件路由
///</summary>
[SugarColumn(ColumnName = "Router")]
public string? Router { get; set; }
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
/// 是否为外部链接
///</summary>
[SugarColumn(ColumnName = "IsLink")]
public bool IsLink { get; set; }
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
/// 是否缓存
///</summary>
[SugarColumn(ColumnName = "IsCache")]
public bool IsCache { get; set; }
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
/// 是否显示
///</summary>
[SugarColumn(ColumnName = "IsShow")]
public bool IsShow { get; set; } = true;
/// <summary>
/// 描述
///</summary>
[SugarColumn(ColumnName = "Remark")]
public string? Remark { get; set; }
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
/// <summary>
/// 组件路径
///</summary>
[SugarColumn(ColumnName = "Component")]
public string? Component { get; set; }
2024-09-06 18:23:11 +08:00
2024-09-07 02:17:07 +08:00
/// <summary>
/// 菜单来源
/// </summary>
public MenuSourceEnum MenuSource { get; set; } = MenuSourceEnum.Ruoyi;
2023-04-13 21:12:06 +08:00
/// <summary>
/// 路由参数
///</summary>
[SugarColumn(ColumnName = "Query")]
public string? Query { get; set; }
2023-04-13 21:12:06 +08:00
2024-09-06 18:23:11 +08:00
[SugarColumn(IsIgnore = true)] public List<MenuAggregateRoot>? Children { get; set; }
2023-04-13 21:12:06 +08:00
}
/// <summary>
/// 实体扩展
/// </summary>
public static class MenuEntityExtensions
{
/// <summary>
/// 构建vue3路由
/// </summary>
/// <param name="menus"></param>
/// <returns></returns>
public static List<Vue3RouterDto> Vue3RuoYiRouterBuild(this List<MenuAggregateRoot> menus)
2023-04-13 21:12:06 +08:00
{
2024-09-07 02:17:07 +08:00
menus = menus
.Where(m => m.State == true)
.Where(m => m.MenuType != MenuTypeEnum.Component)
.Where(m => m.MenuSource == MenuSourceEnum.Ruoyi)
2024-09-07 02:17:07 +08:00
.ToList();
2023-04-13 21:12:06 +08:00
List<Vue3RouterDto> routers = new();
foreach (var m in menus)
{
var r = new Vue3RouterDto();
r.OrderNum = m.OrderNum;
var routerName = m.Router?.Split("/").LastOrDefault();
r.Id = m.Id;
r.ParentId = m.ParentId;
//开头大写
r.Name = routerName?.First().ToString().ToUpper() + routerName?.Substring(1);
r.Path = m.Router!;
r.Hidden = !m.IsShow;
if (m.MenuType == MenuTypeEnum.Catalogue)
{
r.Redirect = "noRedirect";
r.AlwaysShow = true;
//判断是否为最顶层的路由
2023-12-11 09:55:12 +08:00
if (Guid.Empty == m.ParentId)
2023-04-13 21:12:06 +08:00
{
r.Component = "Layout";
}
else
{
r.Component = "ParentView";
}
}
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
if (m.MenuType == MenuTypeEnum.Menu)
{
r.Redirect = "noRedirect";
r.AlwaysShow = true;
r.Component = m.Component!;
r.AlwaysShow = false;
}
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
r.Meta = new Meta
{
Title = m.MenuName!,
Icon = m.MenuIcon!,
NoCache = !m.IsCache
};
if (m.IsLink)
{
r.Meta.link = m.Router!;
r.AlwaysShow = false;
}
routers.Add(r);
}
2024-09-06 18:23:11 +08:00
2023-04-13 21:12:06 +08:00
return TreeHelper.SetTree(routers);
2024-09-06 18:23:11 +08:00
}
/// <summary>
/// 构建vue3路由
/// </summary>
/// <param name="menus"></param>
/// <returns></returns>
public static List<Vue3RouterDto> Vben5RouterBuild(this List<MenuAggregateRoot> menus)
{
menus = menus
.Where(m => m.State == true)
.Where(m => m.MenuType != MenuTypeEnum.Component)
.Where(m => m.MenuSource == MenuSourceEnum.Vben5)
.ToList();
List<Vue3RouterDto> routers = new();
foreach (var m in menus)
{
var r = new Vue3RouterDto();
r.OrderNum = m.OrderNum;
r.Id = m.Id;
r.ParentId = m.ParentId;
r.Hidden = !m.IsShow;
// 检测是否为 URL 链接http:// 或 https:// 开头)
bool isUrl = !string.IsNullOrEmpty(m.Router) &&
(m.Router.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
m.Router.StartsWith("https://", StringComparison.OrdinalIgnoreCase));
// 判断是否为内嵌 iframe
// 1. Component 明确设置为 "InnerLink"(优先级最高)
// 2. 或者检测到是 URL 且 isLink = false自动识别为内嵌
bool isInnerLink = (!string.IsNullOrEmpty(m.Component) &&
m.Component.Equals("InnerLink", StringComparison.OrdinalIgnoreCase)) ||
(isUrl && !m.IsLink);
// 判断是否为外链(新标签页打开):
// 检测到是 URL 且 isLink = true且不是内嵌 iframe
bool isExternalLink = isUrl && m.IsLink && !isInnerLink;
// 生成路由名称
string routerName;
if (isInnerLink)
{
// 内嵌 iframe从 path 或 router 中提取名称
routerName = m.Router?.Split("/").LastOrDefault() ?? "InnerLink";
}
else if (isExternalLink)
{
// 外链:从 URL 中提取名称
try
{
var uri = new Uri(m.Router!);
routerName = uri.Host.Replace(".", "").Replace("-", "");
}
catch
{
// 如果 URL 格式不正确,使用默认名称
routerName = "ExternalLink";
}
}
else
{
// 普通路由:从 router 中提取名称
routerName = m.Router?.Split("/").LastOrDefault() ?? string.Empty;
}
// 开头大写处理
if (string.IsNullOrEmpty(routerName))
{
r.Name = routerName;
}
else if (routerName.Length == 1)
{
r.Name = routerName.ToUpper();
}
else
{
r.Name = routerName.First().ToString().ToUpper() + routerName.Substring(1);
}
// 设置路径
r.Path = m.Router ?? string.Empty;
2023-04-13 21:12:06 +08:00
// 处理内嵌 iframe 场景(优先级最高)
// 触发条件Component = "InnerLink" 或 (检测到 URL 且 isLink = false)
if (isInnerLink)
{
// 内嵌 iframecomponent 为 InnerLinkmeta.link 包含完整 iframe 地址
r.Redirect = "noRedirect";
r.AlwaysShow = false;
r.Component = "InnerLink";
// meta.link 应该包含完整的 iframe 地址,优先使用 Router
string iframeUrl = !string.IsNullOrEmpty(m.Router) ? m.Router : m.Component ?? string.Empty;
// 清理 path去除协议和特殊字符避免前端路由拼接时出现问题
string cleanedPath = m.Router ?? m.Component ?? string.Empty;
if (!string.IsNullOrEmpty(cleanedPath))
{
// 去除 http:// 或 https://
cleanedPath = Regex.Replace(cleanedPath, @"^https?://", "", RegexOptions.IgnoreCase);
// 去除 /#/
cleanedPath = cleanedPath.Replace("/#/", "");
// 去除 #
cleanedPath = cleanedPath.Replace("#", "");
// 去除 ? 和 &
cleanedPath = cleanedPath.Replace("?", "").Replace("&", "");
}
// 使用清理后的 path用于前端路由匹配
r.Path = cleanedPath;
r.Meta = new Meta
{
Title = m.MenuName!,
Icon = m.MenuIcon ?? string.Empty,
NoCache = !m.IsCache,
link = iframeUrl // meta.link 保持完整的 URL用于 iframe 加载
};
}
// 处理外链场景(新标签页打开)
// 触发条件:检测到 URL 且 isLink = true
else if (isExternalLink)
{
// 外链path 保持原样component 为 Layout 或 ParentViewmeta.link 包含完整外链地址
r.Redirect = "noRedirect";
r.AlwaysShow = false;
// 判断是否为最顶层的路由
if (Guid.Empty == m.ParentId)
{
r.Component = "Layout";
}
else
{
r.Component = "ParentView";
}
r.Meta = new Meta
{
Title = m.MenuName!,
Icon = m.MenuIcon ?? string.Empty,
NoCache = !m.IsCache,
link = m.Router! // 完整的外链地址
};
}
// 处理普通路由菜单
else
{
if (m.MenuType == MenuTypeEnum.Catalogue)
{
r.Redirect = "noRedirect";
r.AlwaysShow = true;
// 判断是否为最顶层的路由
if (Guid.Empty == m.ParentId)
{
r.Component = "Layout";
}
else
{
r.Component = "ParentView";
}
}
else if (m.MenuType == MenuTypeEnum.Menu)
{
r.Redirect = "noRedirect";
r.AlwaysShow = false;
r.Component = m.Component ?? string.Empty;
}
r.Meta = new Meta
{
Title = m.MenuName!,
Icon = m.MenuIcon ?? string.Empty,
NoCache = !m.IsCache
};
// 如果 IsLink 为 true 但不是外链,则可能是其他类型的链接
if (m.IsLink && !string.IsNullOrEmpty(m.Router))
{
r.Meta.link = m.Router;
}
}
routers.Add(r);
}
return TreeHelper.SetTree(routers);
}
2024-09-07 02:17:07 +08:00
2024-09-06 18:23:11 +08:00
/// <summary>
/// 构建vue3 pure路由
/// </summary>
/// <param name="menus"></param>
/// <returns></returns>
public static List<Vue3PureRouterDto> Vue3PureRouterBuild(this List<MenuAggregateRoot> menus)
{
2024-09-07 02:17:07 +08:00
//pure的菜单为树形
var allRouters = menus
.Where(m => m.State == true)
.Where(m => m.MenuType != MenuTypeEnum.Component)
.Where(m => m.MenuSource == MenuSourceEnum.Pure)
.Select(m => new Vue3PureRouterDto
{
2024-09-07 13:43:28 +08:00
Path =m.Router.StartsWith("/")?m.Router:"/"+m.Router,
Name =m.IsLink==true?"Link": m.RouterName,
2024-09-07 02:17:07 +08:00
component = m.Component,
Meta = new MetaPureRouterDto()
{
showLink = m.IsShow,
FrameSrc = m.IsLink == true ? m.Router : null,
Auths = new List<string>() { m.PermissionCode },
Icon = m.MenuIcon,
Title = m.MenuName,
},
Children =null,
Id = m.Id,
ParentId = m.ParentId
})
.ToList();
var routerDic = allRouters.GroupBy(x => x.ParentId).ToDictionary(x => x.Key,y=>y.ToList());
//根路由
if (!routerDic.TryGetValue(Guid.Empty, out var rootRouters))
{
return new List<Vue3PureRouterDto>();
}
Stack<Vue3PureRouterDto> stack = new Stack<Vue3PureRouterDto>(rootRouters);
while (stack.Count > 0)
{
var currentRouter = stack.Pop();
if (routerDic.TryGetValue(currentRouter.Id, out var items))
{
currentRouter.Children = items;
items?.ForEach(x => stack.Push(x));
}
}
return rootRouters;
2023-04-13 21:12:06 +08:00
}
/// <summary>
/// 构建菜单树表
/// </summary>
/// <param name="menus"></param>
/// <returns></returns>
public static List<MenuTreeDto> TreeDtoBuild(this List<MenuAggregateRoot> menus)
{
List<MenuTreeDto> treeDtos = new();
foreach (var m in menus)
{
var treeDto = new MenuTreeDto
{
Id = m.Id,
ParentId = m.ParentId,
OrderNum = m.OrderNum,
MenuName = m.MenuName,
MenuType = m.MenuType,
MenuIcon = m.MenuIcon
};
treeDtos.Add(treeDto);
}
return TreeHelper.SetTree(treeDtos);
}
2023-04-13 21:12:06 +08:00
}
2024-09-06 18:23:11 +08:00
}