feat: 面包屑组件 & 顶部菜单调整
This commit is contained in:
parent
b73f2b3270
commit
1124cca16d
|
@ -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>
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<a-menu
|
||||
v-if="appStore.topMenus.length > 0"
|
||||
v-show="appStore.topMenus.length > 0"
|
||||
v-model:selected-keys="activeMenus"
|
||||
class="bg-transparent"
|
||||
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)">
|
||||
{{ t(menu.meta?.locale || '') }}
|
||||
|
@ -12,7 +13,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { Ref, ref, watch } from 'vue';
|
||||
import { useRouter, RouteRecordRaw, RouteRecordNormalized, RouteRecordName } from 'vue-router';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useAppStore } from '@/store';
|
||||
|
@ -26,11 +27,24 @@
|
|||
const appStore = useAppStore();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const activeMenus: Ref<RouteRecordName[]> = ref([]);
|
||||
|
||||
const defaultActiveMenu = computed(() => {
|
||||
const { name } = router.currentRoute.value;
|
||||
return name;
|
||||
});
|
||||
watch(
|
||||
() => appStore.getCurrentTopMenu?.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) || '')
|
||||
);
|
||||
appStore.setTopMenus(currentParent?.children?.filter((item) => item.meta?.isTopMenu));
|
||||
setCurrentTopMenu(name as string);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -182,7 +182,7 @@
|
|||
import { LOCALE_OPTIONS } from '@/locale';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
// 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 { NOT_SHOW_PROJECT_SELECT_MODULE } from '@/router/constants';
|
||||
import { getProjectList } from '@/api/modules/system/project';
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
<Menu />
|
||||
</a-drawer>
|
||||
<a-layout class="layout-content" :style="paddingStyle">
|
||||
<MsBreadCrumb />
|
||||
<a-spin :loading="appStore.loading" :tip="appStore.loadingTip">
|
||||
<a-scrollbar
|
||||
:style="{
|
||||
|
@ -64,6 +65,7 @@
|
|||
import TabBar from '@/components/pure/tab-bar/index.vue';
|
||||
import usePermission from '@/hooks/usePermission';
|
||||
import PageLayout from './page-layout.vue';
|
||||
import MsBreadCrumb from '@/components/bussiness/ms-breadcrumb/index.vue';
|
||||
|
||||
const isInit = ref(false);
|
||||
const appStore = useAppStore();
|
||||
|
|
|
@ -12,5 +12,7 @@ declare module 'vue-router' {
|
|||
order?: number; // 排序权重
|
||||
noAffix?: boolean; // tab展示设置,设置为true则不在tab列表展示激活页面的tab
|
||||
ignoreCache?: boolean; // 缓存设置,true则不缓存
|
||||
isTopMenu?: boolean; // 是否为顶部菜单
|
||||
breadcrumbs?: BreadcrumbItem[]; // 面包屑
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@ import { Notification } from '@arco-design/web-vue';
|
|||
import defaultSettings from '@/config/settings.json';
|
||||
import { getMenuList } from '@/api/modules/user';
|
||||
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 { RouteRecordNormalized, RouteRecordRaw } from 'vue-router';
|
||||
|
||||
|
@ -14,6 +15,8 @@ const useAppStore = defineStore('app', {
|
|||
loading: false,
|
||||
loadingTip: '',
|
||||
topMenus: [] as RouteRecordRaw[],
|
||||
currentTopMenu: {} as RouteRecordRaw,
|
||||
breadcrumbList: [] as BreadcrumbItem[],
|
||||
currentOrgId: '',
|
||||
currentProjectId: '',
|
||||
}),
|
||||
|
@ -37,6 +40,12 @@ const useAppStore = defineStore('app', {
|
|||
getTopMenus(state: AppState): RouteRecordRaw[] {
|
||||
return state.topMenus;
|
||||
},
|
||||
getCurrentTopMenu(state: AppState): RouteRecordRaw {
|
||||
return state.currentTopMenu;
|
||||
},
|
||||
getBreadcrumbList(state: AppState): BreadcrumbItem[] {
|
||||
return cloneDeep(state.breadcrumbList);
|
||||
},
|
||||
getCurrentOrgId(state: AppState): string {
|
||||
return state.currentOrgId;
|
||||
},
|
||||
|
@ -124,6 +133,18 @@ const useAppStore = defineStore('app', {
|
|||
setTopMenus(menus: RouteRecordRaw[] | undefined) {
|
||||
this.topMenus = menus ? [...menus] : [];
|
||||
},
|
||||
/**
|
||||
* 设置顶部菜单组
|
||||
*/
|
||||
setCurrentTopMenu(menu: RouteRecordRaw) {
|
||||
this.currentTopMenu = cloneDeep(menu);
|
||||
},
|
||||
/**
|
||||
* 设置面包屑
|
||||
*/
|
||||
setBreadcrumbList(breadcrumbs: BreadcrumbItem[] | undefined) {
|
||||
this.breadcrumbList = breadcrumbs ? cloneDeep(breadcrumbs) : [];
|
||||
},
|
||||
/**
|
||||
* 设置当前组织 ID
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
theme: string;
|
||||
|
@ -17,6 +22,8 @@ export interface AppState {
|
|||
loading: boolean;
|
||||
loadingTip: string;
|
||||
topMenus: RouteRecordRaw[];
|
||||
currentTopMenu: RouteRecordRaw;
|
||||
breadcrumbList: BreadcrumbItem[];
|
||||
currentOrgId: string;
|
||||
currentProjectId: string;
|
||||
[key: string]: unknown;
|
||||
|
|
Loading…
Reference in New Issue