vue项目处理列表数据的方法归纳
项目中遇到了3个列表,它们的要求各不同,将它们的处理方法总结一下
1、互斥列表
场景:列表只能展开某一项,点击另一项的时候,前一项折叠,点击的该项展开。
<template>
<div v-if="arr.length > 0">
<div v-for="(item, index) in arr" :key="index">
<div class="limits_txt" @click="showInfo(index)">
<div class="txt">{{item.scope_detail.title}}</div>
<div class="lable">
<van-icon :class="{arrowU: select&&(activeIndex === index), arrowD: !select&&(activeIndex === index)}" name="arrow"></van-icon>
</div>
</div>
<div class="genre_type" :class="{active: select&&(activeIndex === index)}">
// 展示item内容
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
select: false,
activeIndex:''
};
},
methods: {
showInfo(index) {
if(index!==this.activeIndex) {
this.activeIndex = index
this.select = true
} else {
this.select = !this.select
}
}
}
}
</script>
解释:全局变量activeIndex用来保存上一次点击的index,布尔值select表示该显示还是隐藏
每次点击时,将本次的index和activeIndex做比较,如果点击的是同一个,则直接把布尔类型的select变量反转;如果点击的不是同一个,就将当前点击的index保存再activeIndex里,再将select置为true。
页面中:class="{active: select&&(activeIndex === index)}"
动态绑定class,一定要加activeIndex === index这个判断,否则就不是一个item被影响了,item全会跟着动。
2、非互斥列表
场景:列表的所有项都能同时展开
// script
this.reviewList.forEach((item,index) => {
this.$set(item,'show',false)
})
解释:在列表的每一项中新增一个show字段,判断该项是显示还是隐藏。
3、从页面角度是一组唯一的数据,但是拿到的数据不在一个列表里。
场景:页面有n个仿微信的短音频,音频播放时有动画,也算是互斥的,页面上只能有一个动画。
// 数据结构长这样
"arr": [{
"submit_item": {
// 一级内容
"status": 20,
"type": 2,
"audio": {
"url": "xx_url",
"length": 12 // 音频长度,单位秒
}
},
"comment_items": [
// 二级内容
{
// 二级内容1
"type": 2,
"audio": {
"url": "xx_url",
"length":60 // 音频长度,单位秒
}
},
{
// 二级内容2
"type": 2,
"audio": {
"url": "xx_url",
"length":60 // 音频长度,单位秒
}
},
{
// 二级内容3
"type": 2,
"audio": {
"url": "xx_url",
"length":60 // 音频长度,单位秒
}
}]
}
返回的arr是一个数组,arr的每个item里又包含一个对象(submit_item)和一个数组(comment_items),item里的数组(comment_items)里又有一个对象,这里面的对象结构和item里的submit_item结构一致,都有音频信息。
<template>
<div class="review-card" v-for="(item, index) in reviewList" :key="index">
<div class="homework">
<img v-if="item.submit_item.type === 1" :src="item.submit_item.image.url">
<div v-if="item.submit_item.type === 2" class="comment-voice" :style="{width: setAudioWidth(item.submit_item.audio.length)}" @click="playAudio(item.submit_item.audio.url,'homework',index,'',item)">
<span class="comment-voice__audio" :class="{play:isplaying && activeIndex === (index +'#'), played: item.submit_item.audio.played && !(isplaying && activeIndex === (index +'#'))}"></span>
<span class="comment-voice__length">{{item.submit_item.audio.length}}"</span>
</div>
<div v-if="item.submit_item.type === 3" class="comment-text">{{item.submit_item.text}}</div>
</div>
<div class="teacheer-comment">
<div class="comment-content" :class="{active: item.show}" v-for="(it, idx) in item.comment_items" :key="idx">
<div class="comment-voice" :style="{width: setAudioWidth(it.audio.length)}" @click="playAudio(it.audio.url,'comment',index,idx,item,it)">
<span class="comment-voice__audio" :class="{play:isplaying && activeIndex === (index+'#'+idx), played: it.audio.played && !(isplaying && activeIndex === (index +'#'+idx))}"></span>
<span class="comment-voice__length">{{it.audio.length}}"</span>
</div>
</div>
</div>
</div>
<audio src="http://oya7y0n8c.bkt.clouddn.com/sound.mp3" ref="audio" @ended="over"></audio>
</template>
<script>
methods:{
playAudio(url,type,index,idx,item,it){
// 点击的音频是否是上一次点击那个,如果是,则切换暂停播放;如果不是上一个,那就把上个音频结束,播放现在点击的这个
if(type === 'homework') {
this.$set(item.submit_item.audio,'played',true)
if(this.activeIndex ===index +'#') {
this.isplaying = !this.isplaying
} else {
this.isplaying = false
this.activeIndex = index +'#'
this.isplaying = true
}
} else {
this.$set(it.audio,'played',true)
if(this.activeIndex ===index +'#' + idx) {
this.isplaying = !this.isplaying
} else {
this.isplaying = false
this.activeIndex = index +'#' + idx
this.isplaying = true
}
}
// this.$refs.audio.src = url
// 测试用音频
this.$refs.audio.src = 'http://oya7y0n8c.bkt.clouddn.com/sound.mp3'
this.isplaying?this.$refs.audio.play():this.$refs.audio.pause()
}
}
</script>
<style>
.comment-voice{
margin: 30px 0;
height: 80px;
line-height: 80px;
background-color: #f5f5f5;
position: relative;
overflow: hidden;
&__audio {
display: inline-block;
position: absolute;
top: 23px;
left: 24px;
width: 26px;
height: 34px;
background: url(~/assets/img/audioPlay.png) no-repeat 0 0;
background-position:-54px;
background-size: 78px 34px;
// animation:audio_playing 1s steps(1) infinite;
}
&__audio.played {
background: url(~/assets/img/audioStatic.png) no-repeat 0 0;
background-size: 26px 34px;
}
&__audio.play {
animation:audio_playing 1s steps(1) infinite;
}
@keyframes audio_playing {
0% {
background-position: 4px;
}
33% {
background-position: -22px;
}
66% {
background-position: -54px;
}
100% {
background-position: 0px;
}
}
}
</style>
解决办法:
因为也是互斥的动画,所以沿用方法1的思路,设置全局变量activeIndex来保存上次点击的index。但是这些音频不在一个数组里,我的解决办法是强行编码,新造一个index:把一级内容的index和二级内容的index拼起来,中间用#隔开。
这样每次点击的时候做判断,是一级内容还是二级内容,然后再拼接index,判断activeIndex,这样就可以保证互斥点击效果。
题外话:发现vue的数据操控视图真的非常方便,仿微信的语音播放动画一共有3个状态:未播放、播放中、播放过。分别对应红色,红色扩音器动画、灰色扩音器。默认是未播放状态,然后用play和played分别绑定播放中和播放过的状态。
:class="{play:isplaying && activeIndex === (index+'#'+idx), played: it.audio.played && !(isplaying && activeIndex === (index +'#'+idx))}"