动态路由调整
This commit is contained in:
parent
5052ab0e56
commit
df639d040a
|
|
@ -1,87 +1,60 @@
|
||||||
// 模拟静态数据
|
import request from '@/utils/request'
|
||||||
const mockMenus = [
|
|
||||||
{ menuId: 1, menuName: '系统管理', parentId: 0, orderNum: 1, path: 'system', component: 'Layout', status: '0' },
|
|
||||||
{ menuId: 2, menuName: '用户管理', parentId: 1, orderNum: 1, path: 'user', component: 'system/user/index', status: '0' },
|
|
||||||
{ menuId: 3, menuName: '角色管理', parentId: 1, orderNum: 2, path: 'role', component: 'system/role/index', status: '0' },
|
|
||||||
{ menuId: 4, menuName: '菜单管理', parentId: 1, orderNum: 3, path: 'menu', component: 'system/menu/index', status: '0' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// 查询菜单列表
|
// 查询菜单列表
|
||||||
export function listMenu(query) {
|
export function listMenu(query) {
|
||||||
const { menuName, status } = query;
|
return request({
|
||||||
let filteredMenus = [...mockMenus];
|
url: '/system/menu/list',
|
||||||
|
method: 'get',
|
||||||
if (menuName) {
|
params: query
|
||||||
filteredMenus = filteredMenus.filter(menu => menu.menuName.includes(menuName));
|
})
|
||||||
}
|
|
||||||
if (status) {
|
|
||||||
filteredMenus = filteredMenus.filter(menu => menu.status === status);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve(filteredMenus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单详细
|
// 查询菜单详细
|
||||||
export function getMenu(menuId) {
|
export function getMenu(menuId) {
|
||||||
const menu = mockMenus.find(m => m.menuId === parseInt(menuId));
|
return request({
|
||||||
return Promise.resolve(menu || {});
|
url: '/system/menu/' + menuId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单下拉树结构
|
// 查询菜单下拉树结构
|
||||||
export function treeselect() {
|
export function treeselect() {
|
||||||
const buildTree = (parentId) => {
|
return request({
|
||||||
const children = mockMenus.filter(m => m.parentId === parentId);
|
url: '/system/menu/treeselect',
|
||||||
return children.map(child => ({
|
method: 'get'
|
||||||
id: child.menuId,
|
})
|
||||||
label: child.menuName,
|
|
||||||
children: buildTree(child.menuId)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.resolve(buildTree(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据角色ID查询菜单下拉树结构
|
// 根据角色ID查询菜单下拉树结构
|
||||||
export function roleMenuTreeselect(roleId) {
|
export function roleMenuTreeselect(roleId) {
|
||||||
const buildTree = (parentId) => {
|
return request({
|
||||||
const children = mockMenus.filter(m => m.parentId === parentId);
|
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||||
return children.map(child => ({
|
method: 'get'
|
||||||
id: child.menuId,
|
})
|
||||||
label: child.menuName,
|
|
||||||
children: buildTree(child.menuId)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.resolve({
|
|
||||||
menus: buildTree(0),
|
|
||||||
checkedKeys: [1, 2, 3, 4] // 模拟已选中的菜单
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增菜单
|
// 新增菜单
|
||||||
export function addMenu(data) {
|
export function addMenu(data) {
|
||||||
const newMenu = {
|
return request({
|
||||||
menuId: mockMenus.length + 1,
|
url: '/system/menu',
|
||||||
...data
|
method: 'post',
|
||||||
};
|
data: data
|
||||||
mockMenus.push(newMenu);
|
})
|
||||||
return Promise.resolve({ code: 200, msg: '新增成功' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改菜单
|
// 修改菜单
|
||||||
export function updateMenu(data) {
|
export function updateMenu(data) {
|
||||||
const index = mockMenus.findIndex(m => m.menuId === data.menuId);
|
return request({
|
||||||
if (index !== -1) {
|
url: '/system/menu',
|
||||||
mockMenus[index] = { ...mockMenus[index], ...data };
|
method: 'put',
|
||||||
}
|
data: data
|
||||||
return Promise.resolve({ code: 200, msg: '修改成功' });
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除菜单
|
// 删除菜单
|
||||||
export function delMenu(menuId) {
|
export function delMenu(menuId) {
|
||||||
const index = mockMenus.findIndex(m => m.menuId === parseInt(menuId));
|
return request({
|
||||||
if (index !== -1) {
|
url: '/system/menu/' + menuId,
|
||||||
mockMenus.splice(index, 1);
|
method: 'delete'
|
||||||
}
|
})
|
||||||
return Promise.resolve({ code: 200, msg: '删除成功' });
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const url = ref('http://doc.ruoyi.vip/ruoyi-vue');
|
const url = ref("http://doc.ruoyi.vip/ruoyi-vue");
|
||||||
|
|
||||||
function goto() {
|
function goto() {
|
||||||
window.open(url.value)
|
window.open(url.value);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -22,6 +22,7 @@ import defaultSettings from '@/settings'
|
||||||
import useAppStore from '@/store/modules/app'
|
import useAppStore from '@/store/modules/app'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
const theme = computed(() => settingsStore.theme);
|
const theme = computed(() => settingsStore.theme);
|
||||||
|
|
@ -68,7 +69,13 @@ function setLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
permissionStore.generateRoutes()
|
// 如果路由未加载,则加载路由(避免与路由守卫中的调用冲突)
|
||||||
|
if (permissionStore.addRoutes.length === 0) {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
if (userStore.roles.length > 0) {
|
||||||
|
permissionStore.generateRoutes(userStore.roles)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { isHttp, isPathMatch } from '@/utils/validate'
|
||||||
import { isRelogin } from '@/utils/request'
|
import { isRelogin } from '@/utils/request'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
|
|
||||||
|
|
@ -32,18 +33,39 @@ router.beforeEach((to, from, next) => {
|
||||||
// 判断当前用户是否已拉取完user_info信息
|
// 判断当前用户是否已拉取完user_info信息
|
||||||
useUserStore().getInfo().then(() => {
|
useUserStore().getInfo().then(() => {
|
||||||
isRelogin.show = false
|
isRelogin.show = false
|
||||||
// 不再动态加载路由,直接使用静态路由
|
// 动态加载路由
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
permissionStore.generateRoutes(useUserStore().roles).then(() => {
|
||||||
|
// 动态路由加载完成后,重新跳转到目标路由
|
||||||
next({ ...to, replace: true })
|
next({ ...to, replace: true })
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('路由加载失败:', error)
|
||||||
|
ElMessage.error(error?.message || '路由加载失败,请刷新页面重试')
|
||||||
|
next({ path: '/' })
|
||||||
|
})
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
useUserStore().logOut().then(() => {
|
useUserStore().logOut().then(() => {
|
||||||
ElMessage.error(err)
|
ElMessage.error(err)
|
||||||
next({ path: '/' })
|
next({ path: '/' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
// 已获取用户信息,检查路由是否已加载
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
if (permissionStore.addRoutes.length === 0) {
|
||||||
|
// 路由未加载,重新加载
|
||||||
|
permissionStore.generateRoutes(useUserStore().roles).then(() => {
|
||||||
|
next({ ...to, replace: true })
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('路由加载失败:', error)
|
||||||
|
ElMessage.error(error?.message || '路由加载失败,请刷新页面重试')
|
||||||
|
next({ path: '/' })
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 没有token
|
// 没有token
|
||||||
if (isWhiteList(to.path)) {
|
if (isWhiteList(to.path)) {
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ export const constantRoutes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/order/intention',
|
redirect: 'index',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/index',
|
path: '/index',
|
||||||
|
|
@ -85,109 +85,11 @@ export const constantRoutes = [
|
||||||
meta: { title: '个人中心', icon: 'user' }
|
meta: { title: '个人中心', icon: 'user' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/order',
|
|
||||||
component: Layout,
|
|
||||||
hidden: false,
|
|
||||||
name: 'Order',
|
|
||||||
meta: { title: '订单管理', icon: 'shopping' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'intention',
|
|
||||||
component: () => import('@/views/order/intention/index.vue'),
|
|
||||||
name: 'Intention',
|
|
||||||
meta: { title: '订单/意向单', icon: 'form' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'create',
|
|
||||||
component: () => import('@/views/order/intention/create.vue'),
|
|
||||||
name: 'IntentionCreate',
|
|
||||||
meta: { title: '创建订单/意向单', icon: 'edit', hidden: true }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'search',
|
|
||||||
component: () => import('@/views/order/intention/search.vue'),
|
|
||||||
name: 'IntentionSearch',
|
|
||||||
meta: { title: '品号查询', icon: 'search', hidden: true }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 以下为原动态路由,现在直接合并到静态路由中
|
|
||||||
{
|
|
||||||
path: '/system/user-auth',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['system:user:edit'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'role/:userId(\\d+)',
|
|
||||||
component: () => import('@/views/system/user/authRole.vue'),
|
|
||||||
name: 'AuthRole',
|
|
||||||
meta: { title: '分配角色', activeMenu: '/system/user' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/system/role-auth',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['system:role:edit'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'user/:roleId(\\d+)',
|
|
||||||
component: () => import('@/views/system/role/authUser.vue'),
|
|
||||||
name: 'AuthUser',
|
|
||||||
meta: { title: '分配用户', activeMenu: '/system/role' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/system/dict-data',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['system:dict:list'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index/:dictId(\\d+)',
|
|
||||||
component: () => import('@/views/system/dict/data.vue'),
|
|
||||||
name: 'Data',
|
|
||||||
meta: { title: '字典数据', activeMenu: '/system/dict' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/monitor/job-log',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['monitor:job:list'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index/:jobId(\\d+)',
|
|
||||||
component: () => import('@/views/monitor/job/log.vue'),
|
|
||||||
name: 'JobLog',
|
|
||||||
meta: { title: '调度日志', activeMenu: '/monitor/job' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/tool/gen-edit',
|
|
||||||
component: Layout,
|
|
||||||
hidden: true,
|
|
||||||
permissions: ['tool:gen:edit'],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index/:tableId(\\d+)',
|
|
||||||
component: () => import('@/views/tool/gen/editTable.vue'),
|
|
||||||
name: 'GenEdit',
|
|
||||||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
// 业务路由(订单管理、系统管理等)将从后端动态获取
|
||||||
];
|
];
|
||||||
|
|
||||||
// 动态路由已合并到静态路由中,不再需要
|
// 动态路由将从后端接口获取,不再在此处定义
|
||||||
// export const dynamicRoutes = [ ... ];
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import auth from '@/plugins/auth'
|
import auth from '@/plugins/auth'
|
||||||
import router, { constantRoutes } from '@/router'
|
import router, { constantRoutes } from '@/router'
|
||||||
// 不再需要动态路由,移除 dynamicRoutes 和 getRouters
|
import { getRouters } from '@/api/menu'
|
||||||
// import { getRouters } from '@/api/menu'
|
|
||||||
import Layout from '@/layout/index'
|
import Layout from '@/layout/index'
|
||||||
import ParentView from '@/components/ParentView'
|
import ParentView from '@/components/ParentView'
|
||||||
import InnerLink from '@/layout/components/InnerLink'
|
import InnerLink from '@/layout/components/InnerLink'
|
||||||
|
import { isHttp } from '@/utils/validate'
|
||||||
|
|
||||||
// 匹配views里面所有的.vue文件
|
// 匹配views里面所有的.vue文件
|
||||||
const modules = import.meta.glob('./../../views/**/*.vue')
|
const modules = import.meta.glob('./../../views/**/*.vue')
|
||||||
|
|
@ -34,14 +34,52 @@ const usePermissionStore = defineStore(
|
||||||
this.sidebarRouters = routes
|
this.sidebarRouters = routes
|
||||||
},
|
},
|
||||||
generateRoutes(roles) {
|
generateRoutes(roles) {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve, reject) => {
|
||||||
// 不再调用后端接口,直接使用静态路由
|
// 调用后端接口获取动态路由
|
||||||
const staticRoutes = constantRoutes
|
getRouters().then(res => {
|
||||||
this.setRoutes(staticRoutes)
|
// 确保 res.data 是数组
|
||||||
this.setSidebarRouters(staticRoutes)
|
if (!res.data || !Array.isArray(res.data)) {
|
||||||
this.setDefaultRoutes(staticRoutes)
|
console.error('路由数据格式错误:', res.data)
|
||||||
this.setTopbarRoutes(staticRoutes)
|
reject(new Error('路由数据格式错误,期望数组'))
|
||||||
resolve(staticRoutes)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sdata = JSON.parse(JSON.stringify(res.data))
|
||||||
|
const rdata = JSON.parse(JSON.stringify(res.data))
|
||||||
|
const defaultData = JSON.parse(JSON.stringify(res.data))
|
||||||
|
const sidebarRoutes = filterAsyncRouter(sdata)
|
||||||
|
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
||||||
|
const defaultRoutes = filterAsyncRouter(defaultData)
|
||||||
|
|
||||||
|
// 过滤权限路由
|
||||||
|
const accessedRoutes = filterDynamicRoutes(rewriteRoutes)
|
||||||
|
|
||||||
|
// 调试:打印路由信息
|
||||||
|
console.log('动态路由加载成功,路由数量:', accessedRoutes.length)
|
||||||
|
if (accessedRoutes.length > 0) {
|
||||||
|
console.log('第一个路由示例:', accessedRoutes[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setRoutes(accessedRoutes)
|
||||||
|
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
|
||||||
|
this.setDefaultRoutes(constantRoutes.concat(defaultRoutes))
|
||||||
|
this.setTopbarRoutes(constantRoutes.concat(sidebarRoutes))
|
||||||
|
|
||||||
|
// 动态添加路由到router
|
||||||
|
accessedRoutes.forEach(route => {
|
||||||
|
router.addRoute(route)
|
||||||
|
})
|
||||||
|
|
||||||
|
resolve(accessedRoutes)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('路由处理错误:', error)
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取路由失败:', error)
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,23 +87,63 @@ const usePermissionStore = defineStore(
|
||||||
|
|
||||||
// 遍历后台传来的路由字符串,转换为组件对象
|
// 遍历后台传来的路由字符串,转换为组件对象
|
||||||
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
||||||
return asyncRouterMap.filter(route => {
|
// 确保 asyncRouterMap 是数组
|
||||||
if (type && route.children) {
|
if (!Array.isArray(asyncRouterMap)) {
|
||||||
route.children = filterChildren(route.children)
|
console.error('filterAsyncRouter: 期望数组,但收到:', asyncRouterMap)
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return asyncRouterMap.filter(route => {
|
||||||
|
// 处理外部链接路径:如果 path 是外部链接,需要转换为有效的内部路径
|
||||||
|
if (route.path && isHttp(route.path)) {
|
||||||
|
// 确保 meta.link 保存实际的外部链接
|
||||||
|
if (!route.meta) {
|
||||||
|
route.meta = {}
|
||||||
|
}
|
||||||
|
// 保存原始的外部链接到 meta.link
|
||||||
|
const originalPath = route.path
|
||||||
|
route.meta.link = originalPath
|
||||||
|
|
||||||
|
// 将外部链接路径转换为内部路径
|
||||||
|
// 使用简单的路径格式:/iframe/域名,避免路径冲突
|
||||||
|
const domain = originalPath.replace(/^https?:\/\//, '').replace(/\/.*$/, '')
|
||||||
|
const timestamp = Date.now()
|
||||||
|
const encodedPath = '/iframe/' + encodeURIComponent(domain) + '-' + timestamp
|
||||||
|
|
||||||
|
// 将 path 转换为有效的内部路径
|
||||||
|
route.path = encodedPath
|
||||||
|
} else if (!lastRouter && route.path && !route.path.startsWith('/')) {
|
||||||
|
// 只对顶级路由(lastRouter 为 false)确保路径以 "/" 开头
|
||||||
|
// 子路由的路径会在 filterChildren 中处理
|
||||||
|
route.path = '/' + route.path
|
||||||
|
}
|
||||||
|
|
||||||
if (route.component) {
|
if (route.component) {
|
||||||
|
// 检查是否有外部链接
|
||||||
|
if (route.meta && route.meta.link && (route.meta.link.startsWith('http://') || route.meta.link.startsWith('https://'))) {
|
||||||
|
route.component = InnerLink
|
||||||
|
}
|
||||||
// Layout ParentView 组件特殊处理
|
// Layout ParentView 组件特殊处理
|
||||||
if (route.component === 'Layout') {
|
else if (route.component === 'Layout') {
|
||||||
route.component = Layout
|
route.component = Layout
|
||||||
} else if (route.component === 'ParentView') {
|
} else if (route.component === 'ParentView') {
|
||||||
route.component = ParentView
|
route.component = ParentView
|
||||||
} else if (route.component === 'InnerLink') {
|
} else if (route.component === 'InnerLink') {
|
||||||
route.component = InnerLink
|
route.component = InnerLink
|
||||||
} else {
|
} else {
|
||||||
route.component = loadView(route.component)
|
const component = loadView(route.component)
|
||||||
|
if (!component) {
|
||||||
|
console.warn('找不到组件:', route.component)
|
||||||
|
}
|
||||||
|
route.component = component || (() => import('@/views/error/404.vue'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (route.children != null && route.children && route.children.length) {
|
if (route.children != null && route.children && route.children.length) {
|
||||||
|
// 如果 type 为 true,先使用 filterChildren 处理子路由路径
|
||||||
|
if (type) {
|
||||||
|
route.children = filterChildren(route.children, route)
|
||||||
|
}
|
||||||
|
// 然后递归处理子路由
|
||||||
route.children = filterAsyncRouter(route.children, route, type)
|
route.children = filterAsyncRouter(route.children, route, type)
|
||||||
} else {
|
} else {
|
||||||
delete route['children']
|
delete route['children']
|
||||||
|
|
@ -78,7 +156,22 @@ function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
||||||
function filterChildren(childrenMap, lastRouter = false) {
|
function filterChildren(childrenMap, lastRouter = false) {
|
||||||
var children = []
|
var children = []
|
||||||
childrenMap.forEach(el => {
|
childrenMap.forEach(el => {
|
||||||
el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path
|
if (lastRouter) {
|
||||||
|
// 处理子路由路径:如果子路由路径以 / 开头,需要去掉,因为要拼接父路由路径
|
||||||
|
let childPath = el.path || ''
|
||||||
|
if (childPath.startsWith('/')) {
|
||||||
|
childPath = childPath.substring(1)
|
||||||
|
}
|
||||||
|
// 拼接父路由路径和子路由路径
|
||||||
|
const parentPath = lastRouter.path || ''
|
||||||
|
// 确保父路径以 / 结尾(如果父路径不是根路径)
|
||||||
|
const separator = parentPath.endsWith('/') ? '' : '/'
|
||||||
|
el.path = parentPath + separator + childPath
|
||||||
|
}
|
||||||
|
// 确保最终路径以 / 开头(如果不是空路径)
|
||||||
|
if (el.path && !el.path.startsWith('/')) {
|
||||||
|
el.path = '/' + el.path
|
||||||
|
}
|
||||||
if (el.children && el.children.length && el.component === 'ParentView') {
|
if (el.children && el.children.length && el.component === 'ParentView') {
|
||||||
children = children.concat(filterChildren(el.children, el))
|
children = children.concat(filterChildren(el.children, el))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -100,20 +193,39 @@ export function filterDynamicRoutes(routes) {
|
||||||
if (auth.hasRoleOr(route.roles)) {
|
if (auth.hasRoleOr(route.roles)) {
|
||||||
res.push(route)
|
res.push(route)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 没有权限控制的路由,直接通过
|
||||||
|
res.push(route)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadView = (view) => {
|
export const loadView = (view) => {
|
||||||
let res;
|
if (!view) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理外部链接
|
||||||
|
if (view.startsWith('http://') || view.startsWith('https://')) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = null;
|
||||||
for (const path in modules) {
|
for (const path in modules) {
|
||||||
const dir = path.split('views/')[1].split('.vue')[0];
|
const dir = path.split('views/')[1]?.split('.vue')[0];
|
||||||
if (dir === view) {
|
if (dir && dir === view) {
|
||||||
res = () => modules[path]();
|
res = () => modules[path]();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
console.warn(`loadView: 找不到组件 "${view}",请检查路径是否正确`)
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export default usePermissionStore
|
export default usePermissionStore
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,12 @@ export function mergeRecursive(source, target) {
|
||||||
* @param {*} children 孩子节点字段 默认 'children'
|
* @param {*} children 孩子节点字段 默认 'children'
|
||||||
*/
|
*/
|
||||||
export function handleTree(data, id, parentId, children) {
|
export function handleTree(data, id, parentId, children) {
|
||||||
|
// 检查 data 是否为数组
|
||||||
|
if (!data || !Array.isArray(data)) {
|
||||||
|
console.warn('handleTree: 期望数组,但收到:', data)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
let config = {
|
let config = {
|
||||||
id: id || 'id',
|
id: id || 'id',
|
||||||
parentId: parentId || 'parentId',
|
parentId: parentId || 'parentId',
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="Menu">
|
<script setup name="Menu">
|
||||||
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from "@/api/system/menu";
|
import { addMenu, delMenu, getMenu, listMenu, treeselect, updateMenu } from "@/api/system/menu";
|
||||||
import SvgIcon from "@/components/SvgIcon";
|
import SvgIcon from "@/components/SvgIcon";
|
||||||
import IconSelect from "@/components/IconSelect";
|
import IconSelect from "@/components/IconSelect";
|
||||||
|
|
||||||
|
|
@ -333,7 +333,7 @@ function getList() {
|
||||||
/** 查询菜单下拉树结构 */
|
/** 查询菜单下拉树结构 */
|
||||||
function getTreeselect() {
|
function getTreeselect() {
|
||||||
menuOptions.value = [];
|
menuOptions.value = [];
|
||||||
listMenu().then(response => {
|
treeselect().then(response => {
|
||||||
const menu = { menuId: 0, menuName: "主类目", children: [] };
|
const menu = { menuId: 0, menuName: "主类目", children: [] };
|
||||||
menu.children = proxy.handleTree(response.data, "menuId");
|
menu.children = proxy.handleTree(response.data, "menuId");
|
||||||
menuOptions.value.push(menu);
|
menuOptions.value.push(menu);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue