463 lines
11 KiB
Vue
463 lines
11 KiB
Vue
<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> |