Compare commits

...

2 Commits

Author SHA1 Message Date
jayhgq
fa88dd8ec2 refactor: 重构权限管理模块为菜单管理模块
1. 前端将权限管理页面替换为菜单管理页面,更新路由配置
2. 后端角色删除逻辑优化,新增角色存在性检查
3. 后端权限删除逻辑增强,自动清理关联数据并调整子权限层级
4. 更新初始化权限数据中的菜单管理配置
2026-06-10 09:47:29 +08:00
jayhgq
12a577f9a3 feat: 实现动态菜单功能,替换硬编码菜单
1.  后端新增menu字段接口和route权限字段,重构权限接口返回菜单树
2.  前端替换硬编码侧边菜单为动态渲染,新增菜单store和图标映射逻辑
3.  调整权限路由元信息和初始化权限数据
2026-06-09 15:57:23 +08:00
9 changed files with 288 additions and 98 deletions

View File

@ -107,6 +107,70 @@ async def get_permission_tree(
return tree return tree
@router.get("/menu", response_model=list[PermissionTreeResponse])
async def get_menu(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取当前用户的菜单树形结构(仅包含有路由的权限)"""
# 获取用户角色的权限
result = await db.execute(
select(Role)
.options(selectinload(Role.permissions))
.filter(Role.id == current_user.role_id)
)
role = result.scalar_one_or_none()
if not role:
raise HTTPException(status_code=404, detail="角色不存在")
# 获取用户有权限的权限编码集合
user_permission_codes = {perm.code for perm in role.permissions}
# 获取所有顶级模块和二级模块权限有route字段的
result = await db.execute(
select(Permission)
.filter(Permission.route.isnot(None))
)
all_menu_permissions = result.scalars().all()
# 过滤用户有权限的菜单
user_menu_permissions = []
for perm in all_menu_permissions:
# 检查用户是否有该权限或其子权限
has_access = False
for user_perm_code in user_permission_codes:
if user_perm_code.startswith(perm.code):
has_access = True
break
if has_access:
user_menu_permissions.append(perm)
# 转换为字典,便于查找
perm_dict = {perm.id: {
"id": perm.id,
"name": perm.name,
"code": perm.code,
"parentid": perm.parentid,
"route": perm.route,
"description": perm.description,
"children": []
} for perm in user_menu_permissions}
# 构建树形结构
tree = []
for perm_id, perm_data in perm_dict.items():
parentid = perm_data["parentid"]
if parentid == 0:
# 顶级节点
tree.append(perm_data)
elif parentid in perm_dict:
# 子节点,添加到父节点的 children
perm_dict[parentid]["children"].append(perm_data)
return tree
@router.get("/{permission_id}", response_model=PermissionResponse) @router.get("/{permission_id}", response_model=PermissionResponse)
async def get_permission( async def get_permission(
permission_id: int, permission_id: int,
@ -154,15 +218,33 @@ async def delete_permission(
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user), current_user: User = Depends(get_current_user),
): ):
"""删除权限""" """删除权限(自动清理角色关联和子权限关联)"""
# 检查权限是否存在 # 检查权限是否存在
result = await db.execute(select(Permission).filter(Permission.id == permission_id)) result = await db.execute(select(Permission).filter(Permission.id == permission_id))
existing_permission = result.scalar_one_or_none() existing_permission = result.scalar_one_or_none()
if not existing_permission: if not existing_permission:
raise HTTPException(status_code=404, detail="权限不存在") raise HTTPException(status_code=404, detail="权限不存在")
# 使用FastCRUD删除权限 # 1. 删除角色-权限关联表中的记录
deleted = await permission_crud.delete(db, id=permission_id) from sqlalchemy import delete as sql_delete
from models import role_permission
stmt = sql_delete(role_permission).where(role_permission.c.permission_id == permission_id)
await db.execute(stmt)
# 2. 处理子权限如果有子权限将子权限的父ID置为0提升为顶级节点
from sqlalchemy import update as sql_update
stmt = (
sql_update(Permission)
.where(Permission.parentid == permission_id)
.values(parentid=0)
)
await db.execute(stmt)
# 3. 使用FastCRUD删除权限本身不提交与前述操作在同一事务中
await permission_crud.delete(db, id=permission_id, commit=False)
await db.commit()
return {"message": "权限删除成功"} return {"message": "权限删除成功"}
@ -227,7 +309,7 @@ async def get_current_user_permissions(
db: AsyncSession = Depends(get_db), db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user), current_user: User = Depends(get_current_user),
): ):
"""获取当前登录用户的权限列表""" """获取当前登录用户的权限列表和菜单"""
# 获取用户角色的权限 # 获取用户角色的权限
result = await db.execute( result = await db.execute(
select(Role) select(Role)
@ -239,9 +321,54 @@ async def get_current_user_permissions(
if not role: if not role:
raise HTTPException(status_code=404, detail="角色不存在") raise HTTPException(status_code=404, detail="角色不存在")
# 返回用户权限信息 # 获取用户有权限的权限编码集合
user_permission_codes = {perm.code for perm in role.permissions}
# 获取所有顶级模块和二级模块权限有route字段的
result = await db.execute(
select(Permission)
.filter(Permission.route.isnot(None))
)
all_menu_permissions = result.scalars().all()
# 过滤用户有权限的菜单
user_menu_permissions = []
for perm in all_menu_permissions:
# 检查用户是否有该权限或其子权限
has_access = False
for user_perm_code in user_permission_codes:
if user_perm_code.startswith(perm.code):
has_access = True
break
if has_access:
user_menu_permissions.append(perm)
# 转换为字典,便于查找
perm_dict = {perm.id: {
"id": perm.id,
"name": perm.name,
"code": perm.code,
"parentid": perm.parentid,
"route": perm.route,
"description": perm.description,
"children": []
} for perm in user_menu_permissions}
# 构建树形结构
menu_tree = []
for perm_id, perm_data in perm_dict.items():
parentid = perm_data["parentid"]
if parentid == 0:
# 顶级节点
menu_tree.append(perm_data)
elif parentid in perm_dict:
# 子节点,添加到父节点的 children
perm_dict[parentid]["children"].append(perm_data)
# 返回用户权限信息和菜单
return RolePermissionResponse( return RolePermissionResponse(
role_id=role.id, role_id=role.id,
role_name=role.name, role_name=role.name,
permissions=[PermissionResponse.from_orm(p) for p in role.permissions] permissions=[PermissionResponse.from_orm(p) for p in role.permissions],
) menu=menu_tree
)

View File

@ -125,9 +125,13 @@ async def delete_role(
current_user: User = Depends(get_current_user), current_user: User = Depends(get_current_user),
): ):
"""删除角色""" """删除角色"""
# 检查是否有用户使用此角色 # 检查角色是否存在
from sqlalchemy import select result = await db.execute(select(Role).filter(Role.id == role_id))
existing_role = result.scalar_one_or_none()
if not existing_role:
raise HTTPException(status_code=404, detail="角色不存在")
# 检查是否有用户使用此角色
result = await db.execute(select(User).filter(User.role_id == role_id)) result = await db.execute(select(User).filter(User.role_id == role_id))
users = result.scalars().all() users = result.scalars().all()
if users: if users:
@ -135,14 +139,6 @@ async def delete_role(
status_code=400, detail=f"该角色正在被 {len(users)} 个用户使用,无法删除" status_code=400, detail=f"该角色正在被 {len(users)} 个用户使用,无法删除"
) )
# 使用原生 SQLAlchemy 删除角色FastCRUD 有问题) # 使用FastCRUD删除角色
from sqlalchemy import delete as sql_delete await role_crud.delete(db, id=role_id)
stmt = sql_delete(Role).where(Role.id == role_id)
result = await db.execute(stmt)
if result.rowcount == 0:
raise HTTPException(status_code=404, detail="角色不存在")
await db.commit()
return {"message": "角色删除成功"} return {"message": "角色删除成功"}

View File

@ -1,6 +1,6 @@
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, text from sqlalchemy import select, text
from models import Role, User, Permission from models import Role, User, Permission, SystemSetting
from utils.password import get_password_hash from utils.password import get_password_hash
@ -32,6 +32,14 @@ async def ensure_permission_table_structure(db: AsyncSession):
except Exception as e: except Exception as e:
print(f"添加 parentid 字段失败: {e}") print(f"添加 parentid 字段失败: {e}")
# 添加 route 字段
if 'route' not in existing_columns:
try:
await db.execute(text("ALTER TABLE permission ADD COLUMN IF NOT EXISTS route VARCHAR(255)"))
print("添加 route 字段成功")
except Exception as e:
print(f"添加 route 字段失败: {e}")
# 删除旧字段(如果存在) # 删除旧字段(如果存在)
for old_column in ['module', 'submodule', 'action']: for old_column in ['module', 'submodule', 'action']:
if old_column in existing_columns: if old_column in existing_columns:
@ -53,21 +61,21 @@ async def init_permissions(db: AsyncSession):
# ======================================== # ========================================
# 顶级模块 # 顶级模块
# ======================================== # ========================================
{"name": "权限管理", "code": "perm", "parentid": 0, "description": "权限管理模块"}, {"name": "权限管理", "code": "perm", "parentid": 0, "route": "/admin/permissions", "description": "权限管理模块"},
{"name": "系统设置", "code": "system", "parentid": 0, "description": "系统设置模块"}, {"name": "系统设置", "code": "system", "parentid": 0, "route": "/admin/settings", "description": "系统设置模块"},
# ======================================== # ========================================
# 权限管理 -> 二级子模块 # 权限管理 -> 二级子模块
# ======================================== # ========================================
{"name": "用户管理", "code": "perm:user", "parentid": 1, "description": "用户管理子模块"}, {"name": "用户管理", "code": "perm:user", "parentid": 1, "route": "/admin/users", "description": "用户管理子模块"},
{"name": "角色管理", "code": "perm:role", "parentid": 1, "description": "角色管理子模块"}, {"name": "角色管理", "code": "perm:role", "parentid": 1, "route": "/admin/roles", "description": "角色管理子模块"},
{"name": "权限配置", "code": "perm:config", "parentid": 1, "description": "权限配置子模块"}, {"name": "菜单管理", "code": "perm:menu", "parentid": 1, "route": "/admin/menu", "description": "菜单管理子模块"},
# ======================================== # ========================================
# 系统设置 -> 二级子模块 # 系统设置 -> 二级子模块
# ======================================== # ========================================
{"name": "基础配置", "code": "system:basic", "parentid": 2, "description": "系统基础配置"}, {"name": "基础配置", "code": "system:basic", "parentid": 2, "route": "/admin/settings", "description": "系统基础配置"},
{"name": "高级配置", "code": "system:advanced", "parentid": 2, "description": "系统高级配置"}, {"name": "高级配置", "code": "system:advanced", "parentid": 2, "route": "/admin/settings", "description": "系统高级配置"},
# ======================================== # ========================================
# 用户管理操作parentid=3 # 用户管理操作parentid=3
@ -96,13 +104,13 @@ async def init_permissions(db: AsyncSession):
# ======================================== # ========================================
# 权限配置操作parentid=5 # 权限配置操作parentid=5
# ======================================== # ========================================
{"name": "查看列表", "code": "perm:config:list", "parentid": 5, "description": "查看权限列表"}, {"name": "查看列表", "code": "perm:menu:list", "parentid": 5, "description": "查看权限列表"},
{"name": "查看详情", "code": "perm:config:detail", "parentid": 5, "description": "查看权限详情"}, {"name": "查看详情", "code": "perm:menu:detail", "parentid": 5, "description": "查看权限详情"},
{"name": "搜索", "code": "perm:config:search", "parentid": 5, "description": "搜索权限"}, {"name": "搜索", "code": "perm:menu:search", "parentid": 5, "description": "搜索权限"},
{"name": "创建", "code": "perm:config:create", "parentid": 5, "description": "创建新权限"}, {"name": "创建", "code": "perm:menu:create", "parentid": 5, "description": "创建新权限"},
{"name": "编辑", "code": "perm:config:edit", "parentid": 5, "description": "编辑权限信息"}, {"name": "编辑", "code": "perm:menu:edit", "parentid": 5, "description": "编辑权限信息"},
{"name": "删除", "code": "perm:config:delete", "parentid": 5, "description": "删除权限"}, {"name": "删除", "code": "perm:menu:delete", "parentid": 5, "description": "删除权限"},
{"name": "导出", "code": "perm:config:export", "parentid": 5, "description": "导岀权限数据"}, {"name": "导出", "code": "perm:menu:export", "parentid": 5, "description": "导岀权限数据"},
# ======================================== # ========================================
# 系统设置 -> 基础配置操作parentid=6 # 系统设置 -> 基础配置操作parentid=6
@ -143,6 +151,9 @@ async def init_permissions(db: AsyncSession):
if existing_perm.parentid != perm_data["parentid"]: if existing_perm.parentid != perm_data["parentid"]:
existing_perm.parentid = perm_data["parentid"] existing_perm.parentid = perm_data["parentid"]
update_needed = True update_needed = True
if existing_perm.route != perm_data.get("route"):
existing_perm.route = perm_data.get("route")
update_needed = True
if existing_perm.description != perm_data["description"]: if existing_perm.description != perm_data["description"]:
existing_perm.description = perm_data["description"] existing_perm.description = perm_data["description"]
update_needed = True update_needed = True
@ -383,6 +394,60 @@ async def init_test_user(db: AsyncSession):
print("测试用户初始化/更新完成") print("测试用户初始化/更新完成")
async def init_system_settings(db: AsyncSession):
"""初始化系统设置数据"""
# 定义需要初始化的系统设置
system_settings = [
{
"key": "系统标题",
"value": "图书管理系统",
"type": "string",
"description": "系统显示标题"
},
]
# 检查现有系统设置
result = await db.execute(select(SystemSetting))
existing_settings = result.scalars().all()
existing_setting_keys = {setting.key: setting for setting in existing_settings}
# 创建或更新系统设置
created_count = 0
updated_count = 0
for setting_data in system_settings:
setting_key = setting_data["key"]
if setting_key not in existing_setting_keys:
# 设置不存在,需要创建
db.add(SystemSetting(**setting_data))
created_count += 1
else:
# 设置存在,检查是否需要更新
existing_setting = existing_setting_keys[setting_key]
update_needed = False
if existing_setting.value != setting_data["value"]:
existing_setting.value = setting_data["value"]
update_needed = True
if existing_setting.type != setting_data["type"]:
existing_setting.type = setting_data["type"]
update_needed = True
if existing_setting.description != setting_data.get("description"):
existing_setting.description = setting_data.get("description")
update_needed = True
if update_needed:
updated_count += 1
if created_count > 0 or updated_count > 0:
await db.commit()
print(f"创建了 {created_count} 个系统设置,更新了 {updated_count} 个系统设置")
else:
print("系统设置数据已存在且无需更新,跳过初始化")
print("系统设置数据初始化/更新完成")
async def init_all_data(db: AsyncSession): async def init_all_data(db: AsyncSession):
"""初始化所有数据""" """初始化所有数据"""
print("开始初始化数据库数据...") print("开始初始化数据库数据...")
@ -392,4 +457,5 @@ async def init_all_data(db: AsyncSession):
await init_role_permissions(db) await init_role_permissions(db)
await init_admin_user(db) await init_admin_user(db)
await init_test_user(db) await init_test_user(db)
await init_system_settings(db)
print("数据库数据初始化完成") print("数据库数据初始化完成")

View File

@ -59,6 +59,7 @@ class Permission(Base):
name = Column(String(100), nullable=False, comment="权限名称") name = Column(String(100), nullable=False, comment="权限名称")
code = Column(String(100), nullable=False, unique=True, comment="权限编码") code = Column(String(100), nullable=False, unique=True, comment="权限编码")
parentid = Column(Integer, default=0, comment="父权限ID0表示顶级模块") parentid = Column(Integer, default=0, comment="父权限ID0表示顶级模块")
route = Column(String(255), nullable=True, comment="前端路由路径")
description = Column(String(255), nullable=True, comment="权限描述") description = Column(String(255), nullable=True, comment="权限描述")
createtime = Column(DateTime, default=datetime.utcnow, comment="创建时间") createtime = Column(DateTime, default=datetime.utcnow, comment="创建时间")
updatetime = Column( updatetime = Column(

View File

@ -139,6 +139,7 @@ class PermissionBase(BaseModel):
name: str = Field(..., max_length=100, description="权限名称") name: str = Field(..., max_length=100, description="权限名称")
code: str = Field(..., max_length=100, description="权限编码") code: str = Field(..., max_length=100, description="权限编码")
parentid: int = Field(0, description="父权限ID0表示顶级模块") parentid: int = Field(0, description="父权限ID0表示顶级模块")
route: Optional[str] = Field(None, max_length=255, description="前端路由路径")
description: Optional[str] = Field(None, max_length=255, description="权限描述") description: Optional[str] = Field(None, max_length=255, description="权限描述")
@ -165,6 +166,7 @@ class PermissionTreeResponse(BaseModel):
name: str = Field(..., description="权限名称") name: str = Field(..., description="权限名称")
code: str = Field(..., description="权限编码") code: str = Field(..., description="权限编码")
parentid: int = Field(..., description="父权限ID") parentid: int = Field(..., description="父权限ID")
route: Optional[str] = Field(None, description="前端路由路径")
description: Optional[str] = Field(None, description="权限描述") description: Optional[str] = Field(None, description="权限描述")
children: list = Field([], description="子权限列表") children: list = Field([], description="子权限列表")
@ -183,6 +185,7 @@ class RolePermissionResponse(BaseModel):
role_id: int = Field(..., description="角色ID") role_id: int = Field(..., description="角色ID")
role_name: str = Field(..., description="角色名称") role_name: str = Field(..., description="角色名称")
permissions: list[PermissionResponse] = Field(..., description="权限列表") permissions: list[PermissionResponse] = Field(..., description="权限列表")
menu: list = Field([], description="菜单树形结构")
# 系统设置相关模型 # 系统设置相关模型

View File

@ -9,47 +9,34 @@
text-color="#e2e8f0" text-color="#e2e8f0"
active-text-color="#38bdf8" active-text-color="#38bdf8"
> >
<!-- 权限管理一级菜单 --> <!-- 动态渲染菜单 -->
<el-sub-menu <template v-for="menu in auth.menu" :key="menu.id">
v-if="hasAnyPermission" <!-- 有子菜单的顶级菜单 -->
index="/admin/permissions" <el-sub-menu
> v-if="menu.children && menu.children.length > 0"
<template #title> :index="menu.route"
<el-icon><Key /></el-icon>
<span>权限管理</span>
</template>
<el-menu-item
v-if="hasUserPermission"
index="/admin/users"
:route="{ name: 'admin-users' }"
> >
<span>用户管理</span> <template #title>
</el-menu-item> <el-icon><component :is="getIcon(menu.code)" /></el-icon>
<span>{{ menu.name }}</span>
</template>
<el-menu-item
v-for="child in menu.children"
:key="child.id"
:index="child.route"
>
<span>{{ child.name }}</span>
</el-menu-item>
</el-sub-menu>
<!-- 无子菜单的顶级菜单 -->
<el-menu-item <el-menu-item
v-if="hasRolePermission" v-else
index="/admin/roles" :index="menu.route"
:route="{ name: 'admin-roles' }"
> >
<span>角色管理</span> <el-icon><component :is="getIcon(menu.code)" /></el-icon>
<span>{{ menu.name }}</span>
</el-menu-item> </el-menu-item>
<el-menu-item </template>
v-if="hasPermissionPermission"
index="/admin/permissions"
:route="{ name: 'admin-permissions' }"
>
<span>权限管理</span>
</el-menu-item>
</el-sub-menu>
<!-- 系统设置一级菜单 -->
<el-menu-item
v-if="hasSystemPermission"
index="/admin/settings"
:route="{ name: 'admin-settings' }"
>
<el-icon><Setting /></el-icon>
<span>系统设置</span>
</el-menu-item>
</el-menu> </el-menu>
</el-aside> </el-aside>
<el-container> <el-container>
@ -72,9 +59,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref, markRaw } from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { Key, Setting } from "@element-plus/icons-vue"; import { Key, Setting, Folder } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { useAuthStore } from "@/modules/admin/stores/auth"; import { useAuthStore } from "@/modules/admin/stores/auth";
import axios from "axios"; import axios from "axios";
@ -87,25 +74,21 @@ const sysTitle = ref("");
const active = computed(() => route.path); const active = computed(() => route.path);
const pageTitle = computed(() => (route.meta.title as string) || ""); const pageTitle = computed(() => (route.meta.title as string) || "");
const hasUserPermission = computed(() => { //
return auth.permissions.some(p => p.code.startsWith("perm:user")); const iconMap: Record<string, any> = {
}); "perm": markRaw(Key),
"system": markRaw(Setting),
};
const hasRolePermission = computed(() => { function getIcon(code: string) {
return auth.permissions.some(p => p.code.startsWith("perm:role")); //
}); for (const key in iconMap) {
if (code.startsWith(key)) {
const hasPermissionPermission = computed(() => { return iconMap[key];
return auth.permissions.some(p => p.code.startsWith("perm:config")); }
}); }
return Folder;
const hasSystemPermission = computed(() => { }
return auth.permissions.some(p => p.code.startsWith("system:"));
});
const hasAnyPermission = computed(() => {
return hasUserPermission.value || hasRolePermission.value || hasPermissionPermission.value;
});
async function getSysTitle() { async function getSysTitle() {
const baseURL = import.meta.env.VITE_API_BASE || "/api"; const baseURL = import.meta.env.VITE_API_BASE || "/api";
@ -115,10 +98,9 @@ async function getSysTitle() {
} }
} }
onMounted(async () => { onMounted(async () => {
try { try {
getSysTitle(); await getSysTitle();
if (!auth.user) { if (!auth.user) {
await auth.fetchProfile(); await auth.fetchProfile();
} }

View File

@ -19,16 +19,28 @@ export type Permission = {
description?: string | null; description?: string | null;
}; };
export type MenuItem = {
id: number;
name: string;
code: string;
parentid: number;
route: string;
description?: string;
children: MenuItem[];
};
export type RolePermissionResponse = { export type RolePermissionResponse = {
role_id: number; role_id: number;
role_name: string; role_name: string;
permissions: Permission[]; permissions: Permission[];
menu: MenuItem[];
}; };
export const useAuthStore = defineStore("auth", () => { export const useAuthStore = defineStore("auth", () => {
const token = ref<string>(localStorage.getItem("access_token") || ""); const token = ref<string>(localStorage.getItem("access_token") || "");
const user = ref<UserProfile | null>(null); const user = ref<UserProfile | null>(null);
const permissions = ref<Permission[]>([]); const permissions = ref<Permission[]>([]);
const menu = ref<MenuItem[]>([]);
const isAuthenticated = computed(() => Boolean(token.value)); const isAuthenticated = computed(() => Boolean(token.value));
@ -52,7 +64,8 @@ export const useAuthStore = defineStore("auth", () => {
async function fetchPermissions() { async function fetchPermissions() {
const { data } = await http.get<RolePermissionResponse>("/permissions/user/me"); const { data } = await http.get<RolePermissionResponse>("/permissions/user/me");
permissions.value = data.permissions || []; permissions.value = data.permissions || [];
return permissions.value; menu.value = data.menu || [];
return { permissions: permissions.value, menu: menu.value };
} }
async function loginAndFetchData() { async function loginAndFetchData() {
@ -76,6 +89,7 @@ export const useAuthStore = defineStore("auth", () => {
} }
user.value = null; user.value = null;
permissions.value = []; permissions.value = [];
menu.value = [];
setToken(""); setToken("");
} }
@ -83,6 +97,7 @@ export const useAuthStore = defineStore("auth", () => {
token, token,
user, user,
permissions, permissions,
menu,
permissionCodes, permissionCodes,
isAuthenticated, isAuthenticated,
setToken, setToken,

View File

@ -39,11 +39,11 @@ const router = createRouter({
}, },
{ {
path: "permissions", path: "menu",
name: "admin-permissions", name: "admin-menu",
meta: { title: "权限管理", permission: "perm:config" }, meta: { title: "菜单管理", permission: "perm:menu" },
component: () => component: () =>
import("@/modules/admin/views/permissions/PermissionManage.vue"), import("@/modules/admin/views/permissions/MenuManage.vue"),
}, },
{ {
path: "settings", path: "settings",