361 lines
12 KiB
Vue
361 lines
12 KiB
Vue
<template>
|
|
<view class="zh-wrapper">
|
|
<scroll-view class="menus" :scroll-into-view="menuScrollIntoView" scroll-with-animation scroll-y>
|
|
<view class="wrapper">
|
|
<view class="menu" :id="`menu-${item.id}`" :class="{ 'current': item.id == curCateId }"
|
|
v-for="(item, index) in goods" :key="index" @click="handleMenuTap(item.id)">
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
<scroll-view class="goods" scroll-with-animation scroll-y :scroll-top="cateScrollTop"
|
|
@scroll="handleGoodsScroll">
|
|
<view class="wrapper">
|
|
<view class="list">
|
|
<!-- category begin -->
|
|
<view class="category" v-for="(item, index) in goods" :key="index" :id="`cate-${item.id}`">
|
|
<slot name="custom" :data="item">
|
|
<view class="title">
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
<view class="items">
|
|
<!-- 商品 begin -->
|
|
<view class="good" v-for="(good, key) in item.goods_list" :key="key">
|
|
<view class="right">
|
|
<view class="tips">
|
|
<text @click="clickTips(tips)" class="tips_item"
|
|
:class="{ 'tips_current': handerTips(tips) }"
|
|
v-for="(tips, k) in good.tips" :key="k">
|
|
{{ tips }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<!-- 商品 end -->
|
|
</view>
|
|
</slot>
|
|
</view>
|
|
|
|
<!-- category end -->
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, nextTick, getCurrentInstance, watch, defineEmits } from "vue";
|
|
const props = defineProps({
|
|
scrollList: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
})
|
|
const instance = getCurrentInstance();
|
|
const menuScrollIntoView = ref("");
|
|
const curCateId = ref(6905);
|
|
const cateScrollTop = ref(0);
|
|
// 计算高度状态
|
|
const sizeCalcState = ref(false);
|
|
const goods = ref([]);
|
|
//选中的数组
|
|
const curCate = ref([])
|
|
const emit = defineEmits(['clickTips'])
|
|
//tips选中
|
|
const clickTips = (id) => {
|
|
//判断id是否在里面
|
|
let index = curCate.value.indexOf(id)
|
|
if (index > -1) {
|
|
//删除
|
|
curCate.value.splice(index, 1)
|
|
} else {
|
|
//添加
|
|
curCate.value.push(id)
|
|
}
|
|
emit('clickTips', curCate)
|
|
}
|
|
//判断是否有选中的tips
|
|
const handerTips = (id) => {
|
|
//判断id是否在里面
|
|
return curCate.value.indexOf(id) > -1
|
|
}
|
|
//重置选择
|
|
const resetTips = () => {
|
|
curCate.value = []
|
|
}
|
|
//暴露方法
|
|
defineExpose({
|
|
resetTips
|
|
})
|
|
//监听数据变化
|
|
watch(
|
|
() => props.scrollList,
|
|
newVal => {
|
|
goods.value = newVal;
|
|
nextTick(() => {
|
|
if (newVal && newVal.length > 0) {
|
|
calcSize();
|
|
}
|
|
})
|
|
},
|
|
{
|
|
immediate: true,
|
|
deep: true
|
|
}
|
|
)
|
|
|
|
// 点击左侧菜单
|
|
function handleMenuTap(id) {
|
|
if (!sizeCalcState.value) {
|
|
calcSize()
|
|
}
|
|
curCateId.value = id
|
|
nextTick(() => {
|
|
cateScrollTop.value = goods.value.find(item => item.id == id).top
|
|
})
|
|
}
|
|
|
|
// 商品滚动
|
|
function handleGoodsScroll({ detail }) {
|
|
if (!sizeCalcState.value) {
|
|
calcSize()
|
|
}
|
|
const { scrollTop } = detail
|
|
// 此处scrollTop + 1为了处理scrolltop的偏差值
|
|
let tabs = goods.value.filter(item => item.top <= (scrollTop + 1)).reverse()
|
|
if (tabs.length > 0) {
|
|
curCateId.value = tabs[0].id
|
|
}
|
|
}
|
|
|
|
function calcSize() {
|
|
let h = 10
|
|
|
|
goods.value.forEach(item => {
|
|
let view = uni.createSelectorQuery().in(instance).select(`#cate-${item.id}`)
|
|
view.fields({
|
|
size: true
|
|
}, (data) => {
|
|
item.top = h
|
|
h += data.height
|
|
item.bottom = h
|
|
}).exec()
|
|
})
|
|
sizeCalcState.value = true
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
$text-color-assist: #919293; // 辅助色
|
|
$text-color-base: #5A5B5C; // 基础色
|
|
$choose: #FF9B27;
|
|
|
|
page {
|
|
height: 100%;
|
|
}
|
|
|
|
.zh-wrapper {
|
|
height: 100%;
|
|
overflow: hidden;
|
|
width: 100%;
|
|
display: flex;
|
|
|
|
.menus {
|
|
width: 200rpx;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
background-color: $uni-bg-color-grey;
|
|
|
|
.wrapper {
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
.menu {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
padding: 30rpx 20rpx;
|
|
font-size: $uni-font-size-base;
|
|
color: $uni-text-color;
|
|
position: relative;
|
|
|
|
&:nth-last-child(1) {
|
|
margin-bottom: 130rpx;
|
|
}
|
|
|
|
&.current {
|
|
background-color: $uni-bg-color;
|
|
color: $choose;
|
|
}
|
|
|
|
.dot {
|
|
position: absolute;
|
|
width: 34rpx;
|
|
height: 34rpx;
|
|
line-height: 34rpx;
|
|
font-size: $uni-font-size-sm;
|
|
background-color: $uni-color-primary;
|
|
color: $uni-bg-color;
|
|
top: 16rpx;
|
|
right: 10rpx;
|
|
border-radius: 100%;
|
|
text-align: center;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.goods {
|
|
flex: 1;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
background-color: $uni-bg-color;
|
|
|
|
.wrapper {
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 20rpx;
|
|
|
|
.list {
|
|
width: 100%;
|
|
font-size: $uni-font-size-base;
|
|
padding-bottom: 130rpx;
|
|
|
|
:deep(.category) {
|
|
width: 100%;
|
|
|
|
.title {
|
|
padding: 30rpx 0;
|
|
display: flex;
|
|
align-items: center;
|
|
color: $text-color-base;
|
|
|
|
.icon {
|
|
width: 38rpx;
|
|
height: 38rpx;
|
|
margin-left: 10rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
:deep(.items) {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding-bottom: -30rpx;
|
|
|
|
.good {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 30rpx;
|
|
|
|
.right {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
padding-right: 14rpx;
|
|
|
|
.name {
|
|
font-size: $uni-font-size-base;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.tips {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
|
|
&_item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 172rpx;
|
|
height: 74rpx;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-size: $uni-font-size-base;
|
|
color: $uni-text-color;
|
|
background: $uni-bg-color-grey;
|
|
margin: 10rpx;
|
|
border-radius: 12rpx;
|
|
|
|
&.tips_current {
|
|
color: $choose;
|
|
background: rgba(248, 209, 164, 0.2);
|
|
box-sizing: border-box;
|
|
border: 2rpx solid #FF9B27;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
.price_and_action {
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
|
|
.price {
|
|
font-size: $uni-font-size-base;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.btn-group {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
position: relative;
|
|
|
|
.btn {
|
|
padding: 0 20rpx;
|
|
box-sizing: border-box;
|
|
font-size: $uni-font-size-sm;
|
|
height: 44rpx;
|
|
line-height: 44rpx;
|
|
|
|
&.property_btn {
|
|
border-radius: 24rpx;
|
|
}
|
|
|
|
&.add_btn,
|
|
&.reduce_btn {
|
|
padding: 0;
|
|
width: 44rpx;
|
|
border-radius: 44rpx;
|
|
}
|
|
}
|
|
|
|
.dot {
|
|
position: absolute;
|
|
background-color: $uni-bg-color;
|
|
border: 1px solid $uni-color-primary;
|
|
color: $uni-color-primary;
|
|
font-size: $uni-font-size-sm;
|
|
width: 36rpx;
|
|
height: 36rpx;
|
|
line-height: 36rpx;
|
|
text-align: center;
|
|
border-radius: 100%;
|
|
right: -12rpx;
|
|
top: -10rpx;
|
|
}
|
|
|
|
.number {
|
|
width: 44rpx;
|
|
height: 44rpx;
|
|
line-height: 44rpx;
|
|
text-align: center;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style> |