shop-applet/components/m-tabbar/m-tabbar.vue

463 lines
11 KiB
Vue
Raw Normal View History

2024-05-14 17:25:13 +08:00
<template>
<view class="m-tabbar-box" :style="tabbarBoxStyle" v-if="isShowTabBar">
<view class="m-tabbar__fill" v-if="fill || native" :class="{'m-tabbar__safe': (safeBottom || native)}"
:style="tabbarFillStyle"></view>
<view id="m-tabbar" class="m-tabbar" :class="{'fixed': (fixed || native), 'm-tabbar__safe': (safeBottom || native)}"
:style="tabbarStyle">
<view class="m-tabbar__border" v-if="borderStyle === 'black' "></view>
<view class="m-tabbar__flex">
<view @click="tabChange(index)" v-for="(item, index) in tabbarList" :key="index" class="m-tabbar__item" :class="{
'm-tabbar__item__active': index === currentIndex,
}">
<slot :name="`tabbar_index_${index}`">
<view class="m-tabbar__icon">
<view class="m-tabbar__badge" v-if="item.dot">{{item.dot}}</view>
<image :src="currentIndex === index ? item.selectedIconPath : item.iconPath" class="m-tabbar__icon_img" />
</view>
<view class="m-tabbar__label"
:style="{'color': index === currentIndex ? tabbarConfig.selectedColor : tabbarConfig.color }">
{{ item.text }}
</view>
</slot>
</view>
</view>
<view :style="{ paddingBottom: systemInfo.tabbarPaddingB + 'px', background: '#fff' }"></view>
</view>
</view>
</template>
<script>
const obj2strStyle = (obj) => {
let style = ''
for (let key in obj) {
style += `${key}:${obj[key]};`
}
return style
}
const padFirstSymbol = (str, smb) => {
if (str.startsWith(smb) || str.startsWith('http')) {
return str
}
return `/${str}`
}
const replaceTabbarList = (list) => {
if (!list.length > 0) {
return []
}
return list.map(item => {
if (item.iconPath) {
item.iconPath = padFirstSymbol(item.iconPath, '/')
}
if (item.pagePath) {
item.pagePath = padFirstSymbol(item.pagePath, '/')
}
if (item.selectedIconPath) {
item.selectedIconPath = padFirstSymbol(item.selectedIconPath, '/')
}
return item
})
}
// import base from '@/config/baseUrl.js';
import PageConfig from '@/pages.json'
export default {
emits: ['change', 'click'],
props: {
current: {
type: [Number, String],
default: 0
},
tabbar: {
type: Object,
default () {
return {}
}
},
fixed: {
type: Boolean,
default: false
},
fill: {
type: Boolean,
default: false
},
zIndex: {
type: [Number, String],
default: 9999
},
native: {
type: Boolean,
default: false
},
safeBottom: {
type: Boolean,
default: true
},
beforeChange: {
type: Function,
default: null
},
tabbarHeight: {
type: [Number, String],
default: 100
}
},
data() {
return {
systemInfo: {},
isShowTabBar: false,
currentIndex: 0,
beforeData: {},
reload: false
}
},
watch: {
current(val) {
this.currentIndex = val * 1
}
},
computed: {
tabbarConfig() {
const {
native,
reload
} = this
if (reload) {}
if (native) {
const {
tabBar
} = PageConfig
if (!tabBar) {
console.error('Native mode, Pages.json no tabbar config')
return {
borderStyle: 'black',
list: []
}
}
return tabBar
}
return this.tabbar
},
tabbarList() {
const {
reload
} = this
const {
list
} = this.tabbarConfig
if (reload) {}
if (list) {
return replaceTabbarList(list)
}
console.error('No tabbar config')
return []
},
borderStyle() {
const {
reload
} = this
const {
borderStyle
} = this.tabbarConfig
if (reload) {}
return borderStyle
},
tabbarBoxStyle() {
const {
zIndex,
reload
} = this
if (reload) {}
return obj2strStyle({
'z-index': zIndex,
})
},
tabbarFillStyle() {
const {
tabbarHeight,
safeBottom,
reload
} = this
if (reload) {}
return obj2strStyle({
'height': `${tabbarHeight}rpx`
})
},
tabbarStyle() {
const {
tabbarHeight,
reload
} = this
const {
backgroundColor
} = this.tabbarConfig
if (reload) {}
return obj2strStyle({
// 'height': `${tabbarHeight}rpx`,
'background-color': backgroundColor || '#fff',
})
},
tabbarItemStyle() {
const {
currentIndex,
reload
} = this
const {
color,
selectedColor
} = this.tabbarConfig
if (reload) {}
return obj2strStyle({
'color': currentIndex ? selectedColor : color
})
}
},
mounted() {
this.initTabbar()
this.getTabbarHeight()
},
methods: {
getTabbarHeight() {
var systemInfo = uni.getSystemInfoSync()
var data = {
...systemInfo,
tabbarH: 50, //tabbar高度--单位px
tabbarPaddingB: 0, //tabbar底部安全距离高度--单位px
device: systemInfo.system.indexOf('iOS') != -1 ? 'iOS' : 'Android', //苹果或者安卓设备
}
let modelArr = ['10,3', '10,6', 'X', 'XR', 'XS', '11', '12', '13', '14', '15', '16'];
let model = systemInfo.model;
model && modelArr.forEach(item => {
//适配iphoneX以上的底部给tabbar一定高度的padding-bottom
if (model.indexOf(item) != -1 && (model.indexOf('iPhone') != -1 || model.indexOf('iphone') != -1)) {
data.tabbarH = 70
data.tabbarPaddingB = 20
}
})
this.systemInfo = data
},
initTabbar() {
const {
current,
fill,
native,
tabbarList
} = this
this.currentIndex = current * 1
if (native) {
const currentPage = `/${getCurrentPages()[0].route}`
const currentIndex = tabbarList.findIndex(item => item.pagePath === currentPage)
this.currentIndex = currentIndex
if (tabbarList.length > 0) {
uni.hideTabBar()
}
}
setTimeout(() => {
this.isShowTabBar = true
})
},
reLoad() {
this.reload = true
setTimeout(() => {
this.reload = false
})
},
checkMaxIndex(index) {
if (!this.tabbarConfig.list[index]) {
console.error('Max tabbar index')
return false
}
return true
},
setTabBarBadge(obj) {
const {
index,
text
} = obj
if (this.checkMaxIndex(index)) {
this.tabbarConfig.list[index].dot = text
this.reLoad()
}
},
setTabBarItem(obj) {
const {
index,
text,
pagePath: newPagePath,
iconPath,
selectedIconPath
} = obj
const {
pagePath: oldPagePath
} = this.tabbarConfig.list[index]
if (this.checkMaxIndex(index)) {
this.tabbarConfig.list[index] = {
pagePath: newPagePath ? newPagePath : oldPagePath,
text,
iconPath,
selectedIconPath
}
this.reLoad()
}
},
showTabBar() {
this.isShowTabBar = true
},
hideTabBar() {
this.isShowTabBar = false
},
tabChange(index) {
const { currentIndex } = this
this.$emit('click', index)
if (index === currentIndex) {
index === 3 ? uni.$emit('resetLocation') : ''
return
}
this.beforeData = {
newIndex: index,
oldIndex: currentIndex,
next: this.jumpPage
}
if (this.beforeChange) {
this.beforeChange(this.jumpPage)
} else {
this.jumpPage()
}
},
jumpPage() {
const {
native,
beforeData,
tabbarList: list
} = this
const {
newIndex: index
} = beforeData
const {
pagePath: url,
openType
} = list[index]
if (url) {
if (native) {
uni.switchTab({
url
})
} else {
this.currentIndex = index
switch (openType) {
case 'navigate':
uni.navigateTo({
url
})
break;
case 'redirect':
uni.redirectTo({
url
})
break;
case 'reLaunch':
uni.reLaunch({
url
})
break;
case 'switchTab':
uni.switchTab({
url
})
break;
case 'navigateBack':
uni.navigateBack({
delta: 1
})
break;
default:
uni.reLaunch({
url
})
}
}
}
this.$emit('change', index)
}
}
};
</script>
<style lang="scss" scoped>
.m-tabbar-box {
position: relative;
z-index: 9999;
}
.m-tabbar {
position: relative;
&.fixed {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
}
&__safe {
// padding-bottom: env(safe-area-inset-bottom);
}
}
.m-tabbar__fill {
pointer-events: none;
opacity: 0;
}
.m-tabbar__flex {
display: flex;
flex-direction: row;
}
.m-tabbar__border {
background-color: rgba(0, 0, 0, 0.33);
width: 100%;
height: 1rpx;
transform: scaleY(0.5);
}
.m-tabbar__item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
padding: 4px 0 2px;
}
.m-tabbar__icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 6rpx;
position: relative;
&_img {
display: block;
width: 100%;
height: 100%;
}
.m-tabbar__badge {
color: #fff;
background-color: #f00;
border-radius: 20rpx;
font-size: 22rpx;
position: absolute;
right: -25rpx;
left: 40rpx;
padding: 2rpx 0;
width: 100%;
text-align: center;
white-space: nowrap;
}
}
.m-tabbar__label {
font-size: 24rpx;
}
</style>