ruoyi vue3 自定义子路由面包屑处理

效果图:

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

代码:
html
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
javascript 复制代码
<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 findRouteByPath(path, routes) {
  for (const route of routes) {
    if (route.path === path) {
      return route;
    }
    if (route.children) {
      const found = findRouteByPath(path, route.children);
      if (found) return found;
    }
  }
  return null;
}
// 获取完整的面包屑路径
function getBreadcrumb() {
  let matched = [];
  // 1. 获取当前路由的breadcrumbMenu(如果有)
  const breadcrumbMenu = route.meta?.breadcrumbMenu;
  const rootMenu = route.meta?.rootMenu;
  // 2. 如果有breadcrumbMenu,则从breadcrumbMenu开始构建面包屑
  if (breadcrumbMenu) {
    // 在权限路由中查找breadcrumbMenu对应的路由
    const activeRoute = findRouteByPath(breadcrumbMenu, permissionStore.routes);
    if (activeRoute) {
      // 获取activeRoute的完整路径链
      const activeMatched = getRouteChain(
        activeRoute,
        permissionStore.routes,
        [],
        rootMenu
      );
      matched = activeMatched.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);
    }
  }
}
// 获取路由的完整链(从根到当前路由)
function getRouteChain(route, routes, chain = [], rootMenu) {
  if (!route) return chain;
  // 添加到链中
  chain.unshift(route);
  if (route.path !== rootMenu) {
    const parentPath = route.path.split("/").slice(0, -1).join("/") || rootMenu;
    const parentRoute = findRouteByPath(parentPath, routes);
    if (parentRoute) {
      return getRouteChain(parentRoute, routes, chain);
    }
  }

  return chain;
}

watchEffect(() => {
  if (route.path.startsWith("/redirect/")) {
    return;
  }
  getBreadcrumb();
});
getBreadcrumb();
</script>
css
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 =

javascript 复制代码
{
    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
javascript 复制代码
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;

大功告成!!!

相关推荐
OkGogooXSailboat15 小时前
flume的log4j日志无输出排查
java·flume·1024程序员节
银河系的一束光15 小时前
tomcat问题
1024程序员节
weixin_4454766815 小时前
Vue 项目全局水印功能完整实现指南
vue·vue2·1024程序员节
黄毛火烧雪下15 小时前
【居中】相对定位 + 绝对定位 或 Flexbox 居中
1024程序员节
Samuel-Gyx15 小时前
数据结构--顺序表与链表
数据结构·算法·链表·1024程序员节
板鸭〈小号〉15 小时前
应用层自定义协议与序列化
运维·服务器·网络·1024程序员节
柳鲲鹏15 小时前
多种方法:OpenCV中修改像素RGB值
前端·javascript·opencv·1024程序员节
wanhengidc15 小时前
传奇手游可以使用云手机挂机搬砖吗
服务器·arm开发·智能手机·玩游戏·1024程序员节
吴禅染15 小时前
爱思唯尔期刊投稿经验
1024程序员节