菜单树检索
题干
请在index.js
和index.html
文件中根据现有的DOM结构(基础项目提供的DOM结构和id、选择器等不做修改)实现二级菜单数据异步获取(使用axios)和搜索功能(使用vue2.x语法)。
具体要求如下:
页面加载后输入框内软为空,页面显示异步获取二级菜单的所有数据;输入待查找的字段后,页面中显示包含改字段的所有菜单数据,并将改搜索内软的菜单数据标记为黄色背景,具体表现如下:
1、若该菜单有父级菜单,则返回其父级菜单及同胞菜单。
2、若该菜单有子级菜单,则返回该菜单及其下子级菜单。
3、若该菜单既无父级也无子级,则返回菜单本身即可。
4、推荐测试字段:查询、首页、管理、配置、维护。
5、 页面加载后显示整个菜单数据。
6、 搜索关键字后显示对应搜索数据,且将关键字高亮显示。
最终效果如【effect.gif】所示
提供的文件目录结构:
├── data.json
├── index.html
└── js
├── axios.min.js
├── index.js
└── vue.js
问题解析
依鄙人所见,本题主要有4个点:1. 数据获取,2.页面数据遍历, 3.菜单搜索功能,4. 高亮处理。
获取的数据存放需要一个变量,使用
rawData
,输入框需要一个变量获取他的值search
,搜索的结果同样需要一个变量,当然这个结果是随search
变化的,因此我们把它加在computed
中,
大致结构如下:
const app = new Vue({
el: "#app",
// 在此处补全代码,实现二级菜单搜索功能
data:{
search:'',
rawData:[]
},
methods:{
},
mounted(){
},
computed:{
menu(){
return this.rawData
}
}
});
解题
1. 数据获取
使用axios直接异步获取文件即可,需要注意的是,获取的时机。可在created
创建完vue实例或者mounted
挂在完成等事件后获取。比较简单,如下
mounted(){
axios.get('./data.json').then(res=>{
// 获取数据
this.rawData = res.data
})
},
2.页面数据遍历
其中,v-model
监听用户输入,:style="{backgroundColor:item.style}"
,这里的item.style
是最后一步做高亮时,添加的一个类属性,存放背景颜色的值。
注:页面上需要显示哪些内容就要去看datajson
文件的格了,这里不多说明
注::key="children.meta.title"
要用一个唯一的值,否则会报错
<div id="app">
<input type="text" v-model="search" placeholder="请输入要搜索的菜单内容" />
<ul>
<li v-for="item in menu" :key="item.meta.title">
<span :style="{backgroundColor: item.style}">{{item.meta.title}}</span>
<ul v-if="item.children">
<li v-for="children in item.children" :key="children.meta.title">
<span :style="{backgroundColor: children.style}">{{children.meta.title}}</span>
</li>
</ul>
</li>
</ul>
</div>
3. *菜单搜索功能
-
如果为空,显示全部,且都不高亮。这里搜索的结果是如果标题或者找到子项标题存在关键字,就返回整个项,因此,定义一个
flag
判断要不要添加到结果,如果在每次判断中直接添加可能会出现重复项或者代码复杂难读。 -
遍历使用
forEach
,因为forEach
没continue
,因此需要一个if
区间来处理遍历子项目 -
判断是否包含关键字,可以使用
String.includes()
方法,包含的话会返回true
,否则false
。关键字为空返回true。
computed:{
menu(){
let result=[]
this.rawData.forEach(item => {
let flag = false;
if(item.meta.title.includes(this.search)){
flag = true
}
if(item.children){
item.children.forEach((children,index)=>{
if(children.meta.title.includes(this.search)){
flag = true
}
})
}
flag && result.push(item)
});
return result
}
}
这时候已经可以搜索了
4. 高亮处理
高亮处理。添加一个属性来存放背景颜色。我们使用style
-
遍历到项的时候,给
style
默认为空,css中backgorud
值为空的话就没用背景颜色。有关键字就改为黄色 -
子项目也一样,先给默认值,如果存在关键字,子项的
style
属性改为黄色 -
如果关键字为空。那么背景颜色也要为空,因为默认是不加背景的
修改如下:
computed:{
menu(){
let result=[]
this.rawData.forEach(item => {
let flag = false;
item.style=''
if(item.meta.title.includes(this.search)){
flag = true
/* 如果为空的话也不高亮,为空是显示全部 */
this.search && (item.style='yellow')
}
if(item.children){
item.children.forEach((children,index)=>{
item.children[index].style=''
if(children.meta.title.includes(this.search)){
flag = true
this.search && (item.children[index].style='yellow')
}
})
}
flag && result.push(item)
});
return result
}
}
两个关键文件的代码:
index.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>test</title>
<script src="./js/vue.js"></script>
<script src="./js/axios.min.js"></script>
<style>
input {
width: 200px;
height: 32px;
padding-left: 5px;
}
</style>
</head>
<body>
<!-- 需求:输入待查找的字段,输出包含该字段的所有菜单数据。
1、若该菜单有父级菜单,则返回其父级菜单及同胞菜单。
2、若该菜单有子级菜单,则返回该菜单及其下子级菜单。
3、若该菜单既无父级也无子级,则返回菜单本身即可。
测试字段:查询、首页、管理、配置、维护 -->
<div id="app">
<input type="text" v-model="search" placeholder="请输入要搜索的菜单内容" />
<ul>
<li v-for="item in menu" :key="item.meta.title">
<span :style="{backgroundColor: item.style}">{{item.meta.title}}</span>
<ul v-if="item.children">
<li v-for="children in item.children" :key="children.meta.title">
<span :style="{backgroundColor: children.style}">{{children.meta.title}}</span>
</li>
</ul>
</li>
</ul>
</div>
</body>
<script type="text/javascript" src="./js/index.js"></script>
</html>
index.js
const app = new Vue({
el: "#app",
// 在此处补全代码,实现二级菜单搜索功能
data:{
search:'',
rawData:[]
},
mounted(){
axios.get('./data.json').then(res=>{
// 获取数据
this.rawData = res.data
})
},
computed:{
menu(){
let result=[]
this.rawData.forEach(item => {
let flag = false;
item.style=''
if(item.meta.title.includes(this.search)){
flag = true
/* 如果为空的话也不高亮,为空是显示全部 */
this.search && (item.style='yellow')
}
if(item.children){
item.children.forEach((children,index)=>{
item.children[index].style=''
if(children.meta.title.includes(this.search)){
flag = true
this.search && (item.children[index].style='yellow')
}
})
}
flag && result.push(item)
});
return result
}
}
});