You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
5.6 KiB
Vue
220 lines
5.6 KiB
Vue
<template>
|
|
<view class="tabbar-shell" :class="{ 'is-hidden': !tabbarVisible }">
|
|
<up-tabbar :value="activeIndex" :activeColor="activeColor" :inactiveColor="inactiveColor"
|
|
:safeAreaInsetBottom="true" :fixed="true" :placeholder="true" :border="false" @change="handleChange"
|
|
zIndex="1000">
|
|
<up-tabbar-item v-for="(item, index) in tabList" :key="index" :text="item.text" :name="index">
|
|
<template #active-icon>
|
|
<uni-icons v-if="item.iconType === 'uni-icons'" :type="item.iconName" size="30" :color="activeColor" />
|
|
<u-icon v-else-if="item.iconType === 'uview-plus'" :name="item.iconName" size="30"
|
|
:color="activeColor"></u-icon>
|
|
<image v-else :src="item.selectedIcon" class="tabbar-icon" mode="widthFix" />
|
|
</template>
|
|
<template #inactive-icon>
|
|
<uni-icons v-if="item.iconType === 'uni-icons'" :type="item.iconName" size="30" :color="inactiveColor" />
|
|
<u-icon v-else-if="item.iconType === 'uview-plus'" :name="item.iconName" size="30"
|
|
:color="inactiveColor"></u-icon>
|
|
<image v-else :src="item.icon" class="tabbar-icon" mode="widthFix" />
|
|
</template>
|
|
</up-tabbar-item>
|
|
</up-tabbar>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
import useUserStore from '@/store/modules/user'
|
|
import { storeToRefs } from 'pinia'
|
|
import { getTabBarMenus } from '@/utils/permissionMenu'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
const TABBAR_VISIBILITY_EVENT = 'tabbar-visibility-change'
|
|
|
|
const userStore = useUserStore()
|
|
const { menus } = storeToRefs(userStore)
|
|
const { t } = useI18n()
|
|
|
|
const inactiveColor = '#666666'
|
|
const activeColor = '#409eff'
|
|
|
|
const activeIndex = ref(0)
|
|
const tabbarVisible = ref(true)
|
|
|
|
function normalizeRoute(path = '') {
|
|
return String(path || '').trim().replace(/^\/+/, '')
|
|
}
|
|
|
|
function normalizeMenuName(value = '') {
|
|
return String(value || '').trim().toLowerCase()
|
|
}
|
|
|
|
function isMineMenu(menu) {
|
|
const names = [menu?.name, menu?.enName, menu?.text].map(normalizeMenuName)
|
|
const paths = [menu?.path, menu?.component].map((item) => normalizeRoute(item).toLowerCase())
|
|
return (
|
|
names.some((name) => ['我的', '个人中心', 'mine', 'profile'].includes(name)) ||
|
|
paths.some((path) => path === 'pages/mine' || path === 'pages/smq/mine')
|
|
)
|
|
}
|
|
|
|
function isSmqTabbar() {
|
|
const pages = getCurrentPages()
|
|
const route = pages && pages.length > 0 ? pages[pages.length - 1].route : ''
|
|
return normalizeRoute(route).startsWith('pages/smq/')
|
|
}
|
|
|
|
function isUniIcon(icon) {
|
|
return String(icon || '').startsWith('uni-icons:')
|
|
}
|
|
|
|
function isUviewIcon(icon) {
|
|
return String(icon || '').startsWith('uview-plus:')
|
|
}
|
|
|
|
function getUniIconName(icon) {
|
|
return String(icon || '').replace(/^uni-icons:/, '').trim()
|
|
}
|
|
|
|
function getUviewIconName(icon) {
|
|
return String(icon || '').replace(/^uview-plus:/, '').trim()
|
|
}
|
|
|
|
function resolveTabIconMeta(menu) {
|
|
if (isUniIcon(menu?.icon)) {
|
|
return {
|
|
iconType: 'uni-icons',
|
|
iconName: getUniIconName(menu.icon)
|
|
}
|
|
}
|
|
|
|
if (isUviewIcon(menu?.icon)) {
|
|
return {
|
|
iconType: 'uview-plus',
|
|
iconName: getUviewIconName(menu.icon)
|
|
}
|
|
}
|
|
|
|
return {
|
|
iconType: '',
|
|
iconName: '',
|
|
icon: '',
|
|
selectedIcon: ''
|
|
}
|
|
}
|
|
|
|
function createTabItem(menu) {
|
|
const iconMeta = resolveTabIconMeta(menu)
|
|
return {
|
|
text: String(menu.name || menu.enName || '').trim(),
|
|
icon: iconMeta.icon,
|
|
selectedIcon: iconMeta.selectedIcon,
|
|
iconType: iconMeta.iconType,
|
|
iconName: iconMeta.iconName,
|
|
path: menu.path
|
|
}
|
|
}
|
|
|
|
function createMineTabItem() {
|
|
return {
|
|
text: t('tab.mine'),
|
|
icon: '',
|
|
selectedIcon: '',
|
|
iconType: 'uni-icons',
|
|
iconName: 'person',
|
|
path: isSmqTabbar() ? '/pages/smq/mine' : '/pages/mine'
|
|
}
|
|
}
|
|
|
|
function getCurrentActiveIndex() {
|
|
const pages = getCurrentPages()
|
|
if (pages && pages.length > 0) {
|
|
const route = pages[pages.length - 1].route
|
|
const currentIndex = tabList.value.findIndex((item) => normalizeRoute(item.path) === route)
|
|
return currentIndex >= 0 ? currentIndex : 0
|
|
}
|
|
return 0
|
|
}
|
|
|
|
const tabList = computed(() => {
|
|
const apiTabs = getTabBarMenus(menus.value)
|
|
.filter((menu) => !isMineMenu(menu))
|
|
.map(createTabItem)
|
|
|
|
return [...apiTabs, createMineTabItem()]
|
|
})
|
|
|
|
onMounted(() => {
|
|
activeIndex.value = getCurrentActiveIndex()
|
|
uni.$on(TABBAR_VISIBILITY_EVENT, handleTabbarVisibility)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
uni.$off(TABBAR_VISIBILITY_EVENT, handleTabbarVisibility)
|
|
})
|
|
|
|
function handleTabbarVisibility(visible = true) {
|
|
tabbarVisible.value = visible !== false
|
|
}
|
|
|
|
function handleChange(index) {
|
|
if (activeIndex.value === index) return
|
|
activeIndex.value = index
|
|
const item = tabList.value[index]
|
|
if (item) {
|
|
uni.reLaunch({
|
|
url: item.path
|
|
})
|
|
}
|
|
}
|
|
|
|
watch(() => useUserStore().menus, () => {
|
|
activeIndex.value = getCurrentActiveIndex()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.tabbar-shell {
|
|
position: relative;
|
|
|
|
:deep(.u-tabbar) {
|
|
position: fixed !important;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0 !important;
|
|
width: 100%;
|
|
z-index: 1000 !important;
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
will-change: transform, opacity;
|
|
transition: transform 0.36s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.24s ease;
|
|
}
|
|
|
|
:deep(.u-tabbar__content) {
|
|
z-index: 1000 !important;
|
|
}
|
|
|
|
&.is-hidden {
|
|
:deep(.u-tabbar) {
|
|
transform: translateY(calc(100% + 24rpx + env(safe-area-inset-bottom)));
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
.tabbar-icon {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
}
|
|
|
|
:deep(.u-tabbar-item__text) {
|
|
margin-top: 0 !important;
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
.u-tabbar {
|
|
flex: none !important;
|
|
}
|
|
</style>
|