xx-applets/src/components/cate/cate.vue

319 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="component-wrap" :style="{ height: wrapHeight + 'px' }">
<view class="component-menu-wrap">
<scroll-view
scroll-y
class="component-tab-view menu-scroll-view"
:scroll-top="scrollTop"
:scroll-into-view="itemId"
>
<view
class="component-tab-item"
v-for="(item, index) in data"
:key="index"
:class="[
current == index ? 'component-tab-item-active' : '',
]"
@tap.stop="swichMenu(index)"
>
<text class="component-line-1">{{ item.name }}</text>
</view>
</scroll-view>
<scroll-view
:scroll-top="scrollRightTop"
scroll-y
scroll-with-animation
class="right-box"
@scroll="rightScroll"
>
<view class="page-view" :class="[cateType]">
<view
class="class-item"
v-for="(item, index) in data"
:id="'item' + index"
:key="index"
:style="{
height:
index == data.length - 1
? cateType == 'cate'
? 'calc(100vh - 70rpx)'
: '100vh'
: 'auto',
}"
>
<cate-tmpl
v-if="cateType === 'cate'"
:data="item"
@clickCate="clickCate"
></cate-tmpl>
<list-tmpl
v-if="cateType === 'list'"
:data="item"
@clickItem="clickItem"
></list-tmpl>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex';
import CateTmpl from "@/components/cate/template/cate";
import ListTmpl from "@/components/cate/template/list";
export default {
name: "component-cate",
data() {
return {
scrollTop: 0, //tab标题的滚动条位置
oldScrollTop: 0, // tab标题的滚动条旧位置
current: 0, // 预设当前项的值
menuHeight: 0, // 左边菜单的高度
menuItemHeight: 50, // 左边菜单item的高度
itemId: "", // 栏目右边scroll-view用于滚动的id
arr: [], // 储存距离顶部高度的数组
scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
timer: null, // 定时器
wrapHeight: 0,
};
},
props: {
cateType: {
type: String,
default: "cate",
},
offsetHeight: {
type: Number,
default: 0,
},
data: {
type: Array,
default: [],
},
},
components: {
CateTmpl,
ListTmpl,
},
computed: {
...mapState({
config: state => state.system.config,
bodyPaddingTop: (state) => state.system.bodyPaddingTop,
})
},
created() {},
mounted() {
this.getMenuItemTop();
let headerHeight = this.$utils.rpx2px(this.bodyPaddingTop);
let offsetHeight = this.$utils.rpx2px(this.offsetHeight);
const { windowHeight, statusBarHeight } = this.config;
// 窗口高度 - 头部高度 - 顶部内容高度 = 分类容器高度
this.wrapHeight = windowHeight - headerHeight - offsetHeight;
// #ifdef MP-WEIXIN
// 微信窗口高度不会计算tabbar高度这里加回来
if (this.cateType == 'cate') {
this.wrapHeight += this.$utils.rpx2px(100);
}
// #endif
},
destroyed() {},
methods: {
getElRect(elClass, dataVal) {
new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query
.select("." + elClass)
.fields(
{
size: true,
},
(res) => {
// 如果节点尚未生成res值为null循环调用执行
if (!res) {
this.getElRect(elClass);
return;
}
this[dataVal] = res.height;
resolve();
}
)
.exec();
});
},
/**
* 获取右边菜单每个item到顶部的距离
* 储存到 arr 数组里面用于后面滚动判断
*/
getMenuItemTop() {
new Promise((resolve) => {
let selectorQuery = uni.createSelectorQuery().in(this);
selectorQuery
.selectAll(".class-item")
.boundingClientRect((rects) => {
// 如果节点尚未生成rects值为[](因为用selectAll所以返回的是数组),循环调用执行
if (!rects.length) {
this.getMenuItemTop();
return;
}
rects.forEach((rect) => {
// 视情况而定这里减去rects[0].top是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
this.arr.push(rect.top - rects[0].top);
// this.arr.push(rect.top)
resolve();
});
})
.exec();
});
},
/**
* 观测元素相交状态
* 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
* 如果跟.right-box底部相交就动态设置左边栏目的活动状态
*/
async observer() {
this.data.map((val, index) => {
let observer = uni.createIntersectionObserver(this);
observer
.relativeTo(".right-box", {
top: 0,
})
.observe("#item" + index, (res) => {
if (res.intersectionRatio > 0) {
let id = res.id.substring(4);
this.leftMenuStatus(id);
}
});
});
},
/**
* 设置左边菜单的滚动状态
* @index 传入的 ID
*/
async leftMenuStatus(index) {
this.current = index;
// 如果为0意味着尚未初始化
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
await this.getElRect("menu-scroll-view", "menuHeight");
await this.getElRect("component-tab-item", "menuItemHeight");
}
// 将菜单活动item垂直居中
this.scrollTop =
index * this.menuItemHeight +
this.menuItemHeight / 2 -
this.menuHeight / 2;
},
/**
* 点击左边的栏目切换
* @index 传入的 ID
*/
async swichMenu(index) {
if (this.arr.length == 0) {
await this.getMenuItemTop();
}
if (index == this.current) return;
this.scrollRightTop = this.oldScrollTop;
this.$nextTick(function () {
this.scrollRightTop = this.arr[index];
this.current = index;
this.leftMenuStatus(index);
});
},
/**
* 右边菜单滚动
* 如果不存在height2意味着数据循环已经到了最后一个设置左边菜单为最后一项即可
*/
async rightScroll(e) {
this.oldScrollTop = e.detail.scrollTop;
if (this.arr.length == 0) {
await this.getMenuItemTop();
}
if (!this.menuHeight) {
await this.getElRect("menu-scroll-view", "menuHeight");
}
// scrollHeight为右边菜单垂直中点位置
// let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
// scrollHeight为右边菜单头部位置
let scrollHeight = e.detail.scrollTop + 15;
for (let i = 0; i < this.arr.length; i++) {
let height1 = this.arr[i];
let height2 = this.arr[i + 1];
if (
!height2 ||
(scrollHeight >= height1 && scrollHeight < height2)
) {
this.leftMenuStatus(i);
return;
}
}
},
clickCate(id) {
this.$emit("clickCate", id);
},
clickItem(id) {
this.$emit("clickItem", id);
},
},
};
</script>
<style lang="less" scoped>
.component-wrap {
display: flex;
flex-direction: column;
width: 100%;
}
.component-search-box {
padding: 18rpx 30rpx;
}
.component-menu-wrap {
flex: 1;
display: flex;
overflow: hidden;
}
.component-tab-view {
width: 200rpx;
height: 100%;
background-color: #f6f6f6;
}
.component-tab-item {
height: 100rpx;
box-sizing: border-box;
padding-left: 20rpx;
display: flex;
align-items: center;
font-size: 24rpx;
color: #8b8b8b;
font-weight: 400;
line-height: 1;
}
.component-tab-item-active {
position: relative;
color: #4b65ed;
background: #ffffff;
}
.component-tab-item-active::before {
content: "";
position: absolute;
border-left: 4px solid #4b65ed;
height: 24rpx;
left: 0;
top: 38rpx;
}
.component-tab-view {
height: 100%;
}
.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>