chore: 修复 Docker 配置并调整菜单排序

This commit is contained in:
2025-12-05 15:09:46 +08:00
parent a42f470113
commit 1994ed6061
6 changed files with 551 additions and 160 deletions

View File

@@ -3,10 +3,21 @@
"TakeoutSaaS.AdminApi": { "TakeoutSaaS.AdminApi": {
"commandName": "Project", "commandName": "Project",
"launchBrowser": true, "launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"applicationUrl": "http://localhost:2680" "applicationUrl": "http://localhost:2680"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"publishAllPorts": true,
"useSSL": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
} }
} }
} }

View File

@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace TakeoutSaaS.Application.Identity.Contracts;
/// <summary>
/// 菜单操作权限描述。
/// </summary>
public sealed record MenuAuthItemDto
{
/// <summary>
/// 操作名称。
/// </summary>
[JsonPropertyName("title")]
public required string Title { get; init; }
/// <summary>
/// 权限标识。
/// </summary>
[JsonPropertyName("authMark")]
public required string AuthMark { get; init; }
}

View File

@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace TakeoutSaaS.Application.Identity.Contracts;
/// <summary>
/// 菜单前端元数据。
/// </summary>
public sealed record MenuMetaDto
{
/// <summary>
/// 菜单标题i18n key
/// </summary>
[JsonPropertyName("title")]
public required string Title { get; init; }
/// <summary>
/// 图标标识。
/// </summary>
[JsonPropertyName("icon")]
public string? Icon { get; init; }
/// <summary>
/// 是否缓存页面。
/// </summary>
[JsonPropertyName("keepAlive")]
public bool KeepAlive { get; init; }
/// <summary>
/// 是否为 iframe 页面。
/// </summary>
[JsonPropertyName("isIframe")]
public bool IsIframe { get; init; }
/// <summary>
/// 外链或 iframe 地址。
/// </summary>
[JsonPropertyName("link")]
public string? Link { get; init; }
/// <summary>
/// 允许访问的角色编码集合。
/// </summary>
[JsonPropertyName("roles")]
public string[] Roles { get; init; } = System.Array.Empty<string>();
/// <summary>
/// 按钮/操作级别权限。
/// </summary>
[JsonPropertyName("authList")]
public IReadOnlyList<MenuAuthItemDto> AuthList { get; init; } = System.Array.Empty<MenuAuthItemDto>();
/// <summary>
/// 访问该菜单所需的权限编码集合(前端用于路由级鉴权)。
/// </summary>
[JsonPropertyName("permissions")]
public string[] Permissions { get; init; } = System.Array.Empty<string>();
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace TakeoutSaaS.Application.Identity.Contracts; namespace TakeoutSaaS.Application.Identity.Contracts;
@@ -8,32 +9,37 @@ namespace TakeoutSaaS.Application.Identity.Contracts;
public sealed record MenuNodeDto public sealed record MenuNodeDto
{ {
/// <summary> /// <summary>
/// 菜单编码(唯一标识) /// 菜单名称
/// </summary> /// </summary>
public string Code { get; init; } = string.Empty; [JsonPropertyName("name")]
public required string Name { get; init; }
/// <summary> /// <summary>
/// 展示名称(中文)。 /// 路由路径(顶级以 / 开头,子级为相对路径)。
/// </summary> /// </summary>
public string Name { get; init; } = string.Empty; [JsonPropertyName("path")]
public required string Path { get; init; }
/// <summary> /// <summary>
/// 前端路由路径 /// 前端组件路径(基于 src/views不含 .vue
/// </summary> /// </summary>
public string Path { get; init; } = string.Empty; [JsonPropertyName("component")]
public required string Component { get; init; }
/// <summary> /// <summary>
/// 可选图标标识 /// 前端渲染元数据
/// </summary> /// </summary>
public string? Icon { get; init; } [JsonPropertyName("meta")]
public required MenuMetaDto Meta { get; init; }
/// <summary>
/// 访问该菜单所需的任一权限,留空表示公共可见。
/// </summary>
public string[] RequiredPermissions { get; init; } = System.Array.Empty<string>();
/// <summary> /// <summary>
/// 子菜单集合。 /// 子菜单集合。
/// </summary> /// </summary>
[JsonPropertyName("children")]
public IReadOnlyList<MenuNodeDto> Children { get; init; } = System.Array.Empty<MenuNodeDto>(); public IReadOnlyList<MenuNodeDto> Children { get; init; } = System.Array.Empty<MenuNodeDto>();
/// <summary>
/// 访问该菜单所需的任一权限(仅用于后端过滤,不序列化)。
/// </summary>
internal string[] RequiredPermissions { get; init; } = System.Array.Empty<string>();
} }

View File

@@ -29,7 +29,10 @@ public static class AdminMenuProvider
var result = new List<MenuNodeDto>(); var result = new List<MenuNodeDto>();
foreach (var node in nodes) foreach (var node in nodes)
{ {
// 1.1 递归过滤子节点
var filteredChildren = Filter(node.Children, permissionSet); var filteredChildren = Filter(node.Children, permissionSet);
// 1.2 判断当前节点是否可见
var visible = node.RequiredPermissions.Length == 0 || node.RequiredPermissions.Any(permissionSet.Contains); var visible = node.RequiredPermissions.Length == 0 || node.RequiredPermissions.Any(permissionSet.Contains);
if (visible || filteredChildren.Count > 0) if (visible || filteredChildren.Count > 0)
{ {
@@ -43,223 +46,515 @@ public static class AdminMenuProvider
private static IReadOnlyList<MenuNodeDto> GetMenuDefinitions() private static IReadOnlyList<MenuNodeDto> GetMenuDefinitions()
{ {
// 1. 顶部菜单定义 // 1. 顶部菜单定义(按需求自定义排序)
return return
[ [
new MenuNodeDto new MenuNodeDto
{ {
Code = "dashboard", Name = "Dashboard",
Name = "仪表盘",
Path = "/dashboard", Path = "/dashboard",
Icon = "dashboard" Component = "/index/index",
Meta = new MenuMetaDto
{
Title = "仪表盘",
Icon = "ri:pie-chart-line",
Permissions = System.Array.Empty<string>()
},
Children =
[
new MenuNodeDto
{
Name = "Console",
Path = "console",
Component = "/dashboard/console",
Meta = new MenuMetaDto
{
Title = "工作台",
Icon = "ri:dashboard-line",
KeepAlive = false,
Permissions = System.Array.Empty<string>()
},
RequiredPermissions = System.Array.Empty<string>()
}
]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "merchant", Name = "Tenant",
Name = "商户", 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", Path = "/merchant",
Icon = "store", Component = "/index/index",
Meta = new MenuMetaDto
{
Title = "商户中心",
Icon = "ri:store-2-line",
Permissions = ["merchant:read"]
},
RequiredPermissions = ["merchant:read"], RequiredPermissions = ["merchant:read"],
Children = Children =
[ [
new MenuNodeDto new MenuNodeDto
{ {
Code = "merchant-list", Name = "MerchantList",
Name = "商户管理", Path = "list",
Path = "/merchant/list", Component = "/merchant/list",
Meta = new MenuMetaDto
{
Title = "商户列表",
Icon = "ri:community-line",
KeepAlive = true,
Permissions = ["merchant:read"]
},
RequiredPermissions = ["merchant:read"] RequiredPermissions = ["merchant:read"]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "store", Name = "Store",
Name = "门店管理", Path = "store",
Path = "/merchant/stores", Component = "/merchant/store",
Meta = new MenuMetaDto
{
Title = "门店管理",
Icon = "ri:store-line",
KeepAlive = true,
Permissions = ["store:read"]
},
RequiredPermissions = ["store:read"] RequiredPermissions = ["store:read"]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "staff", Name = "Staff",
Name = "员工管理", Path = "staff",
Path = "/merchant/staff", Component = "/merchant/staff",
Meta = new MenuMetaDto
{
Title = "员工管理",
Icon = "ri:team-line",
KeepAlive = true,
Permissions = ["store-staff:read"]
},
RequiredPermissions = ["store-staff:read"] RequiredPermissions = ["store-staff:read"]
} }
] ]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "product", Name = "Dictionary",
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 = "字典",
Path = "/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"], RequiredPermissions = ["dictionary:group:read", "dictionary:item:read"],
Children = Children =
[ [
new MenuNodeDto new MenuNodeDto
{ {
Code = "dictionary-group", Name = "DictionaryGroup",
Name = "字典分组", Path = "group",
Path = "/dictionary/group", Component = "/dictionary/group",
Meta = new MenuMetaDto
{
Title = "字典分组",
Icon = "ri:booklet-line",
KeepAlive = true,
Permissions = ["dictionary:group:read"]
},
RequiredPermissions = ["dictionary:group:read"] RequiredPermissions = ["dictionary:group:read"]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "dictionary-item", Name = "DictionaryItem",
Name = "字典项", Path = "item",
Path = "/dictionary/item", Component = "/dictionary/item",
Meta = new MenuMetaDto
{
Title = "字典条目",
Icon = "ri:links-line",
KeepAlive = true,
Permissions = ["dictionary:item:read"]
},
RequiredPermissions = ["dictionary:item:read"] RequiredPermissions = ["dictionary:item:read"]
} }
] ]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "identity", Name = "Product",
Name = "权限", Path = "/product",
Path = "/identity", Component = "/index/index",
Icon = "shield", Meta = new MenuMetaDto
RequiredPermissions = ["identity:role:read", "identity:permission:read"], {
Title = "商品中心",
Icon = "ri:shopping-basket-line",
Permissions = ["product:read"]
},
RequiredPermissions = ["product:read"],
Children = Children =
[ [
new MenuNodeDto new MenuNodeDto
{ {
Code = "identity-user", Name = "ProductList",
Name = "用户权限", Path = "list",
Path = "/identity/users", 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"] RequiredPermissions = ["identity:permission:read"]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "identity-role", Name = "Role",
Name = "角色管理", Path = "role",
Path = "/identity/roles", Component = "/system/role",
Meta = new MenuMetaDto
{
Title = "角色管理",
Icon = "ri:shield-user-line",
KeepAlive = true,
Permissions = ["identity:role:read"]
},
RequiredPermissions = ["identity:role:read"] RequiredPermissions = ["identity:role:read"]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "identity-permission", Name = "Menus",
Name = "权限管理", Path = "menu",
Path = "/identity/permissions", 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"] RequiredPermissions = ["identity:permission:read"]
} }
] ]
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "system", Name = "Docs",
Name = "系统", Path = "/docs",
Path = "/system", Component = "/index/index",
Icon = "settings", Meta = new MenuMetaDto
RequiredPermissions = ["system-parameter:read", "tenant-announcement:read"], {
Title = "帮助文档",
Icon = "ri:book-2-line",
Permissions = System.Array.Empty<string>()
},
Children = Children =
[ [
new MenuNodeDto new MenuNodeDto
{ {
Code = "system-parameter", Name = "GuideIframe",
Name = "系统参数", Path = "guide",
Path = "/system/parameters", Component = "",
RequiredPermissions = ["system-parameter:read"] Meta = new MenuMetaDto
{
Title = "操作指南",
IsIframe = true,
Link = "https://example.com/guide",
Icon = "ri:article-line",
Permissions = System.Array.Empty<string>()
}
}, },
new MenuNodeDto new MenuNodeDto
{ {
Code = "announcement", Name = "ExternalLink",
Name = "公告管理", Path = "official",
Path = "/system/announcements", Component = "",
RequiredPermissions = ["tenant-announcement:read"] Meta = new MenuMetaDto
{
Title = "官网链接",
Link = "https://example.com",
Icon = "ri:external-link-line",
Permissions = System.Array.Empty<string>()
}
} }
] ]
} }
]; ];
} }
} }