feat: 面包屑组件 & 顶部菜单调整

This commit is contained in:
baiqi 2023-06-27 15:20:51 +08:00 committed by 刘瑞斌
parent b73f2b3270
commit 1124cca16d
7 changed files with 116 additions and 10 deletions

View File

@ -0,0 +1,59 @@
<template>
<a-breadcrumb v-if="showBreadcrumb" class="z-10 mb-[-8px] mt-[8px]">
<a-breadcrumb-item v-for="crumb of appStore.breadcrumbList" :key="crumb.name" @click="jumpTo(crumb.name)">
{{ t(crumb.locale) }}
</a-breadcrumb-item>
</a-breadcrumb>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useRouter, RouteRecordName } from 'vue-router';
import { useAppStore } from '@/store';
import { listenerRouteChange } from '@/utils/route-listener';
import { useI18n } from '@/hooks/useI18n';
const appStore = useAppStore();
const { t } = useI18n();
const router = useRouter();
const showBreadcrumb = computed(() => {
const b = appStore.getCurrentTopMenu.meta?.breadcrumbs;
return b && b.length > 0;
});
/**
* 监听路由变化存储打开及选中的菜单
*/
listenerRouteChange((newRoute) => {
const { name } = newRoute;
if (name === appStore.currentTopMenu.name) {
appStore.setBreadcrumbList(appStore.currentTopMenu?.meta?.breadcrumbs);
} else {
appStore.setBreadcrumbList([]);
}
}, true);
function jumpTo(name: RouteRecordName) {
router.push({ name });
}
</script>
<style lang="less">
/** 面包屑 **/
.arco-breadcrumb-item {
@apply cursor-pointer;
color: var(--color-text-4);
&:hover {
color: rgb(var(--primary-5));
}
&:disabled {
color: var(--color-text-brand);
}
&:last-child {
@apply cursor-auto;
color: var(--color-text-2);
}
}
</style>

View File

