368 lines
7.2 KiB
Vue
368 lines
7.2 KiB
Vue
<template>
|
||
<view class="custom-tabbar">
|
||
<view
|
||
class="tabbar-item"
|
||
v-for="(item, index) in tabList"
|
||
:key="index"
|
||
@click="switchTab(index, item)"
|
||
:class="{ active: currentTab === index }"
|
||
>
|
||
<!-- 图标容器 -->
|
||
<view class="icon-container">
|
||
<!-- <uni-icons
|
||
:type="currentTab === index ? item.activeIcon : item.icon"
|
||
:size="currentTab === index ? 28 : 24"
|
||
:color="currentTab === index ? item.activeColor : item.color"
|
||
></uni-icons> -->
|
||
<image class="img" :src="currentTab === index ? item.activeIcon : item.icon" alt="" />
|
||
<!-- 徽章 -->
|
||
<view class="badge" v-if="item.badge && item.badge > 0">
|
||
<uni-badge
|
||
:text="item.badge > 99 ? '99+' : item.badge.toString()"
|
||
type="error"
|
||
size="small"
|
||
></uni-badge>
|
||
</view>
|
||
|
||
<!-- 红点 -->
|
||
<view class="red-dot" v-if="item.showRedDot"></view>
|
||
</view>
|
||
|
||
<!-- 文字 -->
|
||
<text class="tab-text" :class="{ active: currentTab === index }">
|
||
{{ item.text }}
|
||
</text>
|
||
|
||
<!-- 动画效果 -->
|
||
<!-- <view class="active-indicator" v-if="currentTab === index"></view> -->
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, onMounted, nextTick } from 'vue';
|
||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||
import home from '@/static/home_nor.png'
|
||
import homeOn from '@/static/home_sel.png'
|
||
import classroom from "@/static/classroom.png"
|
||
import classroomOn from "@/static/classroomOn.png"
|
||
import live from "@/static/live.png"
|
||
import liveOn from "@/static/liveOn.png"
|
||
import education from '@/static/education.png'
|
||
import educationOn from '@/static/educationOn.png'
|
||
import my from '@/static/my.png'
|
||
import myOn from '@/static/myOn.png'
|
||
|
||
// 定义props
|
||
const props = defineProps({
|
||
// 可以添加自定义props
|
||
});
|
||
|
||
// 定义emits
|
||
const emit = defineEmits(['tabChange']);
|
||
|
||
// 当前选中的tab
|
||
const currentTab = ref(0);
|
||
|
||
// tabbar数据
|
||
const tabList = reactive([
|
||
{
|
||
icon: home,
|
||
activeIcon: homeOn,
|
||
text: '首页',
|
||
color: '#999999',
|
||
activeColor: '#007aff',
|
||
badge: 0,
|
||
showRedDot: false,
|
||
pagePath: '/pages/index/index'
|
||
},
|
||
{
|
||
icon: classroom,
|
||
activeIcon: classroomOn,
|
||
text: '患教学堂',
|
||
color: '#999999',
|
||
activeColor: '#007aff',
|
||
badge: 5,
|
||
showRedDot: false,
|
||
pagePath: '/pages/education/education'
|
||
},
|
||
{
|
||
icon: live,
|
||
activeIcon: liveOn,
|
||
text: '会议·直播',
|
||
color: '#999999',
|
||
activeColor: '#007aff',
|
||
badge: 0,
|
||
showRedDot: true,
|
||
pagePath: '/pages/meeting/meeting'
|
||
},
|
||
{
|
||
icon: education,
|
||
activeIcon:educationOn,
|
||
text: '继续教育',
|
||
color: '#999999',
|
||
activeColor: '#007aff',
|
||
badge: 12,
|
||
showRedDot: false,
|
||
pagePath: '/pages/education/continuing'
|
||
},
|
||
{
|
||
icon: my,
|
||
activeIcon: myOn,
|
||
text: '我的',
|
||
color: '#999999',
|
||
activeColor: '#007aff',
|
||
badge: 0,
|
||
showRedDot: false,
|
||
pagePath: '/pages/profile/profile'
|
||
}
|
||
]);
|
||
|
||
// 切换tab
|
||
const switchTab = (index, item) => {
|
||
if (currentTab.value === index) return;
|
||
|
||
// 更新当前tab
|
||
currentTab.value = index;
|
||
|
||
// 清除红点
|
||
if (item.showRedDot) {
|
||
item.showRedDot = false;
|
||
}
|
||
|
||
// 页面跳转
|
||
uni.switchTab({
|
||
url: item.pagePath,
|
||
fail: () => {
|
||
// 如果页面不存在,使用navigateTo
|
||
uni.navigateTo({
|
||
url: item.pagePath,
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: '页面开发中',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// 触发父组件事件
|
||
emit('tabChange', {
|
||
index,
|
||
item
|
||
});
|
||
};
|
||
|
||
// 设置徽章数量
|
||
const setBadge = (index, count) => {
|
||
if (index >= 0 && index < tabList.length) {
|
||
tabList[index].badge = count;
|
||
}
|
||
};
|
||
|
||
// 显示红点
|
||
const showRedDot = (index) => {
|
||
if (index >= 0 && index < tabList.length) {
|
||
tabList[index].showRedDot = true;
|
||
}
|
||
};
|
||
|
||
// 隐藏红点
|
||
const hideRedDot = (index) => {
|
||
if (index >= 0 && index < tabList.length) {
|
||
tabList[index].showRedDot = false;
|
||
}
|
||
};
|
||
|
||
// 设置当前tab
|
||
const setCurrentTab = (index) => {
|
||
if (index >= 0 && index < tabList.length) {
|
||
currentTab.value = index;
|
||
}
|
||
};
|
||
|
||
// 获取当前tab信息
|
||
const getCurrentTab = () => {
|
||
return {
|
||
index: currentTab.value,
|
||
item: tabList[currentTab.value]
|
||
};
|
||
};
|
||
|
||
// 更新tabbar数据
|
||
const updateTabList = (newTabList) => {
|
||
if (Array.isArray(newTabList)) {
|
||
tabList.splice(0, tabList.length, ...newTabList);
|
||
}
|
||
};
|
||
|
||
// 暴露方法给父组件
|
||
defineExpose({
|
||
setBadge,
|
||
showRedDot,
|
||
hideRedDot,
|
||
setCurrentTab,
|
||
getCurrentTab,
|
||
updateTabList,
|
||
currentTab,
|
||
tabList
|
||
});
|
||
|
||
// 页面加载时获取当前页面路径
|
||
onLoad(() => {
|
||
nextTick(() => {
|
||
const pages = getCurrentPages();
|
||
const currentPage = pages[pages.length - 1];
|
||
const currentPath = '/' + currentPage.route;
|
||
|
||
// 根据当前页面路径设置选中的tab
|
||
const tabIndex = tabList.findIndex(item => item.pagePath === currentPath);
|
||
if (tabIndex !== -1) {
|
||
currentTab.value = tabIndex;
|
||
}
|
||
});
|
||
});
|
||
|
||
// 页面显示时更新状态
|
||
onShow(() => {
|
||
// 可以在这里更新徽章数量等状态
|
||
console.log('Tabbar onShow - 当前tab:', currentTab.value);
|
||
});
|
||
|
||
// 组件挂载后
|
||
onMounted(() => {
|
||
console.log('Tabbar组件已挂载');
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.custom-tabbar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
overflow-x: hidden;
|
||
height: 120rpx;
|
||
background-color: #ffffff;
|
||
display: flex;
|
||
justify-content: space-around;
|
||
align-items: center;
|
||
border-top: 2rpx solid #f0f0f0;
|
||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.1);
|
||
z-index: 999;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
|
||
.tabbar-item {
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: 1;
|
||
height: 100%;
|
||
transition: all 0.3s ease;
|
||
}
|
||
.img{
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
}
|
||
.icon-container {
|
||
width:100%;
|
||
overflow-x: hidden;
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.badge {
|
||
position: absolute;
|
||
top: -8rpx;
|
||
right: -8rpx;
|
||
z-index: 10;
|
||
}
|
||
|
||
.red-dot {
|
||
position: absolute;
|
||
top: -4rpx;
|
||
right: -4rpx;
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
background-color: #ff0000;
|
||
border-radius: 50%;
|
||
border: 2rpx solid #ffffff;
|
||
z-index: 10;
|
||
}
|
||
|
||
.tab-text {
|
||
font-size: 20rpx;
|
||
color: #999999;
|
||
transition: all 0.3s ease;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.tab-text.active {
|
||
color: #8B2316;
|
||
font-weight: 600;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.active-indicator {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 40rpx;
|
||
height: 4rpx;
|
||
background: linear-gradient(90deg, #007aff, #0056b3);
|
||
border-radius: 2rpx;
|
||
animation: slideIn 0.3s ease;
|
||
}
|
||
|
||
@keyframes slideIn {
|
||
from {
|
||
width: 0;
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
width: 40rpx;
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* 点击效果 */
|
||
.tabbar-item:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 750rpx) {
|
||
.custom-tabbar {
|
||
height: 100rpx;
|
||
}
|
||
|
||
.tab-text {
|
||
font-size: 18rpx;
|
||
}
|
||
}
|
||
|
||
/* 深色模式支持 */
|
||
@media (prefers-color-scheme: dark) {
|
||
.custom-tabbar {
|
||
background-color: #1c1c1e;
|
||
border-top-color: #2c2c2e;
|
||
}
|
||
|
||
.tab-text {
|
||
color: #8e8e93;
|
||
}
|
||
|
||
.tab-text.active {
|
||
color: #0a84ff;
|
||
}
|
||
}
|
||
</style>
|