先上效果:
普通屏:
大屏:
也就是根据盒子大小来排布,能排满就直接排列,否则显示...
思路就是获取外部盒子宽度,除以每个集数的宽度+marginRight,得出每一列最多排列的个数maxNum,然后根据:集数总和 > (maxNum*2),来决定是否显示省略号,剩下的就是判断某几个元素的marginRight是否为0(以免超过盒子宽度,导致不必要的换行),整个组件如下:
<template>
<ul class="episode-ul" @click="onClick">
<template v-if="isFold">
<li
v-for="num in maxNum - 1"
:key="'fold' + num"
:data-num="num"
class="video-episode-li"
:class="{ active: selectNum === num }"
>
{{ num }}
<videoTypeIcon
:is-free-limit="list[num - 1].isFreeLimit === 1"
:is-vip-saction="list[num - 1].isVipSaction === 1"
/>
</li>
<li class="video-episode-li video-episode-more" data-num="more">…</li>
<li
v-for="num in leftNumArray"
:key="'last' + num"
:data-num="episodeLen - num"
:class="[{ active: selectNum === episodeLen - num },{'fold-item':num===0}]"
class="video-episode-li"
>
{{ episodeLen - num }}
<videoTypeIcon
:is-free-limit="list[episodeLen - num - 1].isFreeLimit === 1"
:is-vip-saction="list[episodeLen - num - 1].isVipSaction === 1"
/>
</li>
</template>
<template v-else>
<li
v-for="(item, index) in list"
:key="index"
:class="[{ active: selectNum === item.num },{'fold-item':(index===maxNum-1)||(index===2*maxNum-1)}]"
:data-num="item.num"
class="video-episode-li"
>
{{ item.num }}
<videoTypeIcon
:is-free-limit="item.isFreeLimit === 1"
:is-vip-saction="item.isVipSaction === 1"
/>
</li>
</template>
</ul>
</template>
<script>
export default {
name: 'EpisodeList',
props: {
//剧集数据
list: {
type: Array,
default: () => []
},
// 外面盒子的宽度
boxWith: {
type: Number,
default: 703
}
},
data() {
return {
episodeLen: 0,
selectNum: 1,
maxNum: 10, //每行最多渲染多少个,用Math.round(boxWidth/64),64=每个元素宽度52+marginRight12
}
},
computed:{
// 是否显示...,显示条件,是否可以容纳两行
isFold(){
return this.episodeLen > (this.maxNum*2)
},
leftNumArray(){
const arr = Array.from({length:this.maxNum}, (v,k) => k)
return arr.reverse()
}
},
watch: {
list: {
handler(newVal) {
this.episodeLen = newVal.length
},
immediate: true,
deep: true
}
},
created() {
this.maxNum = Math.round(this.boxWith / 64) || 10
},
methods: {
//事件代理
onClick(e) {
if (e.target.nodeName.toLowerCase() === 'li') {
// const searchVal = e.target.innerHTML
// console.log(e.target.innerHTML)
// console.log(e.target.dataset)
const { num } = e.target.dataset
this.selectNum = num * 1
this.$emit('onClick', this.selectNum)
}
}
}
}
</script>
<style lang="scss" scoped>
.episode-ul {
display: flex;
flex-wrap: wrap;
.video-episode-li {
width: 52px;
height: 52px;
border-radius: 6px;
background: #25252c;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 20px;
margin: 0 12px 12px 0;
position: relative;
cursor: pointer;
@include flex-center;
&.video-episode-more,&.fold-item {
margin-right: 0;
}
&.active{
color: $primary;
}
&:nth-child(n + 11) {
margin-bottom: 0;
}
&:hover{
color: $primary;
}
}
}
</style>
<episodeList
:list="mockData"
:box-with="videoIntroWidth"
class="video-episode-list"
/>
data(){
return:{
mockData:[],
videoIntroWidth :0
}
},
mounted() {
this.mockData = []
for (let a = 0; a < 34; a++) {
this.mockData.push({
isFreeLimit: 0,
isVipSaction: 1,
name: '皆大欢喜古装版' + a,
num: 1 + a,
tryWatchType: 1,
uuid: 'ff808081612b4d54016145eadfa5047c'
})
}
this.getVideoIntroWidth()
},
methods:{
getVideoIntroWidth() {
const videoIntroDom = document.getElementsByClassName('box')
if (videoIntroDom && videoIntroDom[0]) {
this.videoIntroWidth = videoIntroDom[0].clientWidth
}
}
}