@ -1,9 +1,10 @@
<template> <template>
<a-menu <a-menu
v-if="appStore.topMenus.length > 0" v-show="appStore.topMenus.length > 0"
v-model:selected-keys="activeMenus"
class="bg-transparent" class="bg-transparent"
mode="horizontal" mode="horizontal"
:default-selected-keys="[defaultActiveMenu]" @menu-item-click="setCurrentTopMenu"
> >
<a-menu-item v-for="menu of appStore.topMenus" :key="(menu.name as string)" @click="jumpPath(menu.name)"> <a-menu-item v-for="menu of appStore.topMenus" :key="(menu.name as string)" @click="jumpPath(menu.name)">
{{ t(menu.meta?.locale || '') }} {{ t(menu.meta?.locale || '') }}
@ -12,7 +13,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { Ref, ref, watch } from 'vue';
import { useRouter, RouteRecordRaw, RouteRecordNormalized, RouteRecordName } from 'vue-router'; import { useRouter, RouteRecordRaw, RouteRecordNormalized, RouteRecordName } from 'vue-router';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
@ -26,11 +27,24 @@
const appStore = useAppStore(); const appStore = useAppStore();
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const activeMenus: Ref<RouteRecordName[]> = ref([]);
const defaultActiveMenu = computed(() => { watch(
const { name } = router.currentRoute.value; () => appStore.getCurrentTopMenu?.name,
return name; (val) => {
}); activeMenus.value = [val || ''];
}
);
function setCurrentTopMenu(key: string) {
const secParent = appStore.topMenus.find((el: RouteRecordRaw) => {
return (el?.name as string).includes(key);
});
if (secParent) {
appStore.setCurrentTopMenu(secParent);
}
}
/** /**
* 监听路由变化存储打开的三级子路由 * 监听路由变化存储打开的三级子路由
@ -45,6 +59,7 @@
(item) => name && (name as string).includes((item?.name as string) || '') (item) => name && (name as string).includes((item?.name as string) || '')
); );
appStore.setTopMenus(currentParent?.children?.filter((item) => item.meta?.isTopMenu)); appStore.setTopMenus(currentParent?.children?.filter((item) => item.meta?.isTopMenu));
setCurrentTopMenu(name as string);
} }
} }
}); });

View File

@ -182,7 +182,7 @@
import { LOCALE_OPTIONS } from '@/locale'; import { LOCALE_OPTIONS } from '@/locale';
import useLocale from '@/locale/useLocale'; import useLocale from '@/locale/useLocale';
// import useUser from '@/hooks/useUser'; // import useUser from '@/hooks/useUser';
import TopMenu from '@/components/pure/ms-top-menu/index.vue'; import TopMenu from '@/components/bussiness/ms-top-menu/index.vue';
import MessageBox from '../message-box/index.vue'; import MessageBox from '../message-box/index.vue';
import { NOT_SHOW_PROJECT_SELECT_MODULE } from '@/router/constants'; import { NOT_SHOW_PROJECT_SELECT_MODULE } from '@/router/constants';
import { getProjectList } from '@/api/modules/system/project'; import { getProjectList } from '@/api/modules/system/project';

View File

@ -34,6 +34,7 @@
<Menu /> <Menu />
</a-drawer> </a-drawer>
<a-layout class="layout-content" :style="paddingStyle"> <a-layout class="layout-content" :style="paddingStyle">
<MsBreadCrumb />
<a-spin :loading="appStore.loading" :tip="appStore.loadingTip"> <a-spin :loading="appStore.loading" :tip="appStore.loadingTip">
<a-scrollbar <a-scrollbar
:style="{ :style="{
@ -64,6 +65,7 @@
import TabBar from '@/components/pure/tab-bar/index.vue'; import TabBar from '@/components/pure/tab-bar/index.vue';
import usePermission from '@/hooks/usePermission'; import usePermission from '@/hooks/usePermission';
import PageLayout from './page-layout.vue'; import PageLayout from './page-layout.vue';
import MsBreadCrumb from '@/components/bussiness/ms-breadcrumb/index.vue';
const isInit = ref(false); const isInit = ref(false);
const appStore = useAppStore(); const appStore = useAppStore();

View File

@ -12,5 +12,7 @@ declare module 'vue-router' {
order?: number; // 排序权重 order?: number; // 排序权重
noAffix?: boolean; // tab展示设置设置为true则不在tab列表展示激活页面的tab noAffix?: boolean; // tab展示设置设置为true则不在tab列表展示激活页面的tab
ignoreCache?: boolean; // 缓存设置true则不缓存 ignoreCache?: boolean; // 缓存设置true则不缓存
isTopMenu?: boolean; // 是否为顶部菜单
breadcrumbs?: BreadcrumbItem[]; // 面包屑
} }
} }

View File

@ -3,8 +3,9 @@ import { Notification } from '@arco-design/web-vue';
import defaultSettings from '@/config/settings.json'; import defaultSettings from '@/config/settings.json';
import { getMenuList } from '@/api/modules/user'; import { getMenuList } from '@/api/modules/user';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { cloneDeep } from 'lodash-es';
import type { AppState } from './types'; import type { AppState, BreadcrumbItem } from './types';
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface'; import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router'; import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
@ -14,6 +15,8 @@ const useAppStore = defineStore('app', {
loading: false, loading: false,
loadingTip: '', loadingTip: '',
topMenus: [] as RouteRecordRaw[], topMenus: [] as RouteRecordRaw[],
currentTopMenu: {} as RouteRecordRaw,
breadcrumbList: [] as BreadcrumbItem[],
currentOrgId: '', currentOrgId: '',
currentProjectId: '', currentProjectId: '',
}), }),
@ -37,6 +40,12 @@ const useAppStore = defineStore('app', {
getTopMenus(state: AppState): RouteRecordRaw[] { getTopMenus(state: AppState): RouteRecordRaw[] {
return state.topMenus; return state.topMenus;
}, },
getCurrentTopMenu(state: AppState): RouteRecordRaw {
return state.currentTopMenu;
},
getBreadcrumbList(state: AppState): BreadcrumbItem[] {
return cloneDeep(state.breadcrumbList);
},
getCurrentOrgId(state: AppState): string { getCurrentOrgId(state: AppState): string {
return state.currentOrgId; return state.currentOrgId;
}, },
@ -124,6 +133,18 @@ const useAppStore = defineStore('app', {
setTopMenus(menus: RouteRecordRaw[] | undefined) { setTopMenus(menus: RouteRecordRaw[] | undefined) {
this.topMenus = menus ? [...menus] : []; this.topMenus = menus ? [...menus] : [];
}, },
/**
*
*/
setCurrentTopMenu(menu: RouteRecordRaw) {
this.currentTopMenu = cloneDeep(menu);
},
/**
*
*/
setBreadcrumbList(breadcrumbs: BreadcrumbItem[] | undefined) {
this.breadcrumbList = breadcrumbs ? cloneDeep(breadcrumbs) : [];
},
/** /**
* ID * ID
*/ */

View File

@ -1,4 +1,9 @@
import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router'; import type { RouteRecordNormalized, RouteRecordRaw, RouteRecordName } from 'vue-router';
export interface BreadcrumbItem {
name: RouteRecordName;
locale: string;
}
export interface AppState { export interface AppState {
theme: string; theme: string;
@ -17,6 +22,8 @@ export interface AppState {
loading: boolean; loading: boolean;
loadingTip: string; loadingTip: string;
topMenus: RouteRecordRaw[]; topMenus: RouteRecordRaw[];
currentTopMenu: RouteRecordRaw;
breadcrumbList: BreadcrumbItem[];
currentOrgId: string; currentOrgId: string;
currentProjectId: string; currentProjectId: string;
[key: string]: unknown; [key: string]: unknown;