You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

370 lines
9.4 KiB

<template>
<view class="app" :class="{overhidden: list.length === 0}">
<!-- 头部 -->
<page-header
:keyword="keyword"
:listType="listType"
:sourcePage="sourcePage"
@delKeyword="delKeyword"
@changeListType="changeListType"
></page-header>
<!-- 顶部筛选 分类栏 -->
<view class="top" :style="{top: navigationBarHeight + statusBarHeight + 'px'}">
<!-- 排序 -->
<view class="sort-bar row">
<view class="item row center" :class="{active: item.current, last: index === sortList.length-1}" v-for="(item,index) in sortList" :key="index" @click="changeSort(item)">
<text>{{ item.name }}</text>
<view v-if="item.isPrice" class="icon-wrap">
<text class="mix-icon icon-down" :class="{active: item.type === 3}"></text>
<text class="mix-icon icon-arrow-top" :class="{active: item.type === 4}"></text>
</view>
</view>
<!-- #ifdef MP -->
<view class="btn center">
<text
class="mix-icon"
:class="listType === 'column' ? 'icon-hengxiangliebiao' : 'icon-shuxiangliebiao'"
@click="changeListType"
></text>
</view>
<!-- #endif -->
</view>
<!-- 商品分类 -->
<scroll-view v-if="curCateItem.parent_id" class="cate-bar b-b" scroll-x :scroll-left="curCateItem.left ? curCateItem.left: 0" scroll-with-animation >
<view class="cate-wrap row">
<view class="fill-view"></view>
<text :id="'cate-'+item._id" class="item" :class="{active: item._id === curCateItem._id}" v-for="item in cateList" :key="item._id" @click="changeCategory(item)">{{ item.name }}</text>
<view class="fill-view"></view>
</view>
</scroll-view>
</view>
<!-- 商品分类的占位栏 -->
<view v-if="curCateItem.parent_id" style="min-height: 94rpx"></view>
<!-- 产品列表 -->
<mescroll-body
ref="mescrollRef"
@init="mescrollInit"
:top="headerHeight"
@down="downCallback"
:up="upOption"
@up="loadList"
>
<product-list ref="productList" :list="list" :listType="listType"></product-list>
</mescroll-body>
<mix-loading v-if="isLoading"></mix-loading>
</view>
</template>
<script>
import pageHeader from './components/list-page-header'
import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
import productList from './components/product-list'
export default {
components: {
pageHeader,
MescrollBody,
productList
},
mixins: [MescrollMixin],
data() {
return {
sourcePage: '', //来源页 主要用来判断是否来自搜索页
listType: 'column', //列表类型 column竖向列表 row 横向列表
sortList: [
{name: '综合排序', type: 1, current: true},
{name: '销量', type: 2, current: false},
{name: '价格', type: 3, isPrice: true, current: false},
{name: '好评率', type: 5, current: false}
],
isHot: 0,//热门推荐
keyword: '',
cateList: [], //分类列表
curCateItem: {}, //当前分类
list: [], //商品列表
upOption:{
auto: false, // 不自动加载
page: {
num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
size: 10 // 每页数据的数量
},
noMoreSize: 4, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
},
}
},
computed: {
statusBarHeight(){
return this.systemInfo.statusBarHeight
},
navigationBarHeight(){
return this.systemInfo.navigationBarHeight;
},
headerHeight(){
return 750 / uni.upx2px(750) * (this.navigationBarHeight + this.statusBarHeight) + 96;
},
},
onLoad(options) {
this.isLoading = true;
this.sourcePage = options.sourcePage || '';
this.keyword = options.keyword || ''; //搜索关键字
this.prePage = options.prePage; //来源页 主要用于区分是否来自搜索页面
//分类
if(options.firstCateId){
this.curCateItem = {
_id: options.cateId || '',
parent_id: options.firstCateId
}
this.loadCateList();
}
//热门推荐
this.isHot = options.isHot || 0;
},
onReady() {
this.calcCateRect();
},
methods: {
//加载产品列表
async loadList(e){
this.mescroll.removeEmpty();
const {sortList, curCateItem, keyword} = this;
const data = {
sort_type: sortList.filter(item=> item.current)[0].type,
cate_id: curCateItem._id || '',
first_cate_id: curCateItem.parent_id || '',
keyword,
is_hot: this.isHot,
offset: (e.num - 1) * e.size,
limit: e.size,
}
this.$refs.productList.loadType = e.num === 1 ? 'refresh' : 'add';
const res = await this.$request('product', 'getList', data);
const curList = res.data;
if(e.num === 1){
//第一页清空数据重载
this.list = [];
this.loaded && curList.forEach(item=> {item.loaded = true;})
if(curList.length > 0){
uni.pageScrollTo({
scrollTop: 0,
duration: 200
})
}
}
this.list = this.list.concat(curList); //追加新数据
this.mescroll.endSuccess(curList.length); //结束加载状态
},
//加载分类列表
async loadCateList(){
const res = await this.$request('product', 'getSecondCategory', {
id: this.curCateItem.parent_id
}, {
changeLoaded: false
})
this.cateList = res.data || [];
this.$nextTick(()=>{
this.calcCateRect();
})
},
mescrollInit(mescroll){
this.mescroll = mescroll;
setTimeout(()=>{
this.refreshList();
}, 10)
},
//刷新列表
refreshList(){
this.mescroll.resetUpScroll(false)
this.isLoading = true;
},
//分类默认 处理分类滚动
calcCateRect(){
const tasks = [];
this.cateList.forEach(async item=> {
tasks.push(new Promise(resolve => {
uni.createSelectorQuery().select(`#cate-${item._id}`).boundingClientRect(data => {
item.left = data.left - (uni.upx2px(375) - data.width/2);
resolve();
}).exec()
}))
})
Promise.all(tasks).then(()=>{
let cur = this.cateList.filter(item=> item._id === this.curCateItem._id);
if(cur.length > 0){
this.curCateItem = cur[0];
}
})
},
//分类选择
changeCategory(item){
if(this.curCateItem._id === item._id){
return;
}
this.curCateItem = item;
this.refreshList();
},
//排序
changeSort(item){
if(item.current){
if(item.isPrice){
item.type = item.type === 3 ? 4 : 3;
}else{
return;
}
}else{
this.sortList.forEach(v=> v.current = false);
item.current = true;
if(item.type === 3 || item.type === 4){
item.type = 3;
}
}
this.refreshList();
},
//删除搜索关键字
delKeyword(){
this.keyword = '';
this.refreshList();
},
//修改列表显示方式
changeListType(){
this.listType = this.listType === 'column' ? 'row' : 'column';
}
}
}
</script>
<style>
page{
background-color: #f8f8f8;
}
</style>
<style scoped lang="scss">
.app{
/* #ifndef MP */
&.overhidden{
height: 100vh;
overflow: hidden;
}
/* #endif */
}
/deep/ .mix-empty .default{
padding-top: 38vh;
/* #ifdef H5 */
padding-top: 34vh;
/* #endif */
}
.top{
position: fixed;
left: 0;
width: 100%;
z-index: 95;
background-color: #fff;
}
.sort-bar{
justify-content: space-around;
height: 76rpx;
padding: 4rpx 0 4rpx 4rpx;
/* #ifdef MP */
padding-left: 10rpx;
/* #endif */
background-color: #fff;
position: relative;
z-index: 1;
.item{
flex: 1;
height: 100%;
font-size: 28rpx;
color: #333;
position: relative;
overflow: hidden;
&.active{
color: $base-color;
font-weight: 700;
&:after{
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-28rpx);
content: '';
width: 56rpx;
height: 4rpx;
background-color: $base-color;
border-radius: 10px;
}
.mix-icon.active{
color: $base-color;
}
}
/* #ifdef MP */
&.last:before{
content: '';
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 2rpx;
height: 40rpx;
box-shadow: 0 0 16rpx rgba(0,0,0,.6);
}
/* #endif */
}
.icon-wrap{
display: flex;
flex-direction: column;
padding-left: 8rpx;
}
.mix-icon{
font-size: 14rpx;
color: #bbb;
}
.btn{
height: 68rpx;
padding-left: 16rpx;
padding-right: 20rpx;
.icon-hengxiangliebiao, .icon-shuxiangliebiao{
font-size: 40rpx;
color: #333;
}
}
}
.cate-bar:after{
border-color: #f5f5f5;
}
.cate-wrap{
flex-wrap: nowrap;
height: 96rpx;
padding-bottom: 4rpx;
.fill-view{
flex-shrink: 0;
width: 20rpx;
height: 20rpx;
&:last-child{
width: 10rpx;
}
}
.item{
flex-shrink: 0;
height: 58rpx;
padding: 0 28rpx;
margin-right: 20rpx;
font-size: 22rpx;
color: #333;
text-align: center;
line-height: 58rpx;
background-color: #f5f5f5;
border-radius: 100px;
}
.active{
color: $base-color;
background-color: #fff8f4;
font-weight: 700;
}
}
</style>