mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-24 15:26:15 +08:00
feat(other): The menu supports jumping to external links and fixing some known problems
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { VbenAdminLayout } from '@vben-core/layout-ui';
|
||||
import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui';
|
||||
import { mapTree } from '@vben-core/toolkit';
|
||||
import { MenuRecordRaw } from '@vben-core/typings';
|
||||
|
||||
import { PreferenceWidget } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
import { preference, updatePreference, usePreference } from '@vben/preference';
|
||||
import { computed } from 'vue';
|
||||
|
||||
@@ -80,6 +83,15 @@ const {
|
||||
sideMenus,
|
||||
sideVisible,
|
||||
} = useMixedMenu();
|
||||
|
||||
function wrapperMenus(menus: MenuRecordRaw[]) {
|
||||
return mapTree(menus, (item) => {
|
||||
return {
|
||||
...item,
|
||||
name: $t(item.name),
|
||||
};
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -154,7 +166,7 @@ const {
|
||||
:rounded="isMenuRounded"
|
||||
mode="horizontal"
|
||||
:theme="headerMenuTheme"
|
||||
:menus="headerMenus"
|
||||
:menus="wrapperMenus(headerMenus)"
|
||||
:default-active="headerActive"
|
||||
@select="handleMenuSelect"
|
||||
/>
|
||||
@@ -175,7 +187,7 @@ const {
|
||||
:collapse-show-title="preference.sideCollapseShowTitle"
|
||||
:collapse="preference.sideCollapse"
|
||||
:theme="theme"
|
||||
:menus="sideMenus"
|
||||
:menus="wrapperMenus(sideMenus)"
|
||||
:default-active="sideActive"
|
||||
@select="handleMenuSelect"
|
||||
/>
|
||||
@@ -195,7 +207,7 @@ const {
|
||||
<template #side-extra>
|
||||
<LayoutExtraMenu
|
||||
:rounded="isMenuRounded"
|
||||
:menus="extraMenus"
|
||||
:menus="wrapperMenus(extraMenus)"
|
||||
:collapse="preference.sideExtraCollapse"
|
||||
:theme="theme"
|
||||
/>
|
||||
|
@@ -3,7 +3,9 @@ import type { MenuRecordRaw } from '@vben-core/typings';
|
||||
|
||||
import { Menu, MenuProps } from '@vben-core/menu-ui';
|
||||
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { useNavigation } from './use-navigation';
|
||||
|
||||
interface Props extends MenuProps {
|
||||
collspae?: boolean;
|
||||
@@ -13,10 +15,10 @@ interface Props extends MenuProps {
|
||||
defineProps<Props>();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { navigation } = useNavigation();
|
||||
|
||||
function handleSelect(key: string) {
|
||||
router.push(key);
|
||||
async function handleSelect(key: string) {
|
||||
await navigation(key);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -3,17 +3,18 @@ import type { MenuRecordRaw } from '@vben-core/typings';
|
||||
import { preference } from '@vben/preference';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { findRootMenuByPath } from './helper';
|
||||
import { useNavigation } from './use-navigation';
|
||||
|
||||
function useExtraMenu() {
|
||||
const accessStore = useAccessStore();
|
||||
const { navigation } = useNavigation();
|
||||
|
||||
const menus = computed(() => accessStore.getAccessMenus);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const extraMenus = ref<MenuRecordRaw[]>([]);
|
||||
const extraVisible = ref<boolean>(false);
|
||||
const extraActiveMenu = ref('');
|
||||
@@ -22,14 +23,14 @@ function useExtraMenu() {
|
||||
* 选择混合菜单事件
|
||||
* @param menu
|
||||
*/
|
||||
const handleMixedMenuSelect = (menu: MenuRecordRaw) => {
|
||||
const handleMixedMenuSelect = async (menu: MenuRecordRaw) => {
|
||||
extraMenus.value = menu?.children ?? [];
|
||||
extraActiveMenu.value = menu.parents?.[0] ?? menu.path;
|
||||
const hasChildren = extraMenus.value.length > 0;
|
||||
|
||||
extraVisible.value = hasChildren;
|
||||
if (!hasChildren) {
|
||||
router.push(menu.path);
|
||||
await navigation(menu.path);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -3,15 +3,15 @@ import type { MenuRecordRaw } from '@vben-core/typings';
|
||||
import { preference, usePreference } from '@vben/preference';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { computed, onBeforeMount, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { findRootMenuByPath } from './helper';
|
||||
import { useNavigation } from './use-navigation';
|
||||
|
||||
function useMixedMenu() {
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const { navigation } = useNavigation();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const splitSideMenus = ref<MenuRecordRaw[]>([]);
|
||||
const rootMenuPath = ref<string>('');
|
||||
|
||||
@@ -75,7 +75,7 @@ function useMixedMenu() {
|
||||
*/
|
||||
const handleMenuSelect = (key: string, mode?: string) => {
|
||||
if (!isMixedNav.value || mode === 'vertical') {
|
||||
router.push(key);
|
||||
navigation(key);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ function useMixedMenu() {
|
||||
rootMenuPath.value = rootMenu?.path ?? '';
|
||||
splitSideMenus.value = rootMenu?.children ?? [];
|
||||
if (splitSideMenus.value.length === 0) {
|
||||
router.push(key);
|
||||
navigation(key);
|
||||
}
|
||||
};
|
||||
|
||||
|
19
packages/business/layouts/src/basic/menu/use-navigation.ts
Normal file
19
packages/business/layouts/src/basic/menu/use-navigation.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { isHttpUrl, openWindow } from '@vben-core/toolkit';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
function useNavigation() {
|
||||
const router = useRouter();
|
||||
|
||||
const navigation = async (path: string) => {
|
||||
if (isHttpUrl(path)) {
|
||||
openWindow(path, { target: '_blank' });
|
||||
} else {
|
||||
await router.push(path);
|
||||
}
|
||||
};
|
||||
|
||||
return { navigation };
|
||||
}
|
||||
|
||||
export { useNavigation };
|
@@ -12,7 +12,12 @@ import {
|
||||
MdiPinOff,
|
||||
} from '@vben-core/iconify';
|
||||
import { filterTree } from '@vben-core/toolkit';
|
||||
import type {
|
||||
RouteLocationNormalized,
|
||||
RouteRecordNormalized,
|
||||
} from 'vue-router';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
import { storeToRefs, useAccessStore, useTabsStore } from '@vben/stores';
|
||||
import { computed, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
@@ -39,6 +44,9 @@ function useTabs() {
|
||||
const affixTabs = filterTree(router.getRoutes(), (route) => {
|
||||
return !!route.meta?.affixTab;
|
||||
});
|
||||
affixTabs.forEach((tab) => {
|
||||
Object.assign(tab, wrapperTabLocale(tab));
|
||||
});
|
||||
tabsStore.setAffixTabs(affixTabs);
|
||||
};
|
||||
|
||||
@@ -52,6 +60,18 @@ function useTabs() {
|
||||
await tabsStore.closeTabByKey(key, router);
|
||||
};
|
||||
|
||||
function wrapperTabLocale(
|
||||
tab: RouteLocationNormalized | RouteRecordNormalized,
|
||||
) {
|
||||
return {
|
||||
...tab,
|
||||
meta: {
|
||||
...tab.meta,
|
||||
title: $t(tab.meta.title as string),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
watch(
|
||||
() => accessMenus.value,
|
||||
() => {
|
||||
@@ -63,7 +83,7 @@ function useTabs() {
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
tabsStore.addTab(route);
|
||||
tabsStore.addTab(wrapperTabLocale(route) as RouteLocationNormalized);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
@@ -4,6 +4,7 @@ import type { IBreadcrumb } from '@vben-core/shadcn-ui';
|
||||
import { VbenBackgroundBreadcrumb, VbenBreadcrumb } from '@vben-core/shadcn-ui';
|
||||
import { BreadcrumbStyle } from '@vben-core/typings';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
import { computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
@@ -43,7 +44,7 @@ const breadcrumbs = computed((): IBreadcrumb[] => {
|
||||
resultBreadcrumb.push({
|
||||
icon: icon as string,
|
||||
path: path || route.path,
|
||||
title: (title || name) as string,
|
||||
title: $t((title || name) as string),
|
||||
// items: children.map((child) => {
|
||||
// return {
|
||||
// icon: child?.meta?.icon as string,
|
||||
|
Reference in New Issue
Block a user