270 lines
7.8 KiB
Vue
270 lines
7.8 KiB
Vue
<template>
|
||
<view class="component-cate" :style="{ height: wrapHeight + 'px' }">
|
||
<view class="menu-wrap">
|
||
<scroll-view scroll-y class="tab-view menu-scroll-view" :scroll-top="scrollLeftTop" :enhanced="true" :bounces="false">
|
||
<view
|
||
class="tab-item"
|
||
v-for="(item, index) in data"
|
||
:key="index"
|
||
:class="[currentLeftIndex == index ? 'tab-item-active' : '']"
|
||
@tap.stop="switchMenu(index)"
|
||
>
|
||
<text class="line-1">{{ item.name }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
<scroll-view
|
||
:scroll-top="scrollRightTop"
|
||
scroll-y
|
||
class="right-box"
|
||
@scroll="rightScroll"
|
||
:enhanced="true"
|
||
:bounces="false"
|
||
>
|
||
<view class="page-view" :class="[cateType]">
|
||
<view
|
||
class="class-item"
|
||
v-for="(item, index) in data"
|
||
:key="index"
|
||
:style="{ height: index == data.length - 1 ? wrapHeight + 'px' : 'auto' }"
|
||
>
|
||
<cate-tmpl v-if="cateType === 'cate'" :parentId="item.id" :data="item" @clickItem="clickItem"></cate-tmpl>
|
||
<list-tmpl
|
||
v-if="cateType === 'list'"
|
||
:parentId="item.id"
|
||
:data="item"
|
||
@clickItem="clickItem"
|
||
@changeNumber="changeNumber"
|
||
></list-tmpl>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import CateTmpl from "@/components/cate/template/cate";
|
||
import ListTmpl from "@/components/cate/template/list";
|
||
export default {
|
||
name: "component-cate",
|
||
data() {
|
||
return {
|
||
utils: this.$utils,
|
||
isReady: false,
|
||
|
||
leftNodePos: [],
|
||
rightNodePos: [],
|
||
queryTimer: null,
|
||
currentLeftIndex: 0,
|
||
wrapHeight: 0,
|
||
scrollLeftTop: 0,
|
||
scrollRightTop: 0,
|
||
menuHeight: 0,
|
||
};
|
||
},
|
||
props: {
|
||
cateType: {
|
||
type: String,
|
||
default: "cate",
|
||
},
|
||
offsetHeight: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
activeId: {
|
||
type: [String, Number],
|
||
default: 0,
|
||
},
|
||
data: {
|
||
type: Array,
|
||
default: [],
|
||
},
|
||
},
|
||
components: {
|
||
CateTmpl,
|
||
ListTmpl,
|
||
},
|
||
created() {},
|
||
mounted() {
|
||
const { windowHeight, statusBarHeight, headerHeight } = getApp().globalData.pageConfig;
|
||
// 计算页面内边距------------------------------------------
|
||
// #ifndef H5
|
||
const bodyPt = statusBarHeight + headerHeight;
|
||
// #endif
|
||
// #ifdef H5
|
||
const bodyPt = 0 + this.$utils.rpx2px(headerHeight * 2);
|
||
// #endif
|
||
// 计算页面内边距------------------------------------------
|
||
const offsetHeight = this.$utils.rpx2px(this.offsetHeight);
|
||
// 窗口高度 - 头部高度 - 顶部内容高度 = 分类容器高度
|
||
this.wrapHeight = windowHeight - bodyPt - offsetHeight;
|
||
// #ifdef H5
|
||
// H5窗口高度会计算tabbar高度,这里减去
|
||
this.wrapHeight -= this.$utils.rpx2px(100);
|
||
// #endif
|
||
|
||
this.menuHeight = this.$utils.rpx2px(100);
|
||
},
|
||
destroyed() {},
|
||
watch: {
|
||
/**
|
||
* 监听列表数据变化
|
||
*/
|
||
data() {
|
||
this.init().then(() => {
|
||
this.isReady = true;
|
||
this.reAction();
|
||
});
|
||
},
|
||
/**
|
||
* 监听ID变化
|
||
*/
|
||
activeId() {
|
||
if (this.isReady) {
|
||
this.reAction();
|
||
}
|
||
},
|
||
},
|
||
methods: {
|
||
/**
|
||
* 执行具体逻辑
|
||
*/
|
||
reAction() {
|
||
this.switchCate(this.activeId);
|
||
},
|
||
/**
|
||
* 初始化列表
|
||
*/
|
||
init() {
|
||
clearTimeout(this.queryTimer);
|
||
return new Promise((resolve, reject) => {
|
||
const that = this;
|
||
this.queryTimer = setTimeout(() => {
|
||
that.rightNodePos = [];
|
||
let query = uni.createSelectorQuery().in(that);
|
||
query
|
||
.selectAll(".class-item")
|
||
.boundingClientRect((result) => {
|
||
if (result.length > 0) {
|
||
result.forEach((item) => {
|
||
that.rightNodePos.push(item.top - result[0].top);
|
||
});
|
||
resolve();
|
||
}
|
||
})
|
||
.exec();
|
||
}, 200);
|
||
});
|
||
},
|
||
/**
|
||
* 更新左侧菜单
|
||
*/
|
||
updateMenuStatus(index) {
|
||
if (index != this.currentLeftIndex) {
|
||
this.$emit("onScroll", this.data[index].id);
|
||
}
|
||
this.currentLeftIndex = index;
|
||
this.scrollLeftTop = index * this.menuHeight - this.wrapHeight / 2;
|
||
},
|
||
/**
|
||
* 滚动监听
|
||
*/
|
||
rightScroll(e) {
|
||
let scrollTop = e.detail.scrollTop + 15;
|
||
for (let index = 0; index < this.rightNodePos.length; index++) {
|
||
let current = this.rightNodePos[index];
|
||
let next = this.rightNodePos[index + 1];
|
||
if (!next || (scrollTop >= current && scrollTop < next)) {
|
||
this.updateMenuStatus(index);
|
||
return;
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 选中某项
|
||
*/
|
||
switchMenu(index) {
|
||
this.currentLeftIndex = index;
|
||
this.scrollRightTop = this.rightNodePos[index];
|
||
},
|
||
/**
|
||
* 按ID选中
|
||
*/
|
||
switchCate(id) {
|
||
let activeIndex = 0;
|
||
this.data.forEach((item, index) => {
|
||
if (item.id == id) {
|
||
activeIndex = index;
|
||
}
|
||
});
|
||
this.switchMenu(activeIndex);
|
||
},
|
||
/**
|
||
* 点击
|
||
*/
|
||
clickItem(parentId, id) {
|
||
this.$emit("clickItem", parentId, id);
|
||
},
|
||
/**
|
||
* 改变数量
|
||
*/
|
||
changeNumber(e, id) {
|
||
this.$emit("changeNumber", e, id);
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.component-cate {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 100%;
|
||
.menu-wrap {
|
||
flex: 1;
|
||
display: flex;
|
||
overflow: hidden;
|
||
}
|
||
.tab-view {
|
||
width: 200rpx;
|
||
height: 100%;
|
||
background-color: #f6f6f6;
|
||
.tab-item {
|
||
height: 100rpx;
|
||
box-sizing: border-box;
|
||
padding-left: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 26rpx;
|
||
font-weight: bold;
|
||
color: #8b8b8b;
|
||
}
|
||
.tab-item-active {
|
||
position: relative;
|
||
color: #4b65ed;
|
||
background: #ffffff;
|
||
}
|
||
.tab-item-active::before {
|
||
content: "";
|
||
position: absolute;
|
||
border-left: 4px solid #4b65ed;
|
||
height: 24rpx;
|
||
left: 0;
|
||
top: 38rpx;
|
||
}
|
||
}
|
||
.right-box {
|
||
width: 550rpx;
|
||
.page-view.cate {
|
||
padding-right: 16rpx;
|
||
}
|
||
.page-view.list {
|
||
padding-right: 0;
|
||
}
|
||
.class-item {
|
||
background-color: #ffffff;
|
||
padding: 36rpx 32rpx 0 32rpx;
|
||
}
|
||
}
|
||
}
|
||
</style> |