一、頁(yè)面布局
1.1 App.vue
-
頁(yè)面布局的區(qū)分:登錄頁(yè)使用全屏顯示,其他頁(yè)面有側(cè)邊欄
// App.vue <script setup lang="ts"> import { RouterView } from "vue-router"; import layout from "@/layouts/index.vue"; import { useRoute } from "vue-router"; import zhCN from "ant-design-vue/es/locale/zh_CN"; const route = useRoute(); </script> <template> <template v-if="route.name === 'Login'"> <RouterView /> </template> <template v-else> <layout /> </template> <div id="message_modal"></div> </template>
-
增加國(guó)際化的配置
// App.vue <script setup lang="ts"> // 全局配置:獲取對(duì)應(yīng)的父級(jí)節(jié)點(diǎn):處理下拉框跟隨頁(yè)面滾動(dòng)的問(wèn)題 const getPopupContainer = (el: any, dialogContext: any) => { if (dialogContext) { return dialogContext.getDialogWrap(); } else if (el) { return el?.parentNode; } else { return document.body; } }; </script> <template> <a-config-provider :locale="zhCN" :getPopupContainer="getPopupContainer"> <!-- 主要的頁(yè)面 --> </a-config-provider> </template>
二、側(cè)邊欄以及路由的設(shè)置
2.1 路由的基本使用
-
存儲(chǔ)一份數(shù)據(jù),可以同步更新路由和側(cè)邊欄
// constants/router.ts // 路由配置;以及側(cè)邊欄的顯示 /** * 除了meta的值,其他的配置項(xiàng)目參考vue-router的文檔:<RouteRecordRaw> * meta的說(shuō)明: * - breadcrumbName:側(cè)邊欄的名稱;面包屑的名稱 * - unShowSiderBar:是否顯示側(cè)邊欄,默認(rèn)為false * - unDirectParent: 當(dāng)前路由放的位置是否不是直接的父節(jié)點(diǎn)(children可以一致嵌套,但是有children又有component會(huì)導(dǎo)致無(wú)法正確識(shí)別路由。故為了確定嵌套關(guān)系和路由的頁(yè)面正常顯示,增加該字段) */ import type { RouteRecordRaw } from "vue-router"; export const routesConfig: Array<RouteRecordRaw> = [ { path: "/login", name: "Login", component: () => import("views/Login.vue"), meta: { breadcrumbName: "登錄", // 不在側(cè)邊欄中顯示該項(xiàng)目,并且不顯示面包屑 showBreadcrumbName: false, unShowSiderBar: true, }, }, ]; -
在 router 中使用
import { createRouter, createWebHashHistory } from "vue-router"; import { routesConfig } from "@/constants/router"; const router = createRouter({ history: createWebHashHistory(import.meta.env.BASE_URL), routes: routesConfig, }); export default router; -
在側(cè)邊欄的布局中使用
<script setup lang="ts"> import { h, onBeforeMount, ref } from "vue"; import { useRouter } from "vue-router"; import { useStore } from "@/store"; import { BarChartOutlined, UserOutlined, DatabaseOutlined, AppstoreOutlined, } from "@ant-design/icons-vue"; import { routesConfig } from "@/constants/router"; import type { RouteRecordRaw } from "vue-router"; import type { ItemType } from "ant-design-vue"; const router = useRouter(); const store = useStore(); let MenuOptions = ref<ItemType[]>([]); // 側(cè)邊欄顯示圖標(biāo)的配置,key為配置文件中的router的name const siderBarIcon: { [key: string]: any } = { Home: () => h(BarChartOutlined), Collection: () => h(UserOutlined), DataCenter: () => h(DatabaseOutlined), ModelCenter: () => h(AppstoreOutlined), }; // 獲取單個(gè)導(dǎo)航菜單的數(shù)據(jù) const decodeConfig = (item: any) => { const routeMeta = item?.meta ?? {}; if ( !item.name || !routeMeta || routeMeta.unShowSiderBar || !routeMeta.breadcrumbName ) return undefined; let MenuItem: any = { key: item?.name, label: routeMeta.breadcrumbName, title: routeMeta.breadcrumbName, }; if (Object.keys(siderBarIcon).includes(item?.name)) { MenuItem["icon"] = siderBarIcon[item?.name]; } return MenuItem; }; /** * 遍歷router的配置文件,將配置轉(zhuǎn)化為導(dǎo)航菜單的配置項(xiàng) */ const resolveRouter = ( routeConfigs: Readonly<RouteRecordRaw[]> ): ItemType[] => { let childrenItems: ItemType[] = []; routeConfigs.map((routeConfig: any) => { let configItem = decodeConfig(routeConfig); if (configItem !== undefined) { if (routeConfig?.children && routeConfig?.children?.length != 0) { const childrenData = resolveRouter(routeConfig?.children); if (childrenData.length != 0) { configItem["children"] = childrenData; } } childrenItems.push(configItem); } }); return childrenItems; }; /** * 選中時(shí)跳到對(duì)應(yīng)的路由 */ const onSelect = ({ item }: any) => { if (item?.originItemValue?.key) { router.push({ name: item?.originItemValue?.key }); } }; onBeforeMount(() => { MenuOptions.value = resolveRouter(routesConfig); }); </script> <template> <a-menu :inlineIndent="18" :style="{ height: '100%', borderRight: 0 }" mode="inline" theme="light" :items="MenuOptions" @select="onSelect" :openKeys="store.openMenuKeys" :selectedKeys="store.selectMenuKeys" ></a-menu> </template>
2.2 在 router/index.ts 中設(shè)置路由守衛(wèi)等
- 包括的功能:(將以下的值存儲(chǔ)在 store 中,全局可以使用)
- 獲取側(cè)邊欄當(dāng)前展開(kāi)的菜單名稱
- 獲取側(cè)邊欄中當(dāng)前選擇的菜單
- 獲取當(dāng)前面包屑該顯示的內(nèi)容
- 判斷是否已經(jīng)是登錄狀態(tài)
router.beforeEach((to, from) => {
const store = useStore()
if (from.name != to.name) {
const { openKeys, breadcrumbConfig } = resolveToMached(to.matched)
if (!to.meta.unShowSiderBar) {
// 設(shè)置側(cè)邊欄被選中的key
store.setSelectMenuKeys(to.name as string)
}
// 設(shè)置側(cè)邊欄展開(kāi)的key
store.setOpenMenuKeys(openKeys)
// 設(shè)置面包屑
if (to.meta.unDirectParent) {
// 如果到達(dá)的頁(yè)面是不顯示側(cè)邊欄的,直接將當(dāng)前的路徑加入面包屑,不使用重新遍歷的
const bread = store.breadcrumbConfig
if (!bread.find((item) => item.breadcrumbName == to.meta.breadcrumbName)) {
bread.push({ breadcrumbName: to.meta.breadcrumbName, path: to.path, query: to.query })
}
store.setBreadcrumbConfig(bread)
} else {
breadcrumbConfig.map((item) => {
if (item.path == to.path) {
item.query = to.query
}
})
store.setBreadcrumbConfig(breadcrumbConfig)
}
}
if (to.name !== 'Login' && !store.token) {
// 此路由需要授權(quán),請(qǐng)檢查是否已登錄
// 如果沒(méi)有,則重定向到登錄頁(yè)面
return {
path: '/login'
}
}
})
/**
* 處理to的參數(shù)中的matched, 獲取展開(kāi)的key和面包屑的顯示
* @param matcheds
* @returns
*/
const resolveToMached = (matcheds: RouteRecordNormalized[]) => {
const openKeys: string[] = []
const breadcrumbConfig: Array<any> = []
matcheds.map((matchItem) => {
if (!matchItem.name) return
openKeys.push(matchItem?.name as string)
const routeMeta = matchItem?.meta ?? {}
if (!routeMeta.breadcrumbName) return
const breadcrumbItem: { [key: string]: any } = { breadcrumbName: routeMeta.breadcrumbName }
if (matchItem.components) {
breadcrumbItem['path'] = matchItem.path
}
breadcrumbConfig.push(breadcrumbItem)
})
return { openKeys, breadcrumbConfig }
}
2.3 顯示面包屑
<a-breadcrumb :routes="store.breadcrumbConfig">
<template #itemRender="{ route }">
<span v-if="!route.path">{{ route.breadcrumbName }}</span>
<router-link
v-else
:to="{ path: route.path, query: route.query }"
class="click_bread_crumb"
>{{ route.breadcrumbName }}</router-link
>
</template>
</a-breadcrumb>