面包屑
面包屑,准确的说叫面包屑导航(BreadcrumbNavigation),这个概念源自与一个童话故事,大体就是两个傻小子在森林里走迷路了,发现道路沿边的面包屑,最后通过这些面包屑走出了森林,找到了回家的路,总之就是面包屑就是有一个导航的作用.
在前端领域,面包屑效果是很常见的,特别是一些 官网、B端产品等等。
分享一下我做的项目demo吧
如图:
设计
面包屑组件效果肯定是要跟随侧边菜单的,侧边菜单的数据一般也是后端根据权限不同返回的。
侧边菜单数据结构大体如下:
menuData: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home',
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'MallManage/MallManage',
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'UserManage/UserManage',
},
{
label: '其他',
name: 'other',
icon: 'location',
path: '/other',
children: [
{
path: '/other/page1',
name: 'page1',
label: '其他1',
icon: 'setting',
url: 'Other/PageOne',
},
{
path: '/other/page2',
name: 'page2',
label: '其他2',
icon: 'setting',
url: 'Other/PageTwo',
},
],
整体思路:
- 获取当前路由
path - 通过侧边菜单数据结构可以看出是一个
树的物理结构,当前路由就是一个子节点 - 只要从树
root一直找到对应的那个子节点,这一段就是我们所需要的数据
比如:
当前路由: /home/other/otherPage1
面包屑导航: 首页>其他>Page1
只需要将当前路由拆分,在菜单树中从上往下依次查找就可以了
对应关系如: /home => 一级菜单, /mult/three => 一级菜单/二级菜单3, /mult/three/threeChild1 => 一级菜单/二级菜单3/三级菜单1
步骤:
- 由于在vue项目中,导航栏和面包屑常常不同组件中,比如导航栏为Aside组件,面包屑在Header中。我这里用vuex进行状态存储,存储一个面包屑数据List(我们叫做
breadcrumbList) - 首先,点击导航栏会触发一个click事件(代码中为clickMenu),在事件中对面包屑数据List进行更新。
- 如何更新List,上面已经说明了思路,切分+遍历树,把每一层节点当作一个数据放进
breadcrumbList中 - 其次在面包屑展示模块中,对
breadcrumbList遍历,每个节点为一个item,中间用 /划分
代码
export default {
data() {
return {
//导航栏Tab
menuData: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home',
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'MallManage/MallManage',
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'UserManage/UserManage',
},
{
label: '其他',
name: 'other',
icon: 'location',
path: '/other',
children: [
{
path: '/other/page1',
name: 'page1',
label: '其他1',
icon: 'setting',
url: 'Other/PageOne',
},
{
path: '/other/page2',
name: 'page2',
label: '其他2',
icon: 'setting',
url: 'Other/PageTwo',
},
],
},
],
};
},
methods: {
getCurrentBreadcrumbData(currentPath) {
//这里将当前路径进行切分
//如“/other/otherPage1” 转换成数组["other","otherPage1"]
const currentPathSplit = currentPath.path.split('/').filter((c) => !!c);
console.log('currentPathSplit', currentPathSplit);
//二次转换,把除了一级节点之后的节点都拼接上之前的节点
//["other","otherPage1"] 转成 ["other","other/otherPage1"]
currentPathSplit.forEach((c, i) => {
if (i) {
currentPathSplit[i] = currentPathSplit[i - 1] + '/' + c;
} else {
currentPathSplit[i] = '/' + currentPathSplit[i];
}
});
//初始化一个面包屑列表,默认包含首页tab
const newBreadTabs = [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home',
},
];
//导航栏menuData可以看成一棵树,每个数据可以看作一个节点
let Tree = this.menuData;
currentPathSplit.forEach((c) => {
//console.log(c);
//匹配path,获取menu tab
const findItem = Tree.find((tab) => tab.path == c);
if (findItem) {
//放进面包屑列表中
newBreadTabs.push(findItem);
//匹配到导航栏的节点之后,如果有chilren节点,则把这个chilren节点赋值给Tree,下次遍历就是去新的Tree里匹配
if (findItem.children) {
Tree = findItem.children;
}
}
});
//console.log('newBreadTabs', newBreadTabs);
return newBreadTabs;
},
//点击导航栏Tab事件
clickMenu(item) {
//console.log(item);
//console.log(this.$route);
//使用router进行页面跳转,这里的逻辑可以不用管,和面包屑无关
if (this.$route.path == item.path || (this.$route.path == '/home' && item.path == '/')) {
return;
}
const newBreadTabs = this.getCurrentBreadcrumbData(item);
//更新面包屑list全局数据-这样面包屑数据才能被面包屑导航栏读取到展示和更新
this.$store.commit('addBreadTabs', newBreadTabs);
this.$router.push(item.path);
},
html我这里使用了element-ui组件,自己写原生html也是用遍历的思想
<!--面包屑-->
<el-breadcrumb separator="/" class="breadcrumb">
<el-breadcrumb-item v-for="tab in getTabsBreadList" :key="tab.name" :to="{ path: tab.path }">{{ tab.label }}</el-breadcrumb-item>
</el-breadcrumb>
最后附上该项目完整github: github.com/Chenhanyi38…