效果图:
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-persistedstateyarn 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;
大功告成!!!