面包屑足迹

面包屑导航、足迹的实现

相当于一个路径导航,也可以记录用户的访问历史,常常需要这样一种效果。在我写类似的导航逻辑时,遇到一些问题,在此记录。

导航样式

样式原本是想使用element的面包屑导航,如图所示的组件:

1
2
3
4
5
6
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>活动管理</el-breadcrumb-item>
<el-breadcrumb-item>活动列表</el-breadcrumb-item>
<el-breadcrumb-item>活动详情</el-breadcrumb-item>
</el-breadcrumb>

后来改了下,并没有使用它的功能,相当于使用普通的divli

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="vue_el_breadcrumb" th:fragment="el_breadcrumb" class="img-rounded ">
<!-- 分割线-->
<el-divider></el-divider>
<el-breadcrumb separator=">">
<el-breadcrumb-item><span class="el-icon-location-information"></span></el-breadcrumb-item>

<el-breadcrumb-item v-for="page in breadcrumb"><a :href="page.path">{{page.title}}</a></el-breadcrumb-item>

</el-breadcrumb>
<!-- 分割线-->
<el-divider></el-divider>
</div>

<style>
#vue_el_breadcrumb {
background-color: aliceblue;
padding-left: 20px;
}

</style>

业务逻辑

由于想使用Vuev-for指令进行动态的遍历,所以需要一个数组,起名为breadcrumb

原先打算将这个数组存入sessionStorage,在每次切换页面的时候读取、截取这个数组:

javaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<script>
const vue_el_breadcrumb2 = new Vue({
el: "#vue_el_breadcrumb2",
data: {
//面包屑数组
breadcrumb: [],
//存储当前页信息的对象
curPage:{
title:'',
path:''
}
},
created:function () {
//获取当前页信息,并保存
this.curPage.title=document.title;
this.curPage.path=window.location.pathname;

//读取数组
let breadcrumb= JSON.parse(sessionStorage.getItem("breadcrumb"));
//新建一个过渡数组
let newArray=[];
//表示当前页是否已经存在于数组中
let isExist=false;
//当获取到的数组不为空时
if (breadcrumb!=null){
//遍历数组
for (let index in breadcrumb){
//把数组中的元素存入过渡数组中
newArray.push(breadcrumb[Number(index)]);
//验证当前页是否已存在
if (breadcrumb[Number(index)].path==this.curPage.path){
//覆盖原数组
breadcrumb=newArray;
isExist=true;
break;
}
}
//为空则使用当前值
}else{
breadcrumb=this.breadcrumb;
}
//不存在时,将当前页添加入数组
if (!isExist){
breadcrumb.push(this.curPage)
}

//存入sessionStorage
sessionStorage.setItem("breadcrumb",JSON.stringify(breadcrumb));
//存入vue data 以遍历
this.breadcrumb=breadcrumb;
}
})
</script>

但这样做只解决了重复问题、截取的问题,还有两个Bug,一个是排序,一个是误存,因此,当用户直接访问详情页,然后再访问主页时,就会出现这种情况:

Java

因此,针对这两项,我做了改善,这是java后端版本的代码,方便查看效果:

  • Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**面包屑导航 辅助
* @param curPage 要存储的pageDTO对象 接收json格式 ,如:
* {
* "title":"标题1",
* "path":"路径1"
* }
* @param request 用于读取session
* @return 返回处理后的数组
*/
@PostMapping("/shApi/breadcrumbHelper")
@ResponseBody
public ShResultDTO<String, Object> getBreadcrumbArray(
@RequestBody PageDTO curPage, HttpServletRequest request) {

//读取session中的数组
PageDTO[] breadcrumb = (PageDTO[]) request.getSession().getAttribute("breadcrumb");

//处理和截取数组
breadcrumb = jsHelperService.cutBreadcrumbArray(breadcrumb, curPage);

//把处理后的数组,存入session
request.getSession().setAttribute("breadcrumb", breadcrumb);

//封装结果
Map<String, Object> map = new HashMap<>();
map.put("breadcrumb", breadcrumb);

return new ShResultDTO<>(200, "面包屑数组已处理", map, null);
}
  • Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package cn.shirtiny.community.SHcommunity.Service.ServiceImpl;

import ...

@Service
public class JsHelperServiceImpl implements IjsHelperService {


//创建页码数组
@Override
public long[] createPageNumArray(Long curPage, Long pages) {
long pageNum = curPage - 3;
ShArrayQueue queue = new ShArrayQueue(7);
for (int i = 0; i < queue.getMaxSize(); i++) {
if (pageNum > 0 && pageNum <= pages) {
queue.add(pageNum);
}
pageNum++;
}
return queue.toArray();
}


//处理和截取面包屑数组
@Override
public PageDTO[] cutBreadcrumbArray(PageDTO[] breadcrumb, PageDTO curPage) {

//新建栈
Stack<PageDTO> pageStack = new Stack<>();

//标识是否page已经存在
boolean isExsit = false;

//若session中能取到值
if (breadcrumb != null && breadcrumb.length != 0) {
//遍历数组
for (PageDTO pageDTO : breadcrumb) {

//只要当前元素path的长度比curPage的path长度小或相等,元素就入栈
if (pageDTO.getPath().split("/").length <= curPage.getPath().split("/").length) {
pageStack.push(pageDTO);
}

//根据路径,比较page与要存入的page相同时
if (curPage.getPath().equals(pageDTO.getPath())) {
//相同则表示curPage已经存在,不需要存入curPage,停止继续入栈,舍弃数组以后的元素
isExsit = true;
//退出数组遍历
break;
}
}
} else {
//如果在session中取不到值
//只要当前的访问页不是主页
if (!curPage.getPath().equals("/")) {
//存入一个主页
PageDTO homePage = new PageDTO("主页", "/");
pageStack.push(homePage);
}
}

//在数组中不存在curPage时,存入curPage
if (!isExsit) {
pageStack.push(curPage);
}


//排序 常规写法
// Comparator<PageDTO> c=new ShPathComparator();
// pageStack.sort(c);

/*
//lambda表达式写法
pageStack.sort(
(page1,page2)->{
String[] split1 = page1.getPath().split("/");
String[] split2 = page2.getPath().split("/");
return split1.length-split2.length;
}
);
*/
//或者
pageStack.sort(
Comparator.comparingInt(page -> page.getPath().split("/").length)
);

//把已入栈元素转为数组,用于覆盖原数组,此处利用toArray()带参数的方法
return pageStack.toArray(new PageDTO[0]);
}
}
  • 其中,Comparator比较器为
1
2
3
4
5
6
7
8
9
10
11
//路径比较器
public class ShPathComparator implements Comparator<PageDTO> {

@Override
public int compare(PageDTO page1, PageDTO page2) {
String[] split1 = page1.getPath().split("/");
String[] split2 = page2.getPath().split("/");

return split1.length-split2.length;
}
}

这里对路径path的排序,是通过比较split(“/“)的长度来完成的,正在考虑其他方法。出于某种原因,我并没有采用将导航集合给定的做法。

结果分析

  • Test_1

①访问主页:

②访问详情页

③返回主页

  • Test_2

①直接访问详情页

②再访问主页

在直接访问详情页时,会显示主页、详情页,这里是我刻意的处理,现在还没有对页面标识、排序的思路,暂且这么处理。