浏览代码

feat: 面包屑组件

runningwater 5 月之前
父节点
当前提交
2aed3223b0
共有 5 个文件被更改,包括 83 次插入4 次删除
  1. 3 0
      components.d.ts
  2. 1 0
      package.json
  3. 12 4
      pnpm-lock.yaml
  4. 65 0
      src/layout/components/BreadCrumb/index.vue
  5. 2 0
      src/layout/components/Navbar.vue

+ 3 - 0
components.d.ts

@@ -9,7 +9,10 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     AppMain: typeof import('./src/layout/components/AppMain.vue')['default']
+    BreadCrumb: typeof import('./src/layout/components/BreadCrumb/index.vue')['default']
     Dashboard: typeof import('./src/views/Dashboard/index.vue')['default']
+    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
+    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCard: typeof import('element-plus/es')['ElCard']
     ElIcon: typeof import('element-plus/es')['ElIcon']

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "element-plus": "^2.9.7",
     "normalize.css": "^8.0.1",
     "path-browserify": "^1.0.1",
+    "path-to-regexp": "^8.2.0",
     "pinia": "^3.0.1",
     "pinia-plugin-persistedstate": "^4.3.0",
     "unplugin-element-plus": "^0.9.1",

+ 12 - 4
pnpm-lock.yaml

@@ -1,9 +1,5 @@
 lockfileVersion: '6.0'
 
-settings:
-  autoInstallPeers: true
-  excludeLinksFromLockfile: false
-
 dependencies:
   '@iconify/vue':
     specifier: ^5.0.0
@@ -23,6 +19,9 @@ dependencies:
   path-browserify:
     specifier: ^1.0.1
     version: 1.0.1
+  path-to-regexp:
+    specifier: ^8.2.0
+    version: 8.2.0
   pinia:
     specifier: ^3.0.1
     version: 3.0.1(typescript@5.7.2)(vue@3.5.13)
@@ -3232,6 +3231,11 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /path-to-regexp@8.2.0:
+    resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
+    engines: {node: '>=16'}
+    dev: false
+
   /pathe@2.0.3:
     resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
 
@@ -4079,3 +4083,7 @@ packages:
     resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==}
     engines: {node: '>=12.20'}
     dev: true
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false

+ 65 - 0
src/layout/components/BreadCrumb/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <!-- 创建一个面包屑导向组件,设置分隔符为 /, 并添加一些样式 -->
+  <el-breadcrumb separator="/" class="breadcrumb">
+    <el-breadcrumb-item v-for="(route, index) in list" :key="route.path">
+      <span v-if="list.length - 1 === index"> {{ route.meta?.title }}</span>
+      <a v-else @click="handleLink(route)">{{ route.meta?.title }}</a>
+    </el-breadcrumb-item>
+  </el-breadcrumb>
+</template>
+<script lang="ts" setup>
+import router from "@/router";
+import type { RouteLocationMatched } from "vue-router";
+import { compile } from "path-to-regexp";
+// 定义 PartialRouteLocationMatched 类型,只取 RouteLocationMatched 的部分属性
+type PartialRouteLocationMatched = Partial<RouteLocationMatched>;
+// 获取当前路由
+const currentRoute = useRoute();
+// 定义响应式变量 List,用于存储面包屑导航列表
+const list = ref<PartialRouteLocationMatched[]>([]);
+// 定义获取面包屑导航列表的函数
+const getBreadCrumb = () => {
+  // 过虑出有 meta.title 属性的路由匹配
+  let matched = currentRoute.matched.filter(
+    (item) => item.meta.title
+  ) as PartialRouteLocationMatched[];
+  // 如果当前访问的不是首页,在面包屑列表开头添加首页项
+  if (matched[0]?.path !== "/dashboard") {
+    matched = [
+      {
+        path: "/dashboard",
+        meta: {
+          title: "dashboard"
+        }
+      },
+      ...matched
+    ];
+  }
+  // 过滤掉 meta.breadcrumb 为 false 的路由匹配项
+  list.value = matched.filter((item) => item.meta?.breadcrumb !== false);
+};
+
+// 监听路由路由路径变化,当路径变化时调用 getBreadCrumb 函数
+watch(() => currentRoute.path, getBreadCrumb, { immediate: true });
+
+// 定义编译路径的函数,根据路径模板和当前路由参数生成完整路径
+function compilePath(path: string) {
+  const params = currentRoute.params; // 当前路由参数
+  const regex = compile(path); // 编译路径模板为正则表达式
+  return regex(params); // 根据当前路由参数替换路径模板中的参数
+}
+// 定义处理面包屑项点击事件的函数
+function handleLink(route: PartialRouteLocationMatched) {
+  const { path, redirect } = route;
+  if (redirect) {
+    router.push(redirect as string);
+  } else {
+    router.push(compilePath(path!));
+  }
+}
+</script>
+<style lang="scss" scoped>
+.breadcrumb {
+  @apply leading-50px text-lg ml-30px inline-block;
+}
+</style>

+ 2 - 0
src/layout/components/Navbar.vue

@@ -5,6 +5,8 @@
       :collapse="sidebar.opened"
       @toggle-collapse="toggleSidebar"
     ></hamburger>
+    <!-- 面包屑导航组件 -->
+    <bread-crumb></bread-crumb>
   </div>
 </template>
 <script lang="ts" setup>