vue3的布局和路由配置

一、頁(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>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容