Skip to content

面包屑导航

  • 静态面包屑

  • 动态面包屑

静态面包屑

指的是:在每个页面中写死对应的面包屑菜单,缺点也很明显

  1. 每个页面都得写一遍
  2. 页面路径结构变化了,得手动更改

简单来说就是 不好维护,不好扩展

动态面包屑

  • 根据当前的url自动生成面包屑导航菜单

无论之后路径发生了什么变化 动态面包屑都会正确的进行计算

那么在后续的实现过程中,我们将会分成三大步骤来实现

  1. 创建,渲染基本的面包屑组件

  2. 计算面包屑结构数据

  3. 根据数据渲染动态面包屑内容

创建,渲染基本的面包屑组件

  • 创建 components/BreadCrumb.vue
vue
<template>
  <el-breadcrumb class="breadcrumb" separator="/">
    <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
    <el-breadcrumb-item><a href="/">活动管理</a></el-breadcrumb-item>
    <el-breadcrumb-item>活动列表</el-breadcrumb-item>
    <!-- 面包屑的最后一项 -->
    <el-breadcrumb-item>
      <span class="no-redirect">活动详情</span>
    </el-breadcrumb-item>
  </el-breadcrumb>
</template>

<script setup></script>

<style lang="scss" scoped>
.breadcrumb {
  display: inline-block;
  font-size: 14px;
  line-height: 50px;
  margin-left: 8px;

  ::v-deep .no-redirect {
    color: #97a8be;
    cursor: text;
  }
}
</style>
  • 挂载到 NavBar 上面
vue
<template>
  <div class="navbarContent">
    <div class="sidebaropen">
      <SideBarOpen></SideBarOpen>
    </div>
    <div class="breadcrumb">
      <BreadCrumb></BreadCrumb>
    </div>
    <div class="avatar">
      <Avater></Avater>
    </div>
  </div>
</template>

<script setup></script>

<style lang="scss" scoped>
.navbarContent {
  width: 100%;
  height: 60px;
  border-bottom: 1px solid #ccc;
  .sidebaropen {
    float: left;
    margin-top: 15px;
    margin-left: 20px;
    cursor: pointer;
  }
  .breadcrumb {
    float: left;
    margin-left: 10px;
    margin-top: 1px;
  }
  .avatar {
    width: 80px;
    height: 50px;
    float: right;
    margin-right: 20px;
    margin-top: 5px;
  }
}
</style>

动态计算面包屑结构数据

现在我们是完成了一个静态的 面包屑,接下来咱们就需要依托这个静态的菜单来完成动态的。

对于现在的静态面包屑来说,他分成了两个组件:

  1. el-breadcrumb:包裹性质的容器
  2. el-breadcrumb-item:每个单独项

如果我们想要完成动态的,那么就需要 **依据动态数据,渲染 el-breadcrumb-item

所以说接下来我们需要做的事情就很简单了

  1. 动态数据
  2. 渲染 el-breadcrumb-item
  3. 点击事件
  4. 动画效果

最后合计

我们希望可以创建一个数组,每个数组中每个item都表示一个路由信息

我们还得渲染面包屑,里面写好点击事件 最后如下

  • components/BreadCrumb.vue
vue
<template>
  <el-breadcrumb class="breadcrumb" separator="/">
    <transition-group name="breadcrumb">
      <el-breadcrumb-item
        v-for="(item, index) in breadcrumbData"
        :key="item.path"
      >
        <!-- 不可点击项 -->
        <span v-if="index === breadcrumbData.length - 1" class="no-redirect">{{
          generateTitle(item.meta.title)
        }}</span>
        <!-- 可点击项 -->
        <a v-else class="redirect" @click.prevent="onLinkClick(item)">{{
          generateTitle(item.meta.title)
        }}</a>
      </el-breadcrumb-item>
    </transition-group>
  </el-breadcrumb>
</template>

<script setup>
import { generateTitle } from "@/utils/i18n";
import { useVariables } from "@/stores/variables";
const route = useRoute();
console.log(route);
// 生成数组数据
const breadcrumbData = ref([]);
const getBreadcrumbData = () => {
  breadcrumbData.value = route.matched.filter(
    (item) => item.meta && item.meta.title
  );
};
// 监听路由变化时触发
watch(
  route,
  () => {
    getBreadcrumbData();
  },
  {
    immediate: true,
  }
);
// 处理点击事件
const router = useRouter();
const onLinkClick = (item) => {
  console.log(item);
  router.push(item.path);
};
// 主题更换
console.log(useVariables().variables.menuBg);
const linkHoverColor = ref(useVariables().variables.menuBg);
</script>

<style lang="scss" scoped>
.breadcrumb {
  display: inline-block;
  font-size: 14px;
  line-height: 50px;
  margin-left: 8px;
  .redirect {
    color: #666;
    font-weight: 600;
  }

  .redirect:hover {
    // 将来需要进行主题替换,所以这里不去写死样式
    color: v-bind(linkHoverColor);
  }
  :deep(.no-redirect) {
    color: #97a8be;
    cursor: text;
  }
}
</style>

动画效果的 css

新建 styles/transition 样式文件

scss
.breadcrumb-enter-active,
.breadcrumb-leave-active {
  transition: all 0.5s;
}

.breadcrumb-enter-from,
.breadcrumb-leave-active {
  opacity: 0;
  transform: translateX(20px);
}

.breadcrumb-leave-active {
  position: absolute;
}

styles/index 中导入

scss
@import "./transition.scss";