修改菜单逻辑.

This commit is contained in:
lijiahang
2023-08-02 17:08:40 +08:00
parent 4322f01354
commit 7915198be4
16 changed files with 230 additions and 94 deletions

View File

@@ -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>

View File

@@ -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);

View File

@@ -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 });
} }

View File

@@ -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;
}
}; };
} }

View File

@@ -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,
};
};

View File

@@ -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();

View File

@@ -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
}, },
}, },
], ],

View File

@@ -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'),
}, },
], ],

View File

@@ -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
}
}, },
], ],
}; };

View File

@@ -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;
} }
} }

View File

@@ -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;
}, },
}, },
}); });

View File

@@ -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);

View File

@@ -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();
} }
}, },

View File

@@ -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);
} }

View File

@@ -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',

View File

@@ -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>