效果图:

a/b/c/新增、编辑、详情之类的 需要展示在面包屑中 因为不需要首页就把首页删除了

代码:
html
<template>
  <div class="breadcrumb-box">
    当前位置:
    <el-breadcrumb class="app-breadcrumb" separator="/">
      <!-- <transition-group name="breadcrumb"> -->
      <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
        <span
          :style="{
            color: index == levelList.length - 1 ? '#ffffff' : '#b0c6d6',
          }"
          >{{ item.meta.title }}</span
        >
      </el-breadcrumb-item>
      <!-- </transition-group> -->
    </el-breadcrumb>
  </div>
</template>
js
<script setup>
import usePermissionStore from "@/store/modules/permission";
import useRouteToPageStore from "@/store/modules/routeToPage";
const route = useRoute();
const permissionStore = usePermissionStore();
const routeToPageStore = useRouteToPageStore();

const levelList = ref([]);
function findParentChain(routes, targetPath, parentPath = "") {
  for (const route of routes) {
    // 检查当前节点是否匹配目标路径
    const currentPath =
      parentPath + (route.path.startsWith("/") ? route.path : `/${route.path}`);
    if (currentPath.endsWith(`/${targetPath}`) || currentPath === targetPath) {
      return [route]; // 返回当前路由对象
    }

    // 递归检查子节点
    if (route.children) {
      const childChain = findParentChain(
        route.children,
        targetPath,
        currentPath
      );
      if (childChain.length > 0) {
        // 返回当前路由对象 + 子路由链
        return [route, ...childChain];
      }
    }
  }
  return [];
}
// 获取完整的面包屑路径
function getBreadcrumb() {
  let matched = [];
  // 1. 获取当前路由的breadcrumbMenu(如果有)
  const breadcrumbMenu = route.meta?.breadcrumbMenu;
  const rootMenu = route.meta?.rootMenu;
  // 2. 如果有breadcrumbMenu,则从breadcrumbMenu开始构建面包屑
  if (breadcrumbMenu) {
    const chain = findParentChain(
      permissionStore.defaultRoutes.filter((item) => item.path == rootMenu),
      breadcrumbMenu
    );
    matched = chain.filter((item) => item.meta?.title && item.meta);
    if (route.meta?.title) {
      matched.push({
        path: route.path,
        meta: { title: routeToPageStore.pageTitle || route.meta.title },
        redirect: "noRedirect",
      });
    }
  } else {
    // 3. 没有breadcrumbMenu,使用常规方法
    const pathNum = findPathNum(route.path);
    if (pathNum > 2) {
      const reg = /\/\w+/gi;
      const pathList = route.path.match(reg).map((item, index) => {
        if (index !== 0) item = item.slice(1);
        return item;
      });
      getMatched(pathList, permissionStore.defaultRoutes, matched);
    } else {
      matched = route.matched.filter((item) => item.meta && item.meta.title);
    }
    levelList.value = matched.filter((item) => item.meta && item.meta.title);
  }

  levelList.value = matched;
}
function findPathNum(str, char = "/") {
  let index = str.indexOf(char);
  let num = 0;
  while (index !== -1) {
    num++;
    index = str.indexOf(char, index + 1);
  }
  return num;
}
function getMatched(pathList, routeList, matched) {
  let data = routeList.find(
    (item) =>
      item.path == pathList[0] || (item.name += "").toLowerCase() == pathList[0]
  );
  if (data) {
    matched.push(data);
    if (data.children && pathList.length) {
      pathList.shift();
      getMatched(pathList, data.children, matched);
    }
  }
}

watchEffect(() => {
  if (route.path.startsWith("/redirect/")) {
    return;
  }
  getBreadcrumb();
});
getBreadcrumb();
</script>
css
<style lang="scss" scoped>
.breadcrumb-box {
  height: 40px;
  padding-left: 16px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  font-family: Source Han Sans CN, Source Han Sans CN;
  font-weight: 400;
  font-size: 12px;
  color: #ffffff;
  border-bottom: 1px solid #1c3667;
  background-color: #0a0a0a;
  .breadcrumb-container {
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 12px;
    height: 22px;
  }
}
.app-breadcrumb {
  display: flex;
  align-items: center;
  height: 22px;
}
.app-breadcrumb.el-breadcrumb {
  display: flex;
  align-items: center;
  font-size: 12px;
  .no-redirect {
    color: #97a8be;
    cursor: text;
  }
}
:deep(.el-breadcrumb__separator) {
  color: #888d98;
}
</style>
router/index.js文件

// 动态路由,基于用户权限动态去加载 在ruoyi原有的这个中加入新的 比如新增页面

export const dynamicRoutes = 

{
    path: "/system/user/data",
    component: Layout,
    hidden: true,
    permissions: ["system:user:list"],
    children: [
      {
        path: "",
        component: () => import("@/views/system/user/components/edit"),
        name: "UserEdit",
        meta: {
          title: "用户管理",
          activeMenu: "/system/user", //当前高亮的路由
          rootMenu: "/system", // 查找的根路由
          breadcrumbMenu: "user", // 面包屑菜单
        },
      },
    ],
  },
处理动态title  使用 pinia

title: "用户管理", 

  persist: true,  数据持久化 可以使用pinia的插件  pinia-plugin-persistedstate

npm i  pinia-plugin-persistedstate

yarn add pinia-plugin-persistedstate 

ruoyi自带 store/index.js  引入持久化插件

const store = createPinia();

import piniaPluginPersistedstate from "pinia-plugin-persistedstate";

store.use(piniaPluginPersistedstate); // 注入插件

export default store;

因为还有其他逻辑之后我新建一个文件 store/modules/routeToPage.js
const useRouteToPageStore = defineStore("routeToPage", {
  state: () => ({
    editParams: null, // 存储编辑页参数
    pageTitle: null, // 存储页面标题
  }),
  actions: {
    setEditParams(params) {
      this.editParams = params;
    },
    clearEditParams() {
      this.editParams = null;
    },
    setPageTitle(title) {
      this.pageTitle = title;
    },
    clearPageTitle() {
      this.pageTitle = null;
    },
  },
  persist: true,
});

export default useRouteToPageStore;

大功告成!!!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