初始化万家商超用户端仓库
This commit is contained in:
349
components/benben-flex-tabs/benben-flex-tabs.vue
Normal file
349
components/benben-flex-tabs/benben-flex-tabs.vue
Normal file
@ -0,0 +1,349 @@
|
||||
<template>
|
||||
<view :class="className" :style="headStyle" :id="`${selectMark}-box`">
|
||||
<view class="benben-flex-tabs" :class="className" :style="[stickyContent]">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
let pageInfo = null
|
||||
export default {
|
||||
components: {},
|
||||
name: 'benben-flex-tabs',
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
selectMark: {
|
||||
type: String,
|
||||
default: 'benben-tabs',
|
||||
},
|
||||
scrollspy: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
openSticky: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
openTitleType: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
top: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: 90,
|
||||
},
|
||||
isShowContent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isShowLeft: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isShowRight: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
tabsInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
lineleft: '',
|
||||
lineWidth: '',
|
||||
moveX: 0,
|
||||
scrollX: 0,
|
||||
}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabs: [],
|
||||
flag: true,
|
||||
isScroll: true,
|
||||
tabsHeight: 0,
|
||||
setTimeoutFn: null,
|
||||
flagSetTimeoutFn: null,
|
||||
moveX: 0,
|
||||
lineWidth: 0,
|
||||
lineleft: 0,
|
||||
stickyWidth: 'auto',
|
||||
stickyHeight: 'auto',
|
||||
stickyLeft: 0,
|
||||
isFixed: false,
|
||||
}
|
||||
},
|
||||
//监听属性
|
||||
watch: {
|
||||
value: {
|
||||
handler(val, oldVal) {
|
||||
this.$emit('change', val)
|
||||
this.queryMultipleNodes(val)
|
||||
if (this.isScroll) {
|
||||
this.pageScrollTo(val)
|
||||
}
|
||||
this.isScroll = true
|
||||
},
|
||||
},
|
||||
'tabsInfo.PageScrollX': {
|
||||
handler(val) {
|
||||
if (!this.scrollspy) return
|
||||
this.setTimeoutFn && clearTimeout(this.setTimeoutFn)
|
||||
this.setTimeoutFn = setTimeout(() => {
|
||||
for (let index = 0; index < this.tabs.length; index++) {
|
||||
let elementBefore = 0
|
||||
let elementAfter = Infinity
|
||||
if (index != 0) {
|
||||
elementBefore = this.tabs[index].top - this.tabsHeight
|
||||
}
|
||||
if (index != this.tabs.length - 1) {
|
||||
elementAfter = this.tabs[index + 1].top - this.tabsHeight
|
||||
}
|
||||
if (val + 1 > elementBefore && val + 1 < elementAfter) {
|
||||
if (this.tabs[index].name != this.value && this.flag) {
|
||||
this.isScroll = false
|
||||
this.$emit('input', this.tabs[index].name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 300)
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
headStyle() {
|
||||
return `box-sizing: content-box;padding-top:${this.openTitleType ? this.StatusBarRpx : 0}rpx`
|
||||
},
|
||||
stickyContent() {
|
||||
let style = {}
|
||||
style.position = this.isFixed ? 'fixed' : 'static'
|
||||
if (this.openTitleType) {
|
||||
style.position = 'fixed'
|
||||
style.paddingTop = this.StatusBarRpx + 'rpx'
|
||||
}
|
||||
style.top = this.stickyTop + 'px'
|
||||
style.left = this.stickyLeft + 'px'
|
||||
if (this.stickyWidth != 'auto') {
|
||||
style.width = this.stickyWidth + 'px'
|
||||
}
|
||||
if (this.stickyHeight != 'auto') {
|
||||
style.height = this.stickyHeight + 'px'
|
||||
}
|
||||
style.zIndex = this.isFixed ? this.zIndex : 0
|
||||
return style
|
||||
},
|
||||
stickyTop() {
|
||||
if (this.openTitleType) {
|
||||
return Math.floor(this.top / this.unitRatio)
|
||||
} else {
|
||||
return Math.floor((this.top + this.StatusBarRpx) / this.unitRatio)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toJSON() {},
|
||||
// 获取元素
|
||||
uGetRect(selector, all) {
|
||||
return new Promise((resolve) => {
|
||||
// #ifdef H5
|
||||
let rect = document[all ? 'querySelectorAll' : 'querySelector'](selector).getBoundingClientRect()
|
||||
if (all && Array.isArray(rect) && rect.length) {
|
||||
resolve(rect)
|
||||
}
|
||||
if (!all && rect) {
|
||||
resolve(rect)
|
||||
}
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni
|
||||
.createSelectorQuery()
|
||||
.in(this)
|
||||
[all ? 'selectAll' : 'select'](selector)
|
||||
.boundingClientRect((rect) => {
|
||||
if (all && Array.isArray(rect) && rect.length) {
|
||||
resolve(rect)
|
||||
}
|
||||
if (!all && rect) {
|
||||
resolve(rect)
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
initObserveContent() {
|
||||
// 获取吸顶内容的高度,用于在js吸顶模式时,给父元素一个填充高度,防止"塌陷"
|
||||
this.uGetRect(`#${this.selectMark}-box`).then((res) => {
|
||||
this.stickyHeight = res.height
|
||||
this.stickyLeft = res.left
|
||||
this.stickyWidth = res.width
|
||||
this.$nextTick(() => {
|
||||
this.observeContent()
|
||||
})
|
||||
})
|
||||
},
|
||||
observeContent() {
|
||||
// 先断掉之前的观察
|
||||
this.disconnectObserver('contentObserver')
|
||||
let contentObserver = uni.createIntersectionObserver(this)
|
||||
/* #ifndef MP-WEIXIN */
|
||||
let pages = getCurrentPages()
|
||||
let pageInfo = getCurrentPages()[pages.length - 1]
|
||||
contentObserver = uni.createIntersectionObserver(pageInfo)
|
||||
/* #endif */
|
||||
// 到屏幕顶部的高度时触发
|
||||
contentObserver.relativeToViewport({
|
||||
top: -this.stickyTop,
|
||||
})
|
||||
// 绑定观察的元素
|
||||
contentObserver.observe(`#${this.selectMark}-box`, (res) => {
|
||||
this.setFixed(res)
|
||||
})
|
||||
this.contentObserver = contentObserver
|
||||
},
|
||||
setFixed(res) {
|
||||
this.isFixed = this.stickyTop > res.boundingClientRect.bottom
|
||||
},
|
||||
disconnectObserver(observerName) {
|
||||
// 断掉观察,释放资源
|
||||
const observer = this[observerName]
|
||||
observer && observer.disconnect()
|
||||
},
|
||||
//滚动到指定位置
|
||||
queryMultipleNodes(name) {
|
||||
let that = this
|
||||
let lineWidth = 0
|
||||
let boxlet = 0
|
||||
let boxWidth = 0
|
||||
let maxLeft = 0
|
||||
let itemLeft = 0
|
||||
let moveX = 0
|
||||
this.$nextTick(() => {
|
||||
uni
|
||||
.createSelectorQuery()
|
||||
.select(`#${that.selectMark}`)
|
||||
.boundingClientRect()
|
||||
.select(`#${that.selectMark}-title-item-${name}`)
|
||||
.boundingClientRect()
|
||||
.select(`#${that.selectMark}-line`)
|
||||
.boundingClientRect()
|
||||
.select(`#${that.selectMark}-content`)
|
||||
.boundingClientRect()
|
||||
.exec(function (rects) {
|
||||
that.tabsHeight = Math.floor(rects[0].height + that.StatusBar + that.top / that.unitRatio)
|
||||
boxlet = rects[0].left
|
||||
boxWidth = rects[0].width
|
||||
lineWidth = rects[2].width
|
||||
|
||||
if (rects[1] && rects[1].width && lineWidth > rects[1].width) {
|
||||
lineWidth = rects[1].width
|
||||
itemLeft = rects[1].left
|
||||
}
|
||||
maxLeft = rects[3].width - rects[0].width
|
||||
maxLeft = maxLeft < 0 ? 0 : maxLeft
|
||||
let query = uni.createSelectorQuery().select(`#${that.selectMark}`).scrollOffset()
|
||||
query.select(`#${that.selectMark}-title-item-${name}`).boundingClientRect()
|
||||
query.exec(function (res) {
|
||||
if (!res[1]) return
|
||||
itemLeft = that.tabsInfo.scrollX + res[1].left - boxlet + res[1].width / 2
|
||||
if (itemLeft >= boxWidth / 2) {
|
||||
moveX = itemLeft - boxWidth / 2
|
||||
moveX = moveX > maxLeft ? maxLeft : moveX
|
||||
} else {
|
||||
moveX = 0
|
||||
}
|
||||
that.$emit('update:tabsInfo', {
|
||||
...that.tabsInfo,
|
||||
lineleft: itemLeft + 'px',
|
||||
lineWidth: lineWidth + 'px',
|
||||
moveX: moveX,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
setflag() {
|
||||
this.flag = false
|
||||
this.flagSetTimeoutFn && clearTimeout(this.flagSetTimeoutFn)
|
||||
this.flagSetTimeoutFn = setTimeout(() => {
|
||||
this.flag = true
|
||||
}, 1000)
|
||||
},
|
||||
pageScrollTo(name) {
|
||||
if (!this.isShowContent) return
|
||||
if (!this.scrollspy) return
|
||||
var that = this
|
||||
var query = uni.createSelectorQuery()
|
||||
query.selectViewport().scrollOffset()
|
||||
query.select(`#${this.selectMark}-body-item-${name}`).boundingClientRect()
|
||||
query.exec(function (res) {
|
||||
var miss = res[0].scrollTop - that.tabsHeight
|
||||
if (res[1]?.top) miss += res[1]?.top
|
||||
that.setflag()
|
||||
uni.pageScrollTo({
|
||||
scrollTop: miss,
|
||||
duration: 300,
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取主体高度
|
||||
getBodyHeight() {
|
||||
if (!this.isShowContent) return
|
||||
if (!this.scrollspy) return
|
||||
let that = this
|
||||
uni
|
||||
.createSelectorQuery()
|
||||
.selectAll(`.${this.selectMark}-body-item`)
|
||||
.boundingClientRect()
|
||||
.exec(function (res) {
|
||||
let tabs = []
|
||||
res[0].map((item, index) => {
|
||||
let name = item.id.replace(`${that.selectMark}-body-item-`, '')
|
||||
if (index == 0) {
|
||||
tabs.push({
|
||||
name,
|
||||
top: 0,
|
||||
})
|
||||
} else {
|
||||
tabs.push({
|
||||
name,
|
||||
top: item.top,
|
||||
})
|
||||
}
|
||||
})
|
||||
that.tabs = tabs
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {},
|
||||
mounted() {
|
||||
this.$emit('update:tabs-info', {
|
||||
...this.tabsInfo,
|
||||
lineleft: '0px',
|
||||
moveX: 0,
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
this.queryMultipleNodes(this.value)
|
||||
this.getBodyHeight()
|
||||
})
|
||||
},
|
||||
updated() {
|
||||
if (this.openSticky || this.openTitleType) {
|
||||
this.initObserveContent()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.disconnectObserver('contentObserver')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
Reference in New Issue
Block a user