diff --git a/src/Api/TakeoutSaaS.AdminApi/Properties/launchSettings.json b/src/Api/TakeoutSaaS.AdminApi/Properties/launchSettings.json index 160a8ad..5b8d303 100644 --- a/src/Api/TakeoutSaaS.AdminApi/Properties/launchSettings.json +++ b/src/Api/TakeoutSaaS.AdminApi/Properties/launchSettings.json @@ -3,10 +3,21 @@ "TakeoutSaaS.AdminApi": { "commandName": "Project", "launchBrowser": true, + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:2680" + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "publishAllPorts": true, + "useSSL": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } } } -} \ No newline at end of file +} diff --git a/src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj b/src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj index 57391fc..554814b 100644 Binary files a/src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj and b/src/Api/TakeoutSaaS.AdminApi/TakeoutSaaS.AdminApi.csproj differ diff --git a/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuAuthItemDto.cs b/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuAuthItemDto.cs new file mode 100644 index 0000000..5699738 --- /dev/null +++ b/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuAuthItemDto.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace TakeoutSaaS.Application.Identity.Contracts; + +/// +/// 菜单操作权限描述。 +/// +public sealed record MenuAuthItemDto +{ + /// + /// 操作名称。 + /// + [JsonPropertyName("title")] + public required string Title { get; init; } + + /// + /// 权限标识。 + /// + [JsonPropertyName("authMark")] + public required string AuthMark { get; init; } +} diff --git a/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuMetaDto.cs b/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuMetaDto.cs new file mode 100644 index 0000000..1a25a73 --- /dev/null +++ b/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuMetaDto.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace TakeoutSaaS.Application.Identity.Contracts; + +/// +/// 菜单前端元数据。 +/// +public sealed record MenuMetaDto +{ + /// + /// 菜单标题(i18n key)。 + /// + [JsonPropertyName("title")] + public required string Title { get; init; } + + /// + /// 图标标识。 + /// + [JsonPropertyName("icon")] + public string? Icon { get; init; } + + /// + /// 是否缓存页面。 + /// + [JsonPropertyName("keepAlive")] + public bool KeepAlive { get; init; } + + /// + /// 是否为 iframe 页面。 + /// + [JsonPropertyName("isIframe")] + public bool IsIframe { get; init; } + + /// + /// 外链或 iframe 地址。 + /// + [JsonPropertyName("link")] + public string? Link { get; init; } + + /// + /// 允许访问的角色编码集合。 + /// + [JsonPropertyName("roles")] + public string[] Roles { get; init; } = System.Array.Empty(); + + /// + /// 按钮/操作级别权限。 + /// + [JsonPropertyName("authList")] + public IReadOnlyList AuthList { get; init; } = System.Array.Empty(); + + /// + /// 访问该菜单所需的权限编码集合(前端用于路由级鉴权)。 + /// + [JsonPropertyName("permissions")] + public string[] Permissions { get; init; } = System.Array.Empty(); +} diff --git a/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuNodeDto.cs b/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuNodeDto.cs index 43f4f4d..b86f891 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuNodeDto.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Contracts/MenuNodeDto.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; namespace TakeoutSaaS.Application.Identity.Contracts; @@ -8,32 +9,37 @@ namespace TakeoutSaaS.Application.Identity.Contracts; public sealed record MenuNodeDto { /// - /// 菜单编码(唯一标识)。 + /// 菜单名称。 /// - public string Code { get; init; } = string.Empty; + [JsonPropertyName("name")] + public required string Name { get; init; } /// - /// 展示名称(中文)。 + /// 路由路径(顶级以 / 开头,子级为相对路径)。 /// - public string Name { get; init; } = string.Empty; + [JsonPropertyName("path")] + public required string Path { get; init; } /// - /// 前端路由路径。 + /// 前端组件路径(基于 src/views,不含 .vue)。 /// - public string Path { get; init; } = string.Empty; + [JsonPropertyName("component")] + public required string Component { get; init; } /// - /// 可选图标标识。 + /// 前端渲染元数据。 /// - public string? Icon { get; init; } - - /// - /// 访问该菜单所需的任一权限,留空表示公共可见。 - /// - public string[] RequiredPermissions { get; init; } = System.Array.Empty(); + [JsonPropertyName("meta")] + public required MenuMetaDto Meta { get; init; } /// /// 子菜单集合。 /// + [JsonPropertyName("children")] public IReadOnlyList Children { get; init; } = System.Array.Empty(); + + /// + /// 访问该菜单所需的任一权限(仅用于后端过滤,不序列化)。 + /// + internal string[] RequiredPermissions { get; init; } = System.Array.Empty(); } diff --git a/src/Application/TakeoutSaaS.Application/Identity/Services/AdminMenuProvider.cs b/src/Application/TakeoutSaaS.Application/Identity/Services/AdminMenuProvider.cs index 2738f4e..b7201e9 100644 --- a/src/Application/TakeoutSaaS.Application/Identity/Services/AdminMenuProvider.cs +++ b/src/Application/TakeoutSaaS.Application/Identity/Services/AdminMenuProvider.cs @@ -29,7 +29,10 @@ public static class AdminMenuProvider var result = new List(); foreach (var node in nodes) { + // 1.1 递归过滤子节点 var filteredChildren = Filter(node.Children, permissionSet); + + // 1.2 判断当前节点是否可见 var visible = node.RequiredPermissions.Length == 0 || node.RequiredPermissions.Any(permissionSet.Contains); if (visible || filteredChildren.Count > 0) { @@ -43,223 +46,515 @@ public static class AdminMenuProvider private static IReadOnlyList GetMenuDefinitions() { - // 1. 顶部菜单定义 + // 1. 顶部菜单定义(按需求自定义排序) return [ new MenuNodeDto { - Code = "dashboard", - Name = "仪表盘", + Name = "Dashboard", Path = "/dashboard", - Icon = "dashboard" + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "仪表盘", + Icon = "ri:pie-chart-line", + Permissions = System.Array.Empty() + }, + Children = + [ + new MenuNodeDto + { + Name = "Console", + Path = "console", + Component = "/dashboard/console", + Meta = new MenuMetaDto + { + Title = "工作台", + Icon = "ri:dashboard-line", + KeepAlive = false, + Permissions = System.Array.Empty() + }, + RequiredPermissions = System.Array.Empty() + } + ] }, new MenuNodeDto { - Code = "merchant", - Name = "商户", + Name = "Tenant", + Path = "/tenant", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "租户管理", + Icon = "ri:building-3-line", + Permissions = ["tenant:read"] + }, + RequiredPermissions = ["tenant:read"], + Children = + [ + new MenuNodeDto + { + Name = "TenantList", + Path = "list", + Component = "/tenant/list", + Meta = new MenuMetaDto + { + Title = "租户列表", + Icon = "ri:community-line", + KeepAlive = true, + Permissions = ["tenant:read"], + AuthList = + [ + new MenuAuthItemDto { Title = "新增", AuthMark = "add" }, + new MenuAuthItemDto { Title = "编辑", AuthMark = "edit" }, + new MenuAuthItemDto { Title = "删除", AuthMark = "delete" }, + new MenuAuthItemDto { Title = "审核", AuthMark = "review" } + ] + }, + RequiredPermissions = ["tenant:read"] + }, + new MenuNodeDto + { + Name = "TenantReview", + Path = "review", + Component = "/tenant/review", + Meta = new MenuMetaDto + { + Title = "入驻审核", + Icon = "ri:checkbox-line", + KeepAlive = true, + Permissions = ["tenant:review"] + }, + RequiredPermissions = ["tenant:review"] + }, + new MenuNodeDto + { + Name = "TenantPackage", + Path = "package", + Component = "/tenant/package", + Meta = new MenuMetaDto + { + Title = "套餐管理", + Icon = "ri:vip-crown-2-line", + KeepAlive = true, + Permissions = ["tenant-package:read"] + }, + RequiredPermissions = ["tenant-package:read"] + }, + new MenuNodeDto + { + Name = "TenantSubscription", + Path = "subscription", + Component = "/tenant/subscription", + Meta = new MenuMetaDto + { + Title = "订阅管理", + Icon = "ri:hand-coin-line", + KeepAlive = true, + Permissions = ["tenant:subscription"] + }, + RequiredPermissions = ["tenant:subscription"] + }, + new MenuNodeDto + { + Name = "TenantBill", + Path = "bill", + Component = "/tenant/bill", + Meta = new MenuMetaDto + { + Title = "账单管理", + Icon = "ri:bill-line", + KeepAlive = true, + Permissions = ["tenant-bill:read"] + }, + RequiredPermissions = ["tenant-bill:read"] + }, + new MenuNodeDto + { + Name = "TenantAnnouncement", + Path = "announcement", + Component = "/tenant/announcement", + Meta = new MenuMetaDto + { + Title = "公告管理", + Icon = "ri:notification-3-line", + KeepAlive = true, + Permissions = ["tenant-announcement:read"] + }, + RequiredPermissions = ["tenant-announcement:read"] + } + ] + }, + new MenuNodeDto + { + Name = "Merchant", Path = "/merchant", - Icon = "store", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "商户中心", + Icon = "ri:store-2-line", + Permissions = ["merchant:read"] + }, RequiredPermissions = ["merchant:read"], Children = [ new MenuNodeDto { - Code = "merchant-list", - Name = "商户管理", - Path = "/merchant/list", + Name = "MerchantList", + Path = "list", + Component = "/merchant/list", + Meta = new MenuMetaDto + { + Title = "商户列表", + Icon = "ri:community-line", + KeepAlive = true, + Permissions = ["merchant:read"] + }, RequiredPermissions = ["merchant:read"] }, new MenuNodeDto { - Code = "store", - Name = "门店管理", - Path = "/merchant/stores", + Name = "Store", + Path = "store", + Component = "/merchant/store", + Meta = new MenuMetaDto + { + Title = "门店管理", + Icon = "ri:store-line", + KeepAlive = true, + Permissions = ["store:read"] + }, RequiredPermissions = ["store:read"] }, new MenuNodeDto { - Code = "staff", - Name = "员工管理", - Path = "/merchant/staff", + Name = "Staff", + Path = "staff", + Component = "/merchant/staff", + Meta = new MenuMetaDto + { + Title = "员工管理", + Icon = "ri:team-line", + KeepAlive = true, + Permissions = ["store-staff:read"] + }, RequiredPermissions = ["store-staff:read"] } ] }, new MenuNodeDto { - Code = "product", - Name = "商品", - Path = "/product", - Icon = "goods", - RequiredPermissions = ["product:read"], - Children = - [ - new MenuNodeDto - { - Code = "product-list", - Name = "商品管理", - Path = "/product/list", - RequiredPermissions = ["product:read"] - }, - new MenuNodeDto - { - Code = "category", - Name = "品类管理", - Path = "/product/category", - RequiredPermissions = ["merchant_category:read"] - } - ] - }, - new MenuNodeDto - { - Code = "order", - Name = "订单", - Path = "/order", - Icon = "order", - RequiredPermissions = ["order:read"], - Children = - [ - new MenuNodeDto - { - Code = "order-list", - Name = "订单管理", - Path = "/order/list", - RequiredPermissions = ["order:read"] - }, - new MenuNodeDto - { - Code = "delivery", - Name = "配送管理", - Path = "/order/delivery", - RequiredPermissions = ["delivery:read"] - } - ] - }, - new MenuNodeDto - { - Code = "inventory", - Name = "库存", - Path = "/inventory", - Icon = "inventory", - RequiredPermissions = ["inventory:read"], - Children = - [ - new MenuNodeDto - { - Code = "inventory-list", - Name = "库存查询", - Path = "/inventory/list", - RequiredPermissions = ["inventory:read"] - }, - new MenuNodeDto - { - Code = "inventory-batch", - Name = "批次管理", - Path = "/inventory/batch", - RequiredPermissions = ["inventory:batch:read"] - } - ] - }, - new MenuNodeDto - { - Code = "payment", - Name = "支付", - Path = "/payment", - Icon = "payment", - RequiredPermissions = ["payment:read"], - Children = - [ - new MenuNodeDto - { - Code = "payment-list", - Name = "支付记录", - Path = "/payment/list", - RequiredPermissions = ["payment:read"] - } - ] - }, - new MenuNodeDto - { - Code = "dictionary", - Name = "字典", + Name = "Dictionary", Path = "/dictionary", - Icon = "dictionary", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "字典管理", + Icon = "ri:book-open-line", + Permissions = ["dictionary:group:read", "dictionary:item:read"] + }, RequiredPermissions = ["dictionary:group:read", "dictionary:item:read"], Children = [ new MenuNodeDto { - Code = "dictionary-group", - Name = "字典分组", - Path = "/dictionary/group", + Name = "DictionaryGroup", + Path = "group", + Component = "/dictionary/group", + Meta = new MenuMetaDto + { + Title = "字典分组", + Icon = "ri:booklet-line", + KeepAlive = true, + Permissions = ["dictionary:group:read"] + }, RequiredPermissions = ["dictionary:group:read"] }, new MenuNodeDto { - Code = "dictionary-item", - Name = "字典项", - Path = "/dictionary/item", + Name = "DictionaryItem", + Path = "item", + Component = "/dictionary/item", + Meta = new MenuMetaDto + { + Title = "字典条目", + Icon = "ri:links-line", + KeepAlive = true, + Permissions = ["dictionary:item:read"] + }, RequiredPermissions = ["dictionary:item:read"] } ] }, new MenuNodeDto { - Code = "identity", - Name = "权限", - Path = "/identity", - Icon = "shield", - RequiredPermissions = ["identity:role:read", "identity:permission:read"], + Name = "Product", + Path = "/product", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "商品中心", + Icon = "ri:shopping-basket-line", + Permissions = ["product:read"] + }, + RequiredPermissions = ["product:read"], Children = [ new MenuNodeDto { - Code = "identity-user", - Name = "用户权限", - Path = "/identity/users", + Name = "ProductList", + Path = "list", + Component = "/product/list", + Meta = new MenuMetaDto + { + Title = "商品列表", + Icon = "ri:shopping-cart-2-line", + KeepAlive = true, + Permissions = ["product:read"] + }, + RequiredPermissions = ["product:read"] + }, + new MenuNodeDto + { + Name = "Category", + Path = "category", + Component = "/product/category", + Meta = new MenuMetaDto + { + Title = "类目管理", + Icon = "ri:folder-settings-line", + KeepAlive = true, + Permissions = ["merchant_category:read"] + }, + RequiredPermissions = ["merchant_category:read"] + } + ] + }, + new MenuNodeDto + { + Name = "Order", + Path = "/order", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "订单中心", + Icon = "ri:bill-line", + Permissions = ["order:read"] + }, + RequiredPermissions = ["order:read"], + Children = + [ + new MenuNodeDto + { + Name = "OrderList", + Path = "list", + Component = "/order/list", + Meta = new MenuMetaDto + { + Title = "订单列表", + Icon = "ri:file-list-3-line", + KeepAlive = true, + Permissions = ["order:read"] + }, + RequiredPermissions = ["order:read"] + }, + new MenuNodeDto + { + Name = "Delivery", + Path = "delivery", + Component = "/order/delivery", + Meta = new MenuMetaDto + { + Title = "配送管理", + Icon = "ri:route-line", + KeepAlive = true, + Permissions = ["delivery:read"] + }, + RequiredPermissions = ["delivery:read"] + } + ] + }, + new MenuNodeDto + { + Name = "Payment", + Path = "/payment", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "支付结算", + Icon = "ri:wallet-3-line", + Permissions = ["payment:read"] + }, + RequiredPermissions = ["payment:read"], + Children = + [ + new MenuNodeDto + { + Name = "PaymentList", + Path = "list", + Component = "/payment/list", + Meta = new MenuMetaDto + { + Title = "收支明细", + Icon = "ri:money-cny-circle-line", + KeepAlive = true, + Permissions = ["payment:read"] + }, + RequiredPermissions = ["payment:read"] + } + ] + }, + new MenuNodeDto + { + Name = "Inventory", + Path = "/inventory", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "库存中心", + Icon = "ri:archive-stack-line", + Permissions = ["inventory:read"] + }, + RequiredPermissions = ["inventory:read"], + Children = + [ + new MenuNodeDto + { + Name = "InventoryList", + Path = "list", + Component = "/inventory/list", + Meta = new MenuMetaDto + { + Title = "库存明细", + Icon = "ri:stock-line", + KeepAlive = true, + Permissions = ["inventory:read"] + }, + RequiredPermissions = ["inventory:read"] + }, + new MenuNodeDto + { + Name = "InventoryBatch", + Path = "batch", + Component = "/inventory/batch", + Meta = new MenuMetaDto + { + Title = "批次管理", + Icon = "ri:layout-grid-line", + KeepAlive = true, + Permissions = ["inventory:batch:read"] + }, + RequiredPermissions = ["inventory:batch:read"] + } + ] + }, + new MenuNodeDto + { + Name = "System", + Path = "/system", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "系统设置", + Icon = "ri:settings-3-line", + Permissions = ["identity:permission:read", "identity:role:read"] + }, + RequiredPermissions = ["identity:permission:read", "identity:role:read"], + Children = + [ + new MenuNodeDto + { + Name = "User", + Path = "user", + Component = "/system/user", + Meta = new MenuMetaDto + { + Title = "用户管理", + Icon = "ri:user-3-line", + KeepAlive = true, + Permissions = ["identity:permission:read"] + }, RequiredPermissions = ["identity:permission:read"] }, new MenuNodeDto { - Code = "identity-role", - Name = "角色管理", - Path = "/identity/roles", + Name = "Role", + Path = "role", + Component = "/system/role", + Meta = new MenuMetaDto + { + Title = "角色管理", + Icon = "ri:shield-user-line", + KeepAlive = true, + Permissions = ["identity:role:read"] + }, RequiredPermissions = ["identity:role:read"] }, new MenuNodeDto { - Code = "identity-permission", - Name = "权限管理", - Path = "/identity/permissions", + Name = "Menus", + Path = "menu", + Component = "/system/menu", + Meta = new MenuMetaDto + { + Title = "菜单管理", + Icon = "ri:menu-fold-line", + KeepAlive = true, + Permissions = ["identity:permission:read"], + AuthList = + [ + new MenuAuthItemDto { Title = "新增", AuthMark = "add" }, + new MenuAuthItemDto { Title = "编辑", AuthMark = "edit" }, + new MenuAuthItemDto { Title = "删除", AuthMark = "delete" } + ] + }, RequiredPermissions = ["identity:permission:read"] } ] }, new MenuNodeDto { - Code = "system", - Name = "系统", - Path = "/system", - Icon = "settings", - RequiredPermissions = ["system-parameter:read", "tenant-announcement:read"], + Name = "Docs", + Path = "/docs", + Component = "/index/index", + Meta = new MenuMetaDto + { + Title = "帮助文档", + Icon = "ri:book-2-line", + Permissions = System.Array.Empty() + }, Children = [ new MenuNodeDto { - Code = "system-parameter", - Name = "系统参数", - Path = "/system/parameters", - RequiredPermissions = ["system-parameter:read"] + Name = "GuideIframe", + Path = "guide", + Component = "", + Meta = new MenuMetaDto + { + Title = "操作指南", + IsIframe = true, + Link = "https://example.com/guide", + Icon = "ri:article-line", + Permissions = System.Array.Empty() + } }, new MenuNodeDto { - Code = "announcement", - Name = "公告管理", - Path = "/system/announcements", - RequiredPermissions = ["tenant-announcement:read"] + Name = "ExternalLink", + Path = "official", + Component = "", + Meta = new MenuMetaDto + { + Title = "官网链接", + Link = "https://example.com", + Icon = "ri:external-link-line", + Permissions = System.Array.Empty() + } } ] } ]; } -} +} \ No newline at end of file