父组件代码:
<template>
<view class="boxPage">
<scroll-view class="contentBox" :scroll-y="true" :scroll-top="scrollTopPage" :refresher-enabled="true"
:refresher-triggered="triggered" @refresherrefresh="freshList" @refresherpulling="onPulling"
@scrolltolower="scrolltolower">
<view class="productBox">
<!-- tab栏 -->
<u-sticky bgColor="#fff" :offset-top="-48">
<view class="tabbar">
<u-tabs :list="tabList" itemStyle="padding-left: 37rpx; padding-right: 37rpx; height: 88rpx;"
:activeStyle="{
fontWeight: 'bold',
fontFamily: 'PingFang SC',
color: '#333333',
transform: 'scale(1.08)'
}" :inactiveStyle="{
fontFamily: 'PingFang SC',
fontWeight: '500',
color: '#666666',
transform: 'scale(1)'
}" :lineColor="'#333333'" @change="changeTab"></u-tabs>
</view>
</u-sticky>
<!-- 商品列表 -->
<view class="content-report-box" v-if="total> 0">
<view class="leftBox">
<productList :productData="leftListData"></productList>
</view>
<view class="rightBox">
<productList :productData="rightListData"></productList>
</view>
</view>
<view class="noDataBox" v-if="total== 0&&loadStatus !== 'loading'">
<!-- 设置需要的无数据图片 -->
<image src="@/static/images/noData.png" class="img"></image>
<view class="noDataText">
暂无产品,敬请期待……
</view>
</view>
<uni-load-more v-else :status="loadStatus"></uni-load-more>
</view>
</scroll-view>
</view>
</template>
<script>
import {
queryProductList //后端接口方法
} from "@/api/api.js"
import productList from './components/productList.vue'
import uniLoadMore from "./components/uni-load-more.vue";
export default {
components: {
productList,uniLoadMore
},
data() {
return {
activeIndex: -1, // tab栏索引
tabList: [{ // tab栏列表 类型:-1全部
name: '全部',
value: -1
}
],
listData: [], // 商品列表
leftListData: [], // 左侧区域商品列表
rightListData: [], // 右侧区域商品列表
triggered: false,
page: 1,
total: 0,
scrollTopPage: 0, //回到顶部
loadStatus: "loading",
};
},
onShow() {
this.page = 1
this.loadStatus = "loading";
this.getProductList()
},
methods: {
scroll(e) {
this.scrollTopPage = e.target.scrollTop //滚动条实时位置
},
scrolltolower(e) {
if (e.detail.direction == "bottom") {
if (this.listData.length < this.total) {
this.loadStatus = "loading";
this.page++
this.getProductList()
} else {
this.loadStatus = "noMore";
}
}
},
onPulling() {
let that = this;
if (!this.triggered) {
//下拉加载,先让其变true再变false才能关闭
this.triggered = true;
//关闭加载状态 (转动的圈),需要一点延时才能关闭
setTimeout(() => {
that.triggered = false;
}, 1000)
}
},
// 下拉刷新
freshList() {
this.page = 1
this.loadStatus = "loading";
this.getProductList()
},
// 获取商城商品列表
getProductList() {
if (this.page == 1) {
this.leftListData = []
this.rightListData = []
this.listData = []
}
let data = {
"limit": 10,
"page": this.page,
"type": this.activeIndex
}
queryProductList(data).then(res => {
if (res.code == 200) {
this.total = res.data.total
let listData = res.data.data
if (listData.length) {
this.listData = this.listData.concat(listData)
listData.forEach((item, index) => {
if (index % 2 == 0) {
this.leftListData.push(item)
} else {
this.rightListData.push(item)
}
})
}
if (this.page == 1 && this.total == this.listData.length) {
this.loadStatus = "noMore";
}
}
})
},
// 切换商品列表tab
changeTab(index) {
this.scrollTopPage = 0 //回到顶部
this.page = 1
this.activeIndex = index.value
this.loadStatus = "loading";
this.getProductList()
},
}
}
</script>
<style lang="scss" scoped>
/deep/ .u-sticky {
border-radius: 8px 8px 0px 0px;
}
.boxPage {
background-color: #fff;
height: 100%;
position: relative;
.contentBox {
// overflow-y: auto;
height: calc(100% - 88rpx);
}
.productBox {
border-radius: 16rpx 16rpx 0rpx 0rpx;
padding: 0rpx 0rpx 100rpx;
z-index: 999;
background-color: #fff;
margin-top: -10rpx;
.tabbar {
margin: 0rpx 23rpx 28rpx;
}
}
.content-report-box {
display: flex;
flex-wrap: wrap;
padding: 0 20rpx;
.leftBox {
width: 50%;
box-sizing: border-box;
}
.rightBox {
width: 50%;
box-sizing: border-box;
}
}
.noDataBox {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.img {
width: 320rpx;
height: 268rpx;
}
.noDataText {
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
margin-top: 48rpx;
}
}
}
.uni-load-more{
position: relative;
background-color: #fff;
padding-bottom: 100rpx;
padding-top: 30rpx;
}
</style>
商品列表子组件:
<template>
<view>
<view class="content-report" v-for="(item,index) in productData" :key="index">
<image class="report-image" :src="item.coverAddress" width="302"></image>
<view class="report-detail">
<view class="detail-report">{{item.name}}</view>
<view class="detail-integral">
<view class="priceUnit">¥</view>
<view class="integral-total">{{item.price}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
productData: {
type: Array,
default: []
}
},
}
</script>
<style lang="scss" scoped>
.leftBox {
.content-report {
margin-bottom: 14rpx;
margin-right: 7rpx;
}
}
.rightBox {
.content-report {
margin-bottom: 14rpx;
margin-left: 7rpx;
}
}
.report-image {
width: 100%;
height: 302rpx;
display: block;
border-radius: 16rpx 16rpx 0rpx 0rpx;
background-size: 100% 100%;
}
.report-detail {
width: 100%;
padding-bottom: 31rpx;
border-radius: 0rpx 0rpx 16rpx 16rpx;
box-shadow: 0rpx 1rpx 20rpx 0rpx rgba(112, 117, 123, 0.1);
.detail-report {
padding: 22rpx 22rpx 0rpx;
font-size: 30rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
word-wrap: break-word; // 数字与汉字结合不换行展示
word-break: break-all;
-webkit-line-clamp: 2; //超出2行显示省略号
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.detail-integral {
display: flex;
align-items: baseline;
margin-left: 24rpx;
margin-bottom: 8rpx;
.integral-total {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: bold;
color: #EA3F49;
}
.priceUnit {
font-size: 20rpx;
font-family: PingFang SC;
font-weight: bold;
margin-right: 6rpx;
color: #E93A40;
}
}
}
</style>
加载更多子组件代码:
<template>
<view class="uni-load-more" v-if="status=='more'||status=='loading' || status=='noMore' || status=='contentText.contentrefresh'">
<view
v-if="iconType==='circle' || iconType==='auto' && platform === 'android'"
v-show="status === 'loading' && showIcon"
class="uni-load-more__img"
>
<view
:style="{borderColor : color}"
class="loader-android" />
</view>
<view
v-else
v-show="status === 'loading' && showIcon"
class="uni-load-more__img">
<view class="load1 load">
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
</view>
<view class="load2 load">
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
</view>
<view class="load3 load">
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
<view
:style="{ background: color }"
class="uni-load-view_wrapper" />
</view>
</view>
<text
:style="{ color: color }"
class="uni-load-more__text">
{{ status === 'more' ? contentText.contentdown : status === 'loading' ? contentText.contentrefresh : contentText.contentnomore }}
</text>
</view>
</template>
<script>
const platform = uni.getSystemInfoSync().platform
export default {
name: 'UniLoadMore',
props: {
status: {
// 上拉的状态:more-loading前;loading-loading中;noMore-没有更多了
type: String,
default: 'more'
},
showIcon: {
type: Boolean,
default: true
},
iconType: {
type: String,
default: 'auto'
},
color: {
type: String,
default: '#777777'
},
contentText: {
type: Object,
default () {
return {
contentdown: '上拉显示更多',
contentrefresh: '正在加载...',
contentnomore: '没有更多数据了'
}
}
}
},
data () {
return {
platform: platform
}
}
}
</script>
<style lang="scss">
.uni-load-more {
display: flex;
flex-direction: row;
height: 80upx;
align-items: center;
justify-content: center;
&__text {
font-size: 28upx;
color: $uni-text-color-grey;
}
&__img {
position: relative;
height: 24px;
width: 24px;
margin-right: 10px;
& > .load {
position: absolute;
.uni-load-view_wrapper {
width: 6px;
height: 2px;
border-top-left-radius: 1px;
border-bottom-left-radius: 1px;
background: $uni-text-color-grey;
position: absolute;
opacity: 0.2;
transform-origin: 50%;
animation: load 0.96s ease infinite;
&:nth-child(1) {
transform: rotate(90deg);
top: 2px;
left: 9px;
}
&:nth-child(2) {
transform: rotate(180deg);
top: 11px;
right: 0px;
}
&:nth-child(3) {
transform: rotate(270deg);
bottom: 2px;
left: 9px;
}
&:nth-child(4) {
top: 11px;
left: 0px;
}
}
}
& > .loader-android {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
box-sizing: border-box;
border: solid 2px #777777;
border-radius: 50%;
border-bottom-color: transparent !important;
animation: loader-android 1s 0s linear infinite;
}
}
}
.load1,
.load2,
.load3 {
height: 24px;
width: 24px;
}
.load2 {
transform: rotate(30deg);
}
.load3 {
transform: rotate(60deg);
}
.load1 .uni-load-view_wrapper:nth-child(1) {
animation-delay: 0s;
}
.load2 .uni-load-view_wrapper:nth-child(1) {
animation-delay: 0.08s;
}
.load3 .uni-load-view_wrapper:nth-child(1) {
animation-delay: 0.16s;
}
.load1 .uni-load-view_wrapper:nth-child(2) {
animation-delay: 0.24s;
}
.load2 .uni-load-view_wrapper:nth-child(2) {
animation-delay: 0.32s;
}
.load3 .uni-load-view_wrapper:nth-child(2) {
animation-delay: 0.40s;
}
.load1 .uni-load-view_wrapper:nth-child(3) {
animation-delay: 0.48s;
}
.load2 .uni-load-view_wrapper:nth-child(3) {
animation-delay: 0.56s;
}
.load3 .uni-load-view_wrapper:nth-child(3) {
animation-delay: 0.64s;
}
.load1 .uni-load-view_wrapper:nth-child(4) {
animation-delay: 0.72s;
}
.load2 .uni-load-view_wrapper:nth-child(4) {
animation-delay: 0.80s;
}
.load3 .uni-load-view_wrapper:nth-child(4) {
animation-delay: 0.88s;
}
@-webkit-keyframes load {
0% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
@-webkit-keyframes loader-android {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>