127 lines
2.6 KiB
Vue
127 lines
2.6 KiB
Vue
<template>
|
|
<view class="app-tabbar">
|
|
<view class="tabbar-inner">
|
|
<view
|
|
v-for="tab in list"
|
|
:key="tab.name"
|
|
class="tab-item"
|
|
:class="{ active: current === tab.name }"
|
|
@tap="onTap(tab.name)"
|
|
>
|
|
<FaIcon
|
|
:name="tab.icon"
|
|
:color="current === tab.name ? '#2563eb' : '#94a3b8'"
|
|
:size="21"
|
|
/>
|
|
<text class="tab-text">{{ tab.text }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="safe-area" />
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
/**
|
|
* 底部 tabbar。
|
|
*
|
|
* variant = 'route':独立页模式。每个子系统(crm / erp / oa / cms / toolbox)有独立的页面组,
|
|
* 通过 group(子系统目录名)解析路由并 redirectTo 跳转;导航页 nav.vue 不使用本组件。
|
|
* variant = 'switch':页内模式。用于 pages/sub/tabs.vue,切换时只 emit change 由父页控制
|
|
* 内容区显隐,避免页面栈堆积、保留各页签状态;tabs 可由父页传入。
|
|
*/
|
|
const props = defineProps({
|
|
group: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
current: {
|
|
type: String,
|
|
default: 'home'
|
|
},
|
|
variant: {
|
|
type: String,
|
|
default: 'route'
|
|
},
|
|
tabs: {
|
|
type: Array,
|
|
default: null
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['change'])
|
|
|
|
const DEFAULT_TABS = [
|
|
{ name: 'home', text: '概览', icon: 'gauge' },
|
|
{ name: 'functions', text: '功能', icon: 'table-cells' },
|
|
{ name: 'todo', text: '待办', icon: 'circle-check' },
|
|
{ name: 'mine', text: '我的', icon: 'user' }
|
|
]
|
|
|
|
const list = computed(() => (props.tabs && props.tabs.length ? props.tabs : DEFAULT_TABS))
|
|
|
|
function onTap(name) {
|
|
if (name === props.current) return
|
|
if (props.variant === 'switch') {
|
|
emit('change', name)
|
|
return
|
|
}
|
|
// 子系统内部页面与 tab 一一对应,用 redirectTo 避免页面栈堆积
|
|
uni.redirectTo({ url: `/pages/${props.group}/${name}/${name}` })
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/src/styles/theme.scss';
|
|
|
|
.app-tabbar {
|
|
flex-shrink: 0;
|
|
background: $color-card;
|
|
border-top: 1rpx solid $color-border;
|
|
box-shadow: 0 -4rpx 20rpx rgba(31, 43, 72, 0.05);
|
|
}
|
|
|
|
.tabbar-inner {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-around;
|
|
padding: 12rpx 0 8rpx;
|
|
}
|
|
|
|
.tab-item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 6rpx;
|
|
padding: 6rpx 0;
|
|
transition: transform 0.18s ease;
|
|
|
|
&.active {
|
|
.tab-text {
|
|
color: $color-primary;
|
|
font-weight: 600;
|
|
}
|
|
|
|
:deep(.fa-icon) {
|
|
transform: translateY(-2rpx);
|
|
}
|
|
}
|
|
|
|
&:active {
|
|
transform: scale(0.94);
|
|
}
|
|
}
|
|
|
|
.tab-text {
|
|
font-size: 22rpx;
|
|
line-height: 1.2;
|
|
color: $color-text-muted;
|
|
}
|
|
|
|
.safe-area {
|
|
height: constant(safe-area-inset-bottom);
|
|
height: env(safe-area-inset-bottom);
|
|
}
|
|
</style> |