修改菜单逻辑.
This commit is contained in:
@@ -30,6 +30,9 @@
|
|||||||
const openKeys = ref<string[]>([]);
|
const openKeys = ref<string[]>([]);
|
||||||
const selectedKey = ref<string[]>([]);
|
const selectedKey = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转
|
||||||
|
*/
|
||||||
const goto = (item: RouteRecordRaw) => {
|
const goto = (item: RouteRecordRaw) => {
|
||||||
// 打开外链
|
// 打开外链
|
||||||
if (regexUrl.test(item.path)) {
|
if (regexUrl.test(item.path)) {
|
||||||
@@ -48,6 +51,7 @@
|
|||||||
name: item.name,
|
name: item.name,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const findMenuOpenKeys = (target: string) => {
|
const findMenuOpenKeys = (target: string) => {
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
let isFind = false;
|
let isFind = false;
|
||||||
@@ -69,9 +73,14 @@
|
|||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听路由 设置打开的 key
|
||||||
|
*/
|
||||||
listenerRouteChange((newRoute) => {
|
listenerRouteChange((newRoute) => {
|
||||||
const { requiresAuth, activeMenu, hideInMenu } = newRoute.meta;
|
// TODO
|
||||||
if (requiresAuth && (!hideInMenu || activeMenu)) {
|
const { activeMenu, hideInMenu } = newRoute.meta;
|
||||||
|
if (!hideInMenu || activeMenu) {
|
||||||
const menuOpenKeys = findMenuOpenKeys(
|
const menuOpenKeys = findMenuOpenKeys(
|
||||||
(activeMenu || newRoute.name) as string
|
(activeMenu || newRoute.name) as string
|
||||||
);
|
);
|
||||||
@@ -84,11 +93,14 @@
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
// 展开菜单
|
||||||
const setCollapse = (val: boolean) => {
|
const setCollapse = (val: boolean) => {
|
||||||
if (appStore.device === 'desktop')
|
if (appStore.device === 'desktop')
|
||||||
appStore.updateSettings({ menuCollapse: val });
|
appStore.updateSettings({ menuCollapse: val });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 渲染菜单
|
||||||
const renderSubMenu = () => {
|
const renderSubMenu = () => {
|
||||||
function travel(_route: RouteRecordRaw[], nodes = []) {
|
function travel(_route: RouteRecordRaw[], nodes = []) {
|
||||||
if (_route) {
|
if (_route) {
|
||||||
@@ -103,7 +115,8 @@
|
|||||||
key={element?.name}
|
key={element?.name}
|
||||||
v-slots={{
|
v-slots={{
|
||||||
icon,
|
icon,
|
||||||
title: () => h(compile(t(element?.meta?.locale || ''))),
|
// 去除国际化 title: () => h(compile(t(element?.meta?.locale || ''))),
|
||||||
|
title: () => h(compile(element?.meta?.locale || '')),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{travel(element?.children)}
|
{travel(element?.children)}
|
||||||
@@ -114,7 +127,7 @@
|
|||||||
v-slots={{ icon }}
|
v-slots={{ icon }}
|
||||||
onClick={() => goto(element)}
|
onClick={() => goto(element)}
|
||||||
>
|
>
|
||||||
{t(element?.meta?.locale || '')}
|
{element?.meta?.locale || ''}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
);
|
);
|
||||||
nodes.push(node as never);
|
nodes.push(node as never);
|
||||||
@@ -158,5 +171,14 @@
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.arco-menu-icon {
|
||||||
|
margin-right: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-menu-indent-list {
|
||||||
|
width: 28px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div class="tab-bar-box">
|
<div class="tab-bar-box">
|
||||||
<div class="tab-bar-scroll">
|
<div class="tab-bar-scroll">
|
||||||
<div class="tags-wrap">
|
<div class="tags-wrap">
|
||||||
<tab-item
|
<TabItem
|
||||||
v-for="(tag, index) in tagList"
|
v-for="(tag, index) in tagList"
|
||||||
:key="tag.fullPath"
|
:key="tag.fullPath"
|
||||||
:index="index"
|
:index="index"
|
||||||
@@ -19,14 +19,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, watch, onUnmounted } from 'vue';
|
import { computed, onUnmounted, ref, watch } from 'vue';
|
||||||
import type { RouteLocationNormalized } from 'vue-router';
|
import type { RouteLocationNormalized } from 'vue-router';
|
||||||
import {
|
import { listenerRouteChange, removeRouteListener, } from '@/utils/route-listener';
|
||||||
listenerRouteChange,
|
|
||||||
removeRouteListener,
|
|
||||||
} from '@/utils/route-listener';
|
|
||||||
import { useAppStore, useTabBarStore } from '@/store';
|
import { useAppStore, useTabBarStore } from '@/store';
|
||||||
import tabItem from './tab-item.vue';
|
import TabItem from './tab-item.vue';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const tabBarStore = useTabBarStore();
|
const tabBarStore = useTabBarStore();
|
||||||
@@ -45,11 +42,19 @@
|
|||||||
affixRef.value.updatePosition();
|
affixRef.value.updatePosition();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听路由变化
|
||||||
|
*/
|
||||||
|
// TODO
|
||||||
listenerRouteChange((route: RouteLocationNormalized) => {
|
listenerRouteChange((route: RouteLocationNormalized) => {
|
||||||
|
console.log(route);
|
||||||
|
console.log(!route.meta.noAffix);
|
||||||
if (
|
if (
|
||||||
!route.meta.noAffix &&
|
!route.meta.noAffix && // todo 改一下
|
||||||
!tagList.value.some((tag) => tag.fullPath === route.fullPath)
|
!tagList.value.some((tag) => tag.fullPath === route.fullPath)
|
||||||
) {
|
) {
|
||||||
|
console.log('updateTabList', route);
|
||||||
tabBarStore.updateTabList(route);
|
tabBarStore.updateTabList(route);
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
@@ -66,7 +71,7 @@
|
|||||||
|
|
||||||
.tab-bar-box {
|
.tab-bar-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 0 0 20px;
|
padding: 0 0 0 6px;
|
||||||
background-color: var(--color-bg-2);
|
background-color: var(--color-bg-2);
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,17 @@
|
|||||||
<a-dropdown
|
<a-dropdown
|
||||||
trigger="contextMenu"
|
trigger="contextMenu"
|
||||||
:popup-max-height="false"
|
:popup-max-height="false"
|
||||||
@select="actionSelect"
|
@select="actionSelect">
|
||||||
>
|
|
||||||
<span
|
<span
|
||||||
class="arco-tag arco-tag-size-medium arco-tag-checked"
|
class="arco-tag arco-tag-size-medium arco-tag-checked"
|
||||||
:class="{ 'link-activated': itemData.fullPath === $route.fullPath }"
|
:class="{ 'link-activated': itemData.fullPath === $route.fullPath }"
|
||||||
@click="goto(itemData)"
|
@click="goto(itemData)">
|
||||||
>
|
|
||||||
<span class="tag-link">
|
<span class="tag-link">
|
||||||
{{ $t(itemData.title) }}
|
{{ itemData.title }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="arco-icon-hover arco-tag-icon-hover arco-icon-hover-size-medium arco-tag-close-btn"
|
class="arco-icon-hover arco-tag-icon-hover arco-icon-hover-size-medium arco-tag-close-btn"
|
||||||
@click.stop="tagClose(itemData, index)"
|
@click.stop="tagClose(itemData, index)">
|
||||||
>
|
|
||||||
<icon-close />
|
<icon-close />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -27,8 +24,7 @@
|
|||||||
<a-doption
|
<a-doption
|
||||||
class="sperate-line"
|
class="sperate-line"
|
||||||
:disabled="disabledCurrent"
|
:disabled="disabledCurrent"
|
||||||
:value="Eaction.current"
|
:value="Eaction.current">
|
||||||
>
|
|
||||||
<icon-close />
|
<icon-close />
|
||||||
<span>关闭当前标签页</span>
|
<span>关闭当前标签页</span>
|
||||||
</a-doption>
|
</a-doption>
|
||||||
@@ -39,8 +35,7 @@
|
|||||||
<a-doption
|
<a-doption
|
||||||
class="sperate-line"
|
class="sperate-line"
|
||||||
:disabled="disabledRight"
|
:disabled="disabledRight"
|
||||||
:value="Eaction.right"
|
:value="Eaction.right">
|
||||||
>
|
|
||||||
<icon-to-right />
|
<icon-to-right />
|
||||||
<span>关闭右侧标签页</span>
|
<span>关闭右侧标签页</span>
|
||||||
</a-doption>
|
</a-doption>
|
||||||
@@ -113,6 +108,9 @@
|
|||||||
return props.index === tagList.value.length - 1;
|
return props.index === tagList.value.length - 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭 tag
|
||||||
|
*/
|
||||||
const tagClose = (tag: TagProps, idx: number) => {
|
const tagClose = (tag: TagProps, idx: number) => {
|
||||||
tabBarStore.deleteTab(idx, tag);
|
tabBarStore.deleteTab(idx, tag);
|
||||||
if (props.itemData.fullPath === route.fullPath) {
|
if (props.itemData.fullPath === route.fullPath) {
|
||||||
@@ -124,12 +122,15 @@
|
|||||||
const findCurrentRouteIndex = () => {
|
const findCurrentRouteIndex = () => {
|
||||||
return tagList.value.findIndex((el) => el.fullPath === route.fullPath);
|
return tagList.value.findIndex((el) => el.fullPath === route.fullPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionSelect = async (value: any) => {
|
const actionSelect = async (value: any) => {
|
||||||
const { itemData, index } = props;
|
const { itemData, index } = props;
|
||||||
const copyTagList = [...tagList.value];
|
const copyTagList = [...tagList.value];
|
||||||
if (value === Eaction.current) {
|
if (value === Eaction.current) {
|
||||||
|
// 关闭当前
|
||||||
tagClose(itemData, index);
|
tagClose(itemData, index);
|
||||||
} else if (value === Eaction.left) {
|
} else if (value === Eaction.left) {
|
||||||
|
// 关闭左侧
|
||||||
const currentRouteIdx = findCurrentRouteIndex();
|
const currentRouteIdx = findCurrentRouteIndex();
|
||||||
copyTagList.splice(1, props.index - 1);
|
copyTagList.splice(1, props.index - 1);
|
||||||
|
|
||||||
@@ -138,6 +139,7 @@
|
|||||||
router.push({ name: itemData.name });
|
router.push({ name: itemData.name });
|
||||||
}
|
}
|
||||||
} else if (value === Eaction.right) {
|
} else if (value === Eaction.right) {
|
||||||
|
// 关闭右侧
|
||||||
const currentRouteIdx = findCurrentRouteIndex();
|
const currentRouteIdx = findCurrentRouteIndex();
|
||||||
copyTagList.splice(props.index + 1);
|
copyTagList.splice(props.index + 1);
|
||||||
|
|
||||||
@@ -146,14 +148,15 @@
|
|||||||
router.push({ name: itemData.name });
|
router.push({ name: itemData.name });
|
||||||
}
|
}
|
||||||
} else if (value === Eaction.others) {
|
} else if (value === Eaction.others) {
|
||||||
|
// 关闭其他
|
||||||
const filterList = tagList.value.filter((el, idx) => {
|
const filterList = tagList.value.filter((el, idx) => {
|
||||||
return idx === 0 || idx === props.index;
|
return idx === 0 || idx === props.index;
|
||||||
});
|
});
|
||||||
tabBarStore.freshTabList(filterList);
|
tabBarStore.freshTabList(filterList);
|
||||||
router.push({ name: itemData.name });
|
router.push({ name: itemData.name });
|
||||||
} else if (value === Eaction.reload) {
|
} else if (value === Eaction.reload) {
|
||||||
|
// 重新加载
|
||||||
tabBarStore.deleteCache(itemData);
|
tabBarStore.deleteCache(itemData);
|
||||||
console.log(route.fullPath);
|
|
||||||
await router.push({
|
await router.push({
|
||||||
name: REDIRECT_ROUTE_NAME,
|
name: REDIRECT_ROUTE_NAME,
|
||||||
params: {
|
params: {
|
||||||
@@ -162,6 +165,7 @@
|
|||||||
});
|
});
|
||||||
tabBarStore.addCache(itemData.name);
|
tabBarStore.addCache(itemData.name);
|
||||||
} else {
|
} else {
|
||||||
|
// 关闭全部
|
||||||
tabBarStore.resetTabList();
|
tabBarStore.resetTabList();
|
||||||
router.push({ name: DEFAULT_ROUTE_NAME });
|
router.push({ name: DEFAULT_ROUTE_NAME });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,26 +4,46 @@ import { useUserStore } from '@/store';
|
|||||||
export default function usePermission() {
|
export default function usePermission() {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
return {
|
return {
|
||||||
// TODO test
|
/**
|
||||||
|
* 是否可访问路由
|
||||||
|
*/
|
||||||
accessRouter(route: RouteLocationNormalized | RouteRecordRaw) {
|
accessRouter(route: RouteLocationNormalized | RouteRecordRaw) {
|
||||||
return (
|
// route.name
|
||||||
!route.meta?.requiresAuth ||
|
// TODO
|
||||||
!route.meta?.permission ||
|
|
||||||
userStore.permission?.includes(route.meta?.permission)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
findFirstPermissionRoute(_routers: any, permission: string) {
|
|
||||||
const cloneRouters = [..._routers];
|
/**
|
||||||
while (cloneRouters.length) {
|
* 是否有权限
|
||||||
const firstElement = cloneRouters.shift();
|
*/
|
||||||
if (firstElement?.meta?.permission === permission) {
|
hasPermission(permission: string) {
|
||||||
return { name: firstElement.name };
|
return userStore.permission?.includes('*') ||
|
||||||
}
|
userStore.permission?.includes(permission);
|
||||||
if (firstElement?.children) {
|
|
||||||
cloneRouters.push(...firstElement.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有权限
|
||||||
|
*/
|
||||||
|
hasAnyPermission(permission: string[]) {
|
||||||
|
return userStore.permission?.includes('*') ||
|
||||||
|
permission.map(s => userStore.permission?.includes(s))
|
||||||
|
.filter(Boolean).length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有角色
|
||||||
|
*/
|
||||||
|
hasRole(role: string) {
|
||||||
|
return userStore.roles?.includes('admin') ||
|
||||||
|
userStore.roles?.includes(role);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有角色
|
||||||
|
*/
|
||||||
|
hasAnyRole(role: string[]) {
|
||||||
|
return userStore.roles?.includes('*') ||
|
||||||
|
role.map(s => userStore.roles?.includes(s))
|
||||||
|
.filter(Boolean).length > 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { RouteLocationNormalized } from 'vue-router';
|
||||||
|
import { TagProps } from '@/store/modules/tab-bar/types';
|
||||||
|
|
||||||
export const REDIRECT_ROUTE_NAME = 'redirect';
|
export const REDIRECT_ROUTE_NAME = 'redirect';
|
||||||
|
|
||||||
export const LOGIN_ROUTE_NAME = 'login';
|
export const LOGIN_ROUTE_NAME = 'login';
|
||||||
@@ -10,15 +13,6 @@ export const DEFAULT_ROUTE_NAME = 'workplace';
|
|||||||
|
|
||||||
export const DEFAULT_ROUTE_FULL_PATH = '/dashboard/workplace';
|
export const DEFAULT_ROUTE_FULL_PATH = '/dashboard/workplace';
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认 tab 页面
|
|
||||||
*/
|
|
||||||
export const DEFAULT_TAB = {
|
|
||||||
title: 'menu.dashboard.workplace',
|
|
||||||
name: DEFAULT_ROUTE_NAME,
|
|
||||||
fullPath: DEFAULT_ROUTE_FULL_PATH,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 路由白名单
|
* 路由白名单
|
||||||
*/
|
*/
|
||||||
@@ -28,3 +22,29 @@ export const WHITE_ROUTER_LIST = [
|
|||||||
{ name: FORBIDDEN_ROUTER_NAME, children: [] },
|
{ name: FORBIDDEN_ROUTER_NAME, children: [] },
|
||||||
{ name: REDIRECT_ROUTE_NAME, children: [] },
|
{ name: REDIRECT_ROUTE_NAME, children: [] },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认 tab 页面
|
||||||
|
*/
|
||||||
|
export const DEFAULT_TAB = {
|
||||||
|
title: '工作台',
|
||||||
|
name: DEFAULT_ROUTE_NAME,
|
||||||
|
fullPath: DEFAULT_ROUTE_FULL_PATH,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* router 转 tag
|
||||||
|
*/
|
||||||
|
// TODO 获取后端meta
|
||||||
|
export const routerToTag = (route: RouteLocationNormalized): TagProps => {
|
||||||
|
console.log(route);
|
||||||
|
// TODO 还是得需要 name 和 meta 的映射
|
||||||
|
const { name, meta, fullPath, query } = route;
|
||||||
|
return {
|
||||||
|
title: meta.locale || 'me',
|
||||||
|
name: String(name),
|
||||||
|
fullPath,
|
||||||
|
query,
|
||||||
|
ignoreCache: meta.ignoreCache,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
import type { Router, RouteRecordNormalized } from 'vue-router';
|
import type { Router, RouteRecordNormalized } from 'vue-router';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import usePermission from '@/hooks/permission';
|
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { WHITE_ROUTER_LIST, NOT_FOUND_ROUTER_NAME, FORBIDDEN_ROUTER_NAME } from '../constants';
|
import { NOT_FOUND_ROUTER_NAME, WHITE_ROUTER_LIST } from '../constants';
|
||||||
|
|
||||||
export default function setupPermissionGuard(router: Router) {
|
export default function setupPermissionGuard(router: Router) {
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const permission = usePermission();
|
|
||||||
|
|
||||||
// 未加载菜单 并且 未从白名单中找到 to.name
|
// 未加载菜单 并且 不在白名单内 则加载菜单
|
||||||
if (
|
if (
|
||||||
!appStore.appAsyncMenus.length &&
|
!appStore.menuFetched &&
|
||||||
!WHITE_ROUTER_LIST.find((el) => el.name === to.name)
|
!WHITE_ROUTER_LIST.find((el) => el.name === to.name)
|
||||||
) {
|
) {
|
||||||
// 加载菜单
|
// 加载菜单
|
||||||
await appStore.fetchMenuConfig();
|
await appStore.fetchMenuConfig();
|
||||||
}
|
}
|
||||||
// 检查路由是否存在
|
// 检查路由是否存在于授权路由中
|
||||||
const menuConfig = [...appStore.appAsyncMenus, ...WHITE_ROUTER_LIST];
|
const menuConfig = [...appStore.appAsyncMenus, ...WHITE_ROUTER_LIST];
|
||||||
let exist = false;
|
let exist = false;
|
||||||
while (menuConfig.length && !exist) {
|
while (menuConfig.length && !exist) {
|
||||||
@@ -30,14 +28,9 @@ export default function setupPermissionGuard(router: Router) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 检查是否有权限
|
|
||||||
const permissionsAllow = permission.accessRouter(to);
|
|
||||||
if (!exist) {
|
if (!exist) {
|
||||||
// 页面不存在
|
// 页面不存在
|
||||||
next({ name: NOT_FOUND_ROUTER_NAME });
|
next({ name: NOT_FOUND_ROUTER_NAME });
|
||||||
} else if (!permissionsAllow) {
|
|
||||||
// 无权限
|
|
||||||
next({ name: FORBIDDEN_ROUTER_NAME });
|
|
||||||
} else {
|
} else {
|
||||||
// 正常跳转
|
// 正常跳转
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export const REDIRECT_ROUTER: RouteRecordRaw = {
|
|||||||
component: () => import('@/views/redirect/index.vue'),
|
component: () => import('@/views/redirect/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
|
noAffix: true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { AppRouteRecordRaw } from '../types';
|
|||||||
|
|
||||||
const DASHBOARD: AppRouteRecordRaw = {
|
const DASHBOARD: AppRouteRecordRaw = {
|
||||||
name: 'dashboard',
|
name: 'dashboard',
|
||||||
|
path: '/dashboard',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/dashboard/workplace',
|
|
||||||
name: 'workplace',
|
name: 'workplace',
|
||||||
|
path: '/dashboard/workplace',
|
||||||
component: () => import('@/views/dashboard/workplace/index.vue'),
|
component: () => import('@/views/dashboard/workplace/index.vue'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { AppRouteRecordRaw } from '../types';
|
|||||||
|
|
||||||
const USER: AppRouteRecordRaw = {
|
const USER: AppRouteRecordRaw = {
|
||||||
name: 'user',
|
name: 'user',
|
||||||
|
path: '/user',
|
||||||
component: DEFAULT_LAYOUT,
|
component: DEFAULT_LAYOUT,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -14,6 +15,9 @@ const USER: AppRouteRecordRaw = {
|
|||||||
path: '/user/userChild2',
|
path: '/user/userChild2',
|
||||||
name: 'userChild2',
|
name: 'userChild2',
|
||||||
component: () => import('@/views/user/child2/index.vue'),
|
component: () => import('@/views/user/child2/index.vue'),
|
||||||
|
meta: {
|
||||||
|
noAffix: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
19
orion-ops-ui/src/router/typings.d.ts
vendored
19
orion-ops-ui/src/router/typings.d.ts
vendored
@@ -1,22 +1,25 @@
|
|||||||
import 'vue-router';
|
import 'vue-router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前端覆盖后端
|
||||||
|
*/
|
||||||
declare module 'vue-router' {
|
declare module 'vue-router' {
|
||||||
interface RouteMeta {
|
interface RouteMeta {
|
||||||
// 后端赋值
|
// 图标
|
||||||
icon?: string;
|
icon?: string;
|
||||||
// 后端赋值
|
// 名称
|
||||||
locale?: string;
|
locale?: string;
|
||||||
// 后端赋值
|
// 排序
|
||||||
order?: number;
|
order?: number;
|
||||||
// 后端赋值 是否隐藏菜单
|
// 是否隐藏菜单
|
||||||
hideInMenu?: boolean;
|
hideInMenu?: boolean;
|
||||||
// 后端赋值 是否隐藏子菜单
|
// 是否隐藏子菜单
|
||||||
hideChildrenInMenu?: boolean;
|
hideChildrenInMenu?: boolean;
|
||||||
// 后端赋值 是否添加到 tab
|
// 是否添加到 tab
|
||||||
noAffix?: boolean;
|
noAffix?: boolean;
|
||||||
// 前端赋值 是否忽略缓存
|
// 是否忽略缓存
|
||||||
ignoreCache?: boolean;
|
ignoreCache?: boolean;
|
||||||
// 不赋值
|
// 是否活跃
|
||||||
activeMenu?: string;
|
activeMenu?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import { defineStore } from 'pinia';
|
|||||||
import { Notification } from '@arco-design/web-vue';
|
import { Notification } from '@arco-design/web-vue';
|
||||||
import type { RouteRecordNormalized } from 'vue-router';
|
import type { RouteRecordNormalized } from 'vue-router';
|
||||||
import defaultSettings from '@/config/settings.json';
|
import defaultSettings from '@/config/settings.json';
|
||||||
import { getMenuList } from '@/api/user';
|
import { getMenuList } from '@/api/user/auth';
|
||||||
import { AppState } from './types';
|
import { AppState } from './types';
|
||||||
|
import router from '@/router';
|
||||||
|
|
||||||
const useAppStore = defineStore('app', {
|
const useAppStore = defineStore('app', {
|
||||||
state: (): AppState => ({ ...defaultSettings }),
|
state: (): AppState => ({
|
||||||
|
...defaultSettings,
|
||||||
|
menuFetched: false,
|
||||||
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
appCurrentSetting(state: AppState): AppState {
|
appCurrentSetting(state: AppState): AppState {
|
||||||
@@ -62,7 +66,54 @@ const useAppStore = defineStore('app', {
|
|||||||
async fetchMenuConfig() {
|
async fetchMenuConfig() {
|
||||||
try {
|
try {
|
||||||
const { data } = await getMenuList();
|
const { data } = await getMenuList();
|
||||||
this.serverMenu = data;
|
// @ts-ignore
|
||||||
|
this.serverMenu = (data as Array<any>).map(s => {
|
||||||
|
// 转换
|
||||||
|
const convert = (item: any) => {
|
||||||
|
// 设置路由属性
|
||||||
|
const meta = {
|
||||||
|
locale: item.name,
|
||||||
|
icon: item.icon,
|
||||||
|
order: item.sort,
|
||||||
|
hideInMenu: item.visible === 0,
|
||||||
|
hideChildrenInMenu: item.visible === 0,
|
||||||
|
noAffix: item.visible === 0,
|
||||||
|
ignoreCache: item.cache === 0,
|
||||||
|
};
|
||||||
|
// 获取 router
|
||||||
|
const route = router.getRoutes().find(r => {
|
||||||
|
return r.name === item.component;
|
||||||
|
});
|
||||||
|
// 设置 router meta
|
||||||
|
if (route) {
|
||||||
|
// 路由配置覆盖菜单配置
|
||||||
|
route.meta = { ...meta, ...route.meta };
|
||||||
|
}
|
||||||
|
// 返回
|
||||||
|
return {
|
||||||
|
name: item.component,
|
||||||
|
path: item.path,
|
||||||
|
meta: meta,
|
||||||
|
children: undefined as unknown
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// 构建父目录
|
||||||
|
const menu = convert(s);
|
||||||
|
// 构建子目录
|
||||||
|
if (s.children) {
|
||||||
|
menu.children = (s.children as Array<any>).map(convert);
|
||||||
|
}
|
||||||
|
return menu;
|
||||||
|
});
|
||||||
|
// 是否已加载过
|
||||||
|
this.menuFetched = true;
|
||||||
|
// 未配置菜单
|
||||||
|
if (this.serverMenu.length === 0) {
|
||||||
|
Notification.error({
|
||||||
|
content: '该用户未配置菜单, 请先配置',
|
||||||
|
closable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Notification.error({
|
Notification.error({
|
||||||
content: '加载菜单失败',
|
content: '加载菜单失败',
|
||||||
@@ -76,6 +127,7 @@ const useAppStore = defineStore('app', {
|
|||||||
*/
|
*/
|
||||||
clearMenu() {
|
clearMenu() {
|
||||||
this.serverMenu = [];
|
this.serverMenu = [];
|
||||||
|
this.menuFetched = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { RouteLocationNormalized } from 'vue-router';
|
import type { RouteLocationNormalized } from 'vue-router';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { DEFAULT_TAB, DEFAULT_ROUTE_NAME, BAN_TAB_LIST, routerToTag } from '@/router/constants';
|
import { DEFAULT_TAB, DEFAULT_ROUTE_NAME, routerToTag } from '@/router/constants';
|
||||||
import { isString } from '@/utils/is';
|
import { isString } from '@/utils/is';
|
||||||
import { TabBarState, TagProps } from './types';
|
import { TabBarState, TagProps } from './types';
|
||||||
|
|
||||||
@@ -24,7 +24,6 @@ const useTabBarStore = defineStore('tabBar', {
|
|||||||
* 添加 tab
|
* 添加 tab
|
||||||
*/
|
*/
|
||||||
updateTabList(route: RouteLocationNormalized) {
|
updateTabList(route: RouteLocationNormalized) {
|
||||||
if (BAN_TAB_LIST.includes(route.name as string)) return;
|
|
||||||
this.tagList.push(routerToTag(route));
|
this.tagList.push(routerToTag(route));
|
||||||
if (!route.meta.ignoreCache) {
|
if (!route.meta.ignoreCache) {
|
||||||
this.cacheTabList.add(route.name as string);
|
this.cacheTabList.add(route.name as string);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { getUserInfo } from '@/api/user';
|
import { getUserPermission, login as userLogin, LoginRequest, logout as userLogout } from '@/api/user/auth';
|
||||||
import { login as userLogin, LoginRequest, logout as userLogout, } from '@/api/user/auth';
|
|
||||||
import { clearToken, setToken } from '@/utils/auth';
|
import { clearToken, setToken } from '@/utils/auth';
|
||||||
import { md5 } from '@/utils';
|
import { md5 } from '@/utils';
|
||||||
import { removeRouteListener } from '@/utils/route-listener';
|
import { removeRouteListener } from '@/utils/route-listener';
|
||||||
@@ -13,8 +12,8 @@ const useUserStore = defineStore('user', {
|
|||||||
username: undefined,
|
username: undefined,
|
||||||
nickname: undefined,
|
nickname: undefined,
|
||||||
avatar: undefined,
|
avatar: undefined,
|
||||||
permission: undefined,
|
|
||||||
roles: undefined,
|
roles: undefined,
|
||||||
|
permission: undefined,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
@@ -35,13 +34,14 @@ const useUserStore = defineStore('user', {
|
|||||||
* 获取用户信息
|
* 获取用户信息
|
||||||
*/
|
*/
|
||||||
async info() {
|
async info() {
|
||||||
const res = await getUserInfo();
|
const { data } = await getUserPermission();
|
||||||
this.setInfo({
|
this.setInfo({
|
||||||
id: 1,
|
id: data.user.id,
|
||||||
username: 'admin',
|
username: data.user.username,
|
||||||
nickname: '管理员',
|
nickname: data.user.nickname,
|
||||||
permission: ['*'],
|
avatar: data.user.avatar,
|
||||||
roles: ['admin'],
|
roles: data.roles,
|
||||||
|
permission: data.permissions,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -54,7 +54,9 @@ const useUserStore = defineStore('user', {
|
|||||||
username: loginForm.username,
|
username: loginForm.username,
|
||||||
password: md5(loginForm.password),
|
password: md5(loginForm.password),
|
||||||
};
|
};
|
||||||
|
// 执行登陆
|
||||||
const res = await userLogin(loginRequest);
|
const res = await userLogin(loginRequest);
|
||||||
|
// 设置登陆 token
|
||||||
setToken(res.data.token);
|
setToken(res.data.token);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
clearToken();
|
clearToken();
|
||||||
@@ -69,6 +71,7 @@ const useUserStore = defineStore('user', {
|
|||||||
try {
|
try {
|
||||||
await userLogout();
|
await userLogout();
|
||||||
} finally {
|
} finally {
|
||||||
|
// 登出回调
|
||||||
this.logoutCallBack();
|
this.logoutCallBack();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
/**
|
|
||||||
* Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management
|
|
||||||
* 单独监听路由会浪费渲染性能。使用发布订阅模式去进行分发管理。
|
|
||||||
*/
|
|
||||||
import mitt, { Handler } from 'mitt';
|
import mitt, { Handler } from 'mitt';
|
||||||
import type { RouteLocationNormalized } from 'vue-router';
|
import type { RouteLocationNormalized } from 'vue-router';
|
||||||
|
|
||||||
@@ -13,9 +9,14 @@ let latestRoute: RouteLocationNormalized;
|
|||||||
|
|
||||||
export function setRouteEmitter(to: RouteLocationNormalized) {
|
export function setRouteEmitter(to: RouteLocationNormalized) {
|
||||||
emitter.emit(key, to);
|
emitter.emit(key, to);
|
||||||
|
// TODO 这里寻找
|
||||||
latestRoute = to;
|
latestRoute = to;
|
||||||
|
console.log('change', to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加路由跳转监听器
|
||||||
|
*/
|
||||||
export function listenerRouteChange(
|
export function listenerRouteChange(
|
||||||
handler: (route: RouteLocationNormalized) => void,
|
handler: (route: RouteLocationNormalized) => void,
|
||||||
immediate = true
|
immediate = true
|
||||||
@@ -26,6 +27,9 @@ export function listenerRouteChange(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除路由跳转监听器
|
||||||
|
*/
|
||||||
export function removeRouteListener() {
|
export function removeRouteListener() {
|
||||||
emitter.off(key);
|
emitter.off(key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,9 @@
|
|||||||
if (!errors) {
|
if (!errors) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// 执行登陆
|
||||||
await userStore.login(values as LoginData);
|
await userStore.login(values as LoginData);
|
||||||
|
// 跳转路由
|
||||||
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
||||||
router.push({
|
router.push({
|
||||||
name: (redirect as string) || 'workplace',
|
name: (redirect as string) || 'workplace',
|
||||||
|
|||||||
@@ -2,13 +2,16 @@
|
|||||||
<div>
|
<div>
|
||||||
<p>UserChild 2</p>
|
<p>UserChild 2</p>
|
||||||
<h1 v-permission="['admin']">123</h1>
|
<h1 v-permission="['admin']">123</h1>
|
||||||
|
<button @click="red">red</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
export default {
|
import router from '@/router';
|
||||||
name: 'UserChild2',
|
|
||||||
};
|
function red() {
|
||||||
|
router.push({ name: 'workplace' });
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user