<template>
<view class="class">
<!-- 一级分类 -->
<scroll-view :scroll-top="maxTitle.scrollTop" scroll-y scroll-with-animation class="class-left no-scrollbar">
<view @click="maxTitleTab(item.id, index)" v-for="(item, index) in maxTitle.list" :key="index" :class="{ checked: maxTitle.current === index }" class="item">
<view class="line-overflow">{{ item.name }}</view>
</view>
</scroll-view>
<view class="class-right">
<!-- 二级分类 -->
<scroll-view :scroll-left="minTitle.scrollLeft" scroll-x scroll-with-animation style="width: 100%" class="no-scrollbar">
<view class="class-nav">
<view @click="minTitleTab(item.id, index)" v-for="(item, index) in minTitle.list" :key="index" :class="{ checked: minTitle.current === index }" class="min-title-item item">{{
item.name
}}</view>
</view>
</scroll-view>
<scroll-view @scroll="onScroll" :scroll-top="listModule.scrollTop" scroll-with-animation scroll-y enable-back-to-top class="content-scroll">
<block v-for="(item, index) in listModule.list" :key="index">
<view v-if="item.products.length" class="list-modele class-model">
<view class="class-name">{{ item.cname }}</view>
<view class="class-content">
<view @click="toDetail(e.productId)" v-for="(e, i) in item.products" :key="i" class="product-item">
<view class="logo">
<image :src="e.littleImage" lazy-load mode="aspectFill" />
</view>
<view class="content">
<view class="product-name line-overflow">{{ e.onlyName }}</view>
<view class="tip-view">
<view v-for="(tag, a) in e.tags" :key="a" class="tip">{{ tag }}</view>
</view>
<view class="remain">销量 {{ e.saleAmount || 0 }} · 库存 {{ e.stockAmount || 0 }}</view>
<view class="sale-money">
<text class="money">{{ e.salePriceYuan || '0.00' }}</text>
<text class="old-money" style="font-size: 13px">¥{{ e.originPriceYuan || '0.00' }}</text>
<!-- <text v-if="e.agentMoney" class="return-money">{{ e.agentMoney }}</text> -->
</view>
</view>
<text v-if="e.isSoldOut === 1" class="status">已售罄</text>
<view v-else @click.stop="joinCart(`#join-cart${index}-${i}`, e, index, i)" class="join-cart" :id="`join-cart${index}-${i}`">
<image mode="widthFix" src="/static/images/home/join-cart.png"></image>
<view v-if="joinCartLoading[`${index}-${i}`]" @click.stop class="cart-loading"></view>
</view>
</view>
</view>
</view>
</block>
</scroll-view>
</view>
<!-- 购物车动画 -->
<cart-animate ref="cartAnimate"></cart-animate>
</view>
</template>
<script>
import cartAnimate from '@/component/cartAnimate.vue'
import { setCartNum, getProductCategories, getProductPageCategory, cartAdd } from '@/service/consumer'
export default {
name: 'class',
components: {
cartAnimate
},
data() {
return {
httpErr: false,
maxTitle: {
list: [],
scrollTop: 0,
current: 0
}, // 一级分类
minTitle: {
list: [], // 数据
current: 0, // 选中索引
domtArr: [], // dom信息
scrollLeft: 0
}, // 二级标题
listModule: {
list: [],
heightArr: [],
scrollTop: 0
}, // 分类数据
iscartAnimate: true, // 允许加入购物车
joinCartLoading: {} // 购物车加载
}
},
onLoad() {
this.getClassTitle()
},
onShow() {
if (this.httpErr) this.getClassTitle() // onLoad 错误继续请求
setCartNum() // 设置购物车数量
},
methods: {
// 竖向滚动
onScroll(e) {
const scrollTop = e.detail.scrollTop
let minTitleCurrent = 0
this.listModule.heightArr.forEach((item, index) => {
if (parseInt(scrollTop) >= parseInt(item)) minTitleCurrent = index
})
this.minTitle.current = minTitleCurrent
this.minTitleScroll(minTitleCurrent) // 二级分类滚动
},
// 去详情
toDetail(productId = '') {
uni.navigateTo({
url: `/pages/consumer/home/detail?productId=${productId}`
})
},
// 二级菜单点击
minTitleTab(id, index) {
this.minTitle.current = index
this.minTitleScroll(index) // 二级分类滚动
const scrollTop = this.listModule.scrollTop
let curScrollTop = this.listModule.heightArr[index]
scrollTop === curScrollTop && curScrollTop++ // 解决小程序兼容性
this.listModule.scrollTop = curScrollTop
},
// 一级菜单点击
maxTitleTab(id, index) {
this.minTitle.list = this.maxTitle.list[index].sons || []
this.maxTitle.current = index
this.getListModule(id)
this.minTitle.current = 0 // 二级分类重置
this.minTitle.scrollLeft = 0 // 二级分类滚动位置重置
this.listModule.scrollTop = 0 // 竖向滚动重置
this.getMinTitleItemDom() // 获取二级标题dom信息集合
if (index >= 2) {
this.maxTitle.scrollTop = (index - 1) * uni.upx2px(126)
} else {
this.maxTitle.scrollTop = 0
}
},
// 加入购物车
joinCart(joinCartDom, row = {}, index = 0, i = 0) {
if (!this.iscartAnimate)
return uni.showToast({
title: '别着急,慢慢来',
duration: 1000,
icon: 'none'
})
this.$set(this.joinCartLoading, `${index}-${i}`, true) // 当前加入loading显示
this.iscartAnimate = false
const { agentId, productId, productItemId } = row
const body = {
agentId,
productId,
productItemId,
quantity: 1
}
cartAdd(body)
.then(() => {
this.$refs.cartAnimate.joinCart(joinCartDom) // 执行购物车动画
})
.finally(() => {
this.iscartAnimate = true
this.$set(this.joinCartLoading, `${index}-${i}`, false) // 当前加入loading关闭
setCartNum() // 设置购物车数量
})
},
// 获取分类列表
getListModule(id = '') {
this.listModule.list = []
getProductPageCategory({ concurrentId: id }).then((res = {}) => {
const { records = [] } = res
this.listModule.list = records
// 计算高度
this.$nextTick(() => {
uni
.createSelectorQuery()
.selectAll('.list-modele')
.boundingClientRect((rect) => {
let height = 0
const domArr = []
rect.forEach((item) => {
domArr.push(height)
height += item.height
})
this.listModule.heightArr = domArr
console.log(this.listModule.heightArr, 'listModule')
})
.exec()
})
})
},
// 获取分类
getClassTitle() {
getProductCategories()
.then((res = {}) => {
const { records = [] } = res
this.maxTitle.list = records
console.log(this.maxTitle)
// 默认列表渲染
if (records.length) {
this.minTitle.list = records[0].sons || []
this.getListModule(records[0].id)
}
})
.catch(() => {
this.httpErr = true
})
},
// 二级标题滚动
minTitleScroll(index = 0) {
if (index >= 2) {
const domtArr = this.minTitle.domtArr
const uselessLeft = domtArr[0].left // 无用的left
this.minTitle.scrollLeft = domtArr[index].left - uselessLeft - (domtArr[index - 1].width + uni.upx2px(20))
console.log('scrollLeft', this.minTitle.scrollLeft)
} else {
this.minTitle.scrollLeft = 0
}
},
// 获取二级标题dom信息集合
getMinTitleItemDom() {
this.$nextTick(() => {
uni
.createSelectorQuery()
.selectAll('.min-title-item')
.boundingClientRect((rect) => {
console.log('获取二级标题dom信息集合', rect)
this.minTitle.domtArr = rect
})
.exec()
})
}
}
}
</script>
<style lang="scss" scoped>
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.class {
height: 100%;
display: flex;
&-left {
margin-top: 20rpx;
width: 170rpx;
height: calc(100% - 20rpx);
background: hsl(0, 0%, 96%);
.item {
position: relative;
width: 100%;
height: 126rpx;
padding: 0 15rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 26rpx;
transition: background-color 0.3s;
&::after {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 4rpx;
height: 0;
background: $main-color;
transform: translateY(-50%);
transition: height 0.3s;
}
}
.checked {
background: #fff;
&::after {
// content: '';
height: 50rpx;
}
}
}
&-right {
flex: 1;
height: 100%;
padding-top: 20rpx;
padding-left: 30rpx;
box-sizing: border-box;
overflow: hidden;
.class-nav {
padding-bottom: 30rpx;
display: inline-block;
white-space: nowrap;
.item {
margin-right: 20rpx;
min-width: 80rpx;
height: 50rpx;
border-radius: 5rpx;
background: #f4f4f4;
padding: 0 20rpx;
display: inline-block;
text-align: center;
line-height: 50rpx;
font-weight: 500;
font-size: 26rpx;
// transition: background-color 0.2s;
}
.checked {
background: $main-color;
color: #fff;
}
}
.content-scroll {
height: calc(100% - 80rpx);
}
.class-model {
padding-bottom: 30rpx;
.class-name {
padding: 20rpx 0 0 0;
font-weight: bold;
font-size: 24rpx;
}
.class-content {
padding-right: 30rpx;
.product-item {
position: relative;
border-bottom: solid 1rpx #f2f2f2;
padding: 50rpx 0;
display: flex;
.logo {
margin-right: 30rpx;
flex-shrink: 0;
width: 150rpx;
height: 150rpx;
background: #f4f4f4;
image {
width: 100%;
height: 100%;
}
}
.content {
width: calc(100% - 180rpx);
.product-name {
height: 46rpx;
box-sizing: border-box;
width: 100%;
line-height: 1.5;
font-weight: 500;
font-size: 26rpx;
}
.tip-view {
height: 30rpx;
display: flex;
align-items: center;
.tip {
margin-right: 10rpx;
border-radius: 2rpx;
background: rgba(242, 109, 48, 0.1);
padding: 0 15rpx;
line-height: 30rpx;
font-weight: bold;
font-size: 23rpx;
color: #f26d30;
}
}
.remain {
flex-shrink: 0;
height: 32rpx;
padding-top: 15rpx;
box-sizing: border-box;
line-height: 1;
font-size: 23rpx;
color: #ccc;
}
.sale-money {
padding-top: 6rpx;
.money {
position: relative;
top: 2px;
}
.old-money {
position: relative;
top: 1px;
padding-left: 15rpx;
display: inline-block;
font-size: 12px;
color: #ccc;
text-decoration: line-through;
}
.return-money {
position: relative;
top: -6rpx;
margin-left: 15rpx;
background: #f3b446;
padding: 0 15rpx;
display: inline-block;
line-height: 30rpx;
font-weight: bold;
font-size: 12px;
color: #fff;
}
}
}
.status {
position: absolute;
right: 0;
bottom: 50rpx;
font-size: 13px;
color: #ccc;
}
.join-cart {
position: absolute;
right: -20rpx;
bottom: 26rpx;
padding: 27rpx 28rpx;
image {
width: 45rpx;
height: 45rpx;
}
.cart-loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.8);
&::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 36rpx;
height: 36rpx;
border-radius: 50%;
border-bottom: solid 2rpx $main-color;
box-sizing: border-box;
animation: turn 1s linear infinite;
}
}
}
}
}
}
}
}
</style>
网友评论