278 lines
7.1 KiB
Vue
278 lines
7.1 KiB
Vue
<template>
|
||
<view>
|
||
<view class="type-list flex ai-center">
|
||
<view class="item" :class="{'active':type===item.value}" v-for="(item,index) in typeList" :key="index"
|
||
@click="changeType(item)">{{ item.label }}
|
||
</view>
|
||
</view>
|
||
|
||
<view class="list" v-show="hasList">
|
||
<view class="video-waterfalls-wrap">
|
||
<view class="waterfalls-column">
|
||
<view class="item" v-for="(lItem,lIndex) in leftList" :key="lIndex" @click="goDetail(lItem)">
|
||
<image class="cover" :src="lItem.cover" mode="widthFix" lazy-load></image>
|
||
<view class="content">
|
||
<view class="title more-t">{{ lItem.title }}</view>
|
||
<view class="footer flex jc-between ai-center">
|
||
<view class="visit flex ai-center">
|
||
<image class="icon" style="margin-right: 8rpx" :src="lItem.storeLogo" mode="scaleToFill"></image>
|
||
{{ lItem.storeName }}
|
||
</view>
|
||
<image class="icon flex-0" :src="webUrl+'/20210806121203835373.png'" mode="scaleToFill"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="waterfalls-column">
|
||
<view class="item" v-for="(rItem,rIndex) in rightList" :key="rIndex" @click="goDetail(rItem)">
|
||
<image class="cover" :src="rItem.cover" mode="widthFix" lazy-load></image>
|
||
<view class="content">
|
||
<view class="title more-t">{{ rItem.title }}</view>
|
||
<view class="footer flex jc-between ai-center">
|
||
<view class="visit flex ai-center">
|
||
<image class="icon" style="margin-right: 8rpx" :src="rItem.storeLogo" mode="scaleToFill"></image>
|
||
{{ rItem.storeName }}
|
||
</view>
|
||
<image class="icon flex-0" :src="webUrl+'/20210806121203835373.png'" mode="scaleToFill"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="loading-more" v-show="isWait">加载中...</view>
|
||
|
||
<view class="v4-nodata" v-show="showEmpty">
|
||
<image src="@/static/images/img_nodata.png" mode="scaleToFill"/>
|
||
<view class="text">暂无数据</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {getStoreVideoType, getArticleList} from '@/api/public'
|
||
import {getVideoList} from '@/api/user'
|
||
import { pageListenMixins } from '@/mixins/pageListenMixins'
|
||
|
||
// 封面固定宽度(rpx)
|
||
const COVER_WIDTH = 332
|
||
// 每项除封面外的文字内容预估高度(rpx)
|
||
const TEXT_HEIGHT = 120
|
||
|
||
export default {
|
||
mixins: [pageListenMixins],
|
||
data() {
|
||
return {
|
||
webUrl: this.$VUE_APP_RESOURCES_URL,
|
||
typeList: [{'label': '全部', 'value': ''}],
|
||
type: '',
|
||
page: 1,
|
||
limit: 10,
|
||
keyword: '',
|
||
leftList: [],
|
||
rightList: [],
|
||
leftHeight: 0,
|
||
rightHeight: 0,
|
||
isWait: false,
|
||
requestId: 0,
|
||
pageKeyId: Object.freeze('findVideoList')
|
||
}
|
||
},
|
||
computed: {
|
||
hasList() {
|
||
return this.leftList.length + this.rightList.length > 0
|
||
},
|
||
showEmpty() {
|
||
return !this.isWait && !this.hasList
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
if (!options.click) {
|
||
uni.switchTab({ url: '/pages/home/index' })
|
||
return
|
||
}
|
||
this.getType()
|
||
this.fetchList()
|
||
},
|
||
onReachBottom() {
|
||
this.page++
|
||
this.fetchList()
|
||
},
|
||
methods: {
|
||
getType() {
|
||
getStoreVideoType().then(({data}) => {
|
||
if (data.length > 0) {
|
||
data.forEach(item => {
|
||
this.typeList.push(item)
|
||
})
|
||
}
|
||
})
|
||
},
|
||
changeType(item) {
|
||
this.type = item.value
|
||
this.page = 1
|
||
// 使进行中的请求失效并解锁,避免切换时被上一次请求阻塞
|
||
this.requestId++
|
||
this.isWait = false
|
||
this.fetchList()
|
||
},
|
||
resetList() {
|
||
this.leftList = []
|
||
this.rightList = []
|
||
this.leftHeight = 0
|
||
this.rightHeight = 0
|
||
},
|
||
async fetchList() {
|
||
if (this.isWait) return
|
||
const requestId = ++this.requestId
|
||
this.isWait = true
|
||
const params = {
|
||
type: this.type,
|
||
page: this.page,
|
||
limit: this.limit
|
||
}
|
||
try {
|
||
const res = await getVideoList(params)
|
||
// 请求已被更新的请求(如切换分类)取代,丢弃本次结果
|
||
if (requestId !== this.requestId) return
|
||
if (res.status === 200) {
|
||
const items = res.data
|
||
for (let i = 0; i < items.length; i++) {
|
||
items[i].hide = true
|
||
}
|
||
// 并行获取所有封面高度,完成后一次性替换列表,避免闪现空列表
|
||
const heights = await Promise.all(items.map(item => this.getImageHeight(item.cover)))
|
||
if (requestId !== this.requestId) return
|
||
if (this.page === 1) this.resetList()
|
||
items.forEach((item, i) => {
|
||
this.pushToColumn(item, heights[i] + TEXT_HEIGHT)
|
||
})
|
||
}
|
||
} catch (e) {
|
||
// 忽略请求或图片尺寸获取失败
|
||
} finally {
|
||
if (requestId === this.requestId) {
|
||
this.isWait = false
|
||
}
|
||
}
|
||
},
|
||
getImageHeight(src) {
|
||
return new Promise(resolve => {
|
||
uni.getImageInfo({
|
||
src,
|
||
success: res => {
|
||
resolve(res.height / res.width * COVER_WIDTH)
|
||
},
|
||
fail: () => {
|
||
resolve(COVER_WIDTH)
|
||
}
|
||
})
|
||
})
|
||
},
|
||
pushToColumn(item, height) {
|
||
if (this.leftHeight <= this.rightHeight) {
|
||
this.leftList.push(item)
|
||
this.leftHeight += height
|
||
} else {
|
||
this.rightList.push(item)
|
||
this.rightHeight += height
|
||
}
|
||
},
|
||
goDetail(item) {
|
||
this.$yrouter.push("/pages/VideoPlayBackList/VideoPlayBackDetail?videoUrl=" + item.video + '&id=' + item.id + '&click=1')
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="less">
|
||
.type-list {
|
||
margin: 28rpx 32rpx;
|
||
|
||
.item {
|
||
margin-right: 20rpx;
|
||
padding: 12rpx 16rpx;
|
||
border-radius: 20rpx;
|
||
background: #fff;
|
||
font-size: 26rpx;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.active {
|
||
background: #f66;
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.list {
|
||
margin: 28rpx 32rpx;
|
||
|
||
.video-waterfalls-wrap {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.waterfalls-column {
|
||
width: 332rpx;
|
||
}
|
||
|
||
.item {
|
||
margin-bottom: 20rpx;
|
||
border-radius: 4rpx;
|
||
background: #fff;
|
||
|
||
.cover {
|
||
width: 332rpx;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.title {
|
||
padding: 12rpx 16rpx;
|
||
font-size: 26rpx;
|
||
font-weight: bold;
|
||
line-height: 38rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.mark {
|
||
margin: 14rpx 16rpx;
|
||
font-size: 22rpx;
|
||
line-height: 32rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.footer {
|
||
padding: 12rpx 16rpx 16rpx;
|
||
border-top: 1rpx solid #E1E1E1;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
vertical-align: middle;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.visit {
|
||
width: 100%;
|
||
color: #333;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.time {
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
.loading-more {
|
||
padding: 24rpx 0;
|
||
text-align: center;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
</style>
|