YNUtdPlatform/uni_modules/m-tabbar/components/m-tabbar/m-tabbar.vue

516 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="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 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'" />
<!-- tabbar border -->
<view class="m-tabbar__flex">
<view class="m-tabbar__item" @click="_tabChange(index)" v-for="(item, index) in tabbarList" :key="index"
: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>
<!-- tabbar item -->
</view>
<!-- tabbar item box -->
</view>
<!-- tabbar block -->
</view>
<!-- tabbar box -->
</template>
<script>
// style转为string
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
})
}
// 设置缓存,方便重复调用方法
const setTabbarStorage = (key, value, name) => {
try {
uni.setStorageSync(`mTab_${key}_${name}`, value)
} catch (e) {
console.error(e)
}
}
// 读取缓存
const getTabbarStorage = (key, name) => {
try {
const value = uni.getStorageSync(`mTab_${key}_${name}`)
if (value) {
return value
}
return ''
} catch (e) {
console.error(e)
}
}
import PageConfig from '@/pages.json'
export default {
emits: ['change', 'click'],
props: {
// 使用原生配置自动生成
native: {
type: Boolean,
default: false
},
// 当前选择下标默认0开始
current: {
type: [Number, String],
default: 0
},
// tabbar配置项
tabbar: {
type: Object,
default () {
return {}
}
},
// 是否固定在底部
fixed: {
type: Boolean,
default: false
},
// 是否自动填充底部
fill: {
type: Boolean,
default: false
},
// 显示层级
zIndex: {
type: [Number, String],
default: 9999
},
// 是否自动规避底部安全距离
safeBottom: {
type: Boolean,
default: true
},
// 切换时拦截事件(有兼容性,部分平台不支持)
beforeChange: {
type: Function,
default: null
},
// 自定义tabbar高度单位rpx
tabbarHeight: {
type: [Number, String],
default: 100
},
// 自定义tabbar的name,方便读取默认tabbar
name: {
type: String,
default: 'custom'
}
},
data() {
return {
isShowTabBar: false, //是否显示导航
currentIndex: 0, //当前值
beforeData: {}, //切换前值
initTabbarConfig: {},
isReload: false //uni不支持computed的get set使用reload无感重置刷新
}
},
watch: {
current(val) {
this.currentIndex = val * 1
}
},
computed: {
tabbarConfig() {
const {
isReload,
native,
initTabbarConfig: tabbar
} = this
if(isReload){}
// 如果是native模式直接读取执行
if (native) {
if (!tabbar) {
console.error('Native mode, Pages.json no tabbar config')
return {
borderStyle: 'black',
list: []
}
}
return tabbar
}
// 如果没有配置tabbar会尝试在缓存读取
if (!tabbar.color) {
return this._getDefaultTabbar()
}
return tabbar
},
tabbarList() {
const {
isReload,
tabbarConfig: {
list
}
} = this
if(isReload){}
if (!list) {
console.error('No tabbar config')
return []
}
return replaceTabbarList(list)
},
borderStyle() {
const {
isReload,
borderStyle
} = this.tabbarConfig
if(isReload){}
return borderStyle
},
tabbarBoxStyle() {
const {
isReload,
zIndex
} = this
if(isReload){}
return obj2strStyle({
'z-index': zIndex,
})
},
tabbarFillStyle() {
const {
isReload,
tabbarHeight
} = this
if(isReload){}
return obj2strStyle({
'height': `${tabbarHeight}rpx`
})
},
tabbarStyle() {
const {
isReload,
tabbarHeight,
tabbarConfig: {
backgroundColor
}
} = this
if(isReload){}
return obj2strStyle({
'height': `${tabbarHeight}rpx`,
'background-color': backgroundColor || '#fff',
})
},
tabbarItemStyle() {
const {
isReload,
currentIndex,
tabbarConfig: {
color,
selectedColor
}
} = this
if(isReload){}
return obj2strStyle({
'color': currentIndex ? selectedColor : color
})
}
},
created() {
const {
tabbar,
native
} = this
const pageTabbar = PageConfig.tabBar
this.initTabbarConfig = native ? pageTabbar : tabbar
// console.log(this.name)
this._initTabbar()
},
methods: {
_initTabbar() {
const {
current,
fill,
native,
tabbarList
} = this
this.currentIndex = current * 1
if (native && tabbarList.length > 0) {
const currentPage = `/${getCurrentPages()[0].route}`
const currentIndex = tabbarList.findIndex(item => item.pagePath === currentPage)
this.currentIndex = currentIndex
uni.hideTabBar()
}
setTimeout(() => {
this.isShowTabBar = true
})
this._setTabbarStorage('tabbarConfig')
},
_setTabbarStorage(key){
setTabbarStorage(key, this.tabbarConfig, this.name)
this._setReload()
},
_getDefaultTabbar() {
return getTabbarStorage('tabbarConfig', this.name)
},
_checkMaxIndex(index) {
const {
tabbarConfig: {
list
}
} = this
if (list.length < 1) {
return false
}
if (!list[index]) {
console.error('Max tabbar index')
return false
}
return true
},
_setReload(){
this.isReload = true
setTimeout(()=>{
this.isReload = false
})
},
setTabBarBadge(obj) {
const {
index,
text
} = obj
if (this._checkMaxIndex(index)) {
this.tabbarConfig.list[index].dot = text
this._setTabbarStorage('tabbarConfig')
}
},
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._setTabbarStorage('tabbarConfig')
}
},
showTabBar() {
this.isShowTabBar = true
},
hideTabBar() {
this.isShowTabBar = false
},
reload(){
this.initTabbarConfig = this._getDefaultTabbar()
},
_tabChange(index) {
const {
currentIndex
} = this
this.$emit('click', index)
if (index === currentIndex) {
return
}
this.beforeData = {
newIndex: index,
oldIndex: currentIndex,
next: this.jumpPage
}
if (this.beforeChange) {
this.beforeChange(this._jumpPage, index)
} else {
this._jumpPage()
}
},
_jumpPage() {
const {
native,
beforeData: {
newIndex: index
},
tabbarList: list
} = this
const {
pagePath: url,
openType
} = list[index]
if (url) {
if (native) {
uni.switchTab({
url
})
} else {
if (openType !== 'navigate') {
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>