Initial commit

This commit is contained in:
tanxin
2026-10-29 10:08:39 +08:00
commit 4c6243a274
124 changed files with 17359 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
## 1.0.2(2026-07-16)
- 修复微信小程序滚动内容穿透底部 tab-bar
## 1.0.1(2026-07-07)
- 优化组件的宽度计算方式
## 1.0.0(2026-04-30)
- 支持 uni-tab-bar 组件
@@ -0,0 +1,139 @@
<template>
<view class="uni-tab-list-root" :class="tabBarRootClass">
<slot></slot>
</view>
</template>
<script setup lang="ts">
/**
* 底部选项卡栏
* @description 底部选项卡栏组件
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
const tabContentHeightFull = inject<Ref<boolean>>('uniTabContentHeightFull', ref(false))
const changeTab = inject<(index: number) => void>('uniTabBarChangeTab', (index: number) => {})
const itemCount = ref<number>(0)
const hasMidButtonItem = ref<boolean>(false)
const tabBarRootClass = computed(() : string => {
return tabContentHeightFull.value ? 'uni-tab-list-root-full' : 'uni-tab-list-root-normal'
})
const validMidButtonLayout = computed(() : boolean => {
return hasMidButtonItem.value == false || (itemCount.value > 0 && itemCount.value % 2 == 0)
})
function registerItem() : number {
const index = itemCount.value
itemCount.value++
return index
}
function registerMidButton(width : number, height : number) : void {
hasMidButtonItem.value = true
if (width <= 0) {
console.warn('uni-tab-midbutton: width 必须大于 0')
}
if (height <= 0) {
console.warn('uni-tab-midbutton: height 必须大于 0')
}
}
function syncOccupiedHeight() : void {
if (hasMidButtonItem.value && validMidButtonLayout.value == false) {
console.warn('uni-tab-bar: mid-button 需要偶数个普通 uni-tab-item,才能保持居中均分布局')
}
}
onMounted(() => {
syncOccupiedHeight()
})
watch(() : number => itemCount.value, () => {
syncOccupiedHeight()
})
watch(() : boolean => hasMidButtonItem.value, () => {
syncOccupiedHeight()
})
provide('uniTabListRegisterItem', registerItem)
provide('uniTabListRegisterMidButton', registerMidButton)
provide('uniTabListChangeTab', changeTab)
</script>
<style>
.uni-tab-list-root {
display: flex;
flex-direction: row;
width: 100%;
height: var(--uni-tab-bar-height, 50px);
flex-shrink: 0;
box-sizing: content-box;
padding-bottom: var(--uni-safe-area-inset-bottom);
justify-content: flex-start;
align-items: flex-end;
overflow: visible;
border-top-width: 0.5px;
border-top-color: rgba(0,0,0,0.33);
border-top-style: solid;
}
.uni-tab-list-root-normal {
position: relative;
z-index: 10;
background-color: #ffffff;
}
/* #ifdef MP-ALIPAY */
.uni-tab-list-root-normal {
position: fixed;
left: 0;
right: 0;
bottom: 0;
}
/* #endif */
.uni-tab-list-root-full {
position: absolute;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
background-color: #ffffff;
}
</style>
@@ -0,0 +1,104 @@
<template>
<view v-if="hasRendered" class="uni-tab-content-root"
:style="contentStyle">
<slot></slot>
</view>
</template>
<script setup lang="ts">
/**
* 选项卡内容
* @description 选项卡内容组件
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
// 从父组件注入数据和方法
const currentIndex = inject<Ref<number>>('uniTabBarCurrentIndex', ref(0))
const registerContent = inject<() => number>('uniTabBarRegisterContent', () => 0)
const tabContentHeightFull = inject<Ref<boolean>>('uniTabContentHeightFull', ref(false))
// 当前 content 的索引
const contentIndex = ref<number>(-1)
// 是否已经渲染过(按需加载)
const hasRendered = ref<boolean>(false)
// 组件挂载时分配索引
onMounted(() => {
contentIndex.value = registerContent()
// 如果是第一个激活的 tab-content,立即渲染
if (contentIndex.value == currentIndex.value) {
hasRendered.value = true
}
})
// 计算是否激活
const isActive = computed((): boolean => {
return currentIndex.value == contentIndex.value
})
// 监听激活状态,首次激活时渲染
watch((): boolean => {
return isActive.value
}, (newValue: boolean) => {
if (newValue && hasRendered.value == false) {
hasRendered.value = true
}
})
const contentStyle = computed(() => {
const active = isActive.value
return {
visibility: active ? 'visible' : 'hidden',
position: active ? 'relative' : 'absolute',
left: active ? 'auto' : '0px',
top: active ? 'auto' : '0px',
right: active ? 'auto' : '0px',
bottom: active ? 'auto' : (tabContentHeightFull.value ? '0px' : 'auto')
}
})
</script>
<style>
.uni-tab-content-root {
flex: 1;
min-height: 0px;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
z-index: 0;
}
</style>
@@ -0,0 +1,187 @@
<template>
<view class="uni-tab-list-item-root" @click.stop="handleClick">
<slot></slot>
<text v-if="props.badgeText != null && props.badgeText != '0' && props.badgeText != ''" class="uni-tab-list-item-badge-position uni-tab-list-item-badge-text" :class="props.badgeClass">{{ props.badgeText }}</text>
<view v-else-if="props.badgeText != null && props.badgeText != '0'" class="uni-tab-list-item-badge-position uni-tab-list-item-badge-dot" :class="props.badgeClass"></view>
</view>
</template>
<script setup lang="ts">
/**
* 选项卡项
* @description 选项卡项组件
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
defineOptions({
externalClasses: ['badge-class']
})
// 定义组件属性
type Props = {
/**
* @description badge 的内容
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
badgeText?: string,
/**
* @description badge 的样式类
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
badgeClass?: string.ClassString,
}
const props = withDefaults(defineProps<Props>(), {
badgeClass: ''
})
const registerItem = inject<() => number>('uniTabListRegisterItem', () => 0)
const changeTab = inject<(index: number) => void>('uniTabListChangeTab', (index: number) => {})
const itemIndex = ref<number>(-1)
onMounted(() => {
itemIndex.value = registerItem()
})
function handleClick() : void {
if (itemIndex.value >= 0) {
changeTab(itemIndex.value)
}
}
</script>
<style>
.uni-tab-list-item-root {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
overflow: visible;
}
.uni-tab-list-item-badge-position {
position: absolute;
top: 7px;
left: 50%;
transform: translate(25%, -50%);
}
.uni-tab-list-item-badge-text {
background-color: rgb(225, 80, 80);
min-width: 14px;
height: 14px;
line-height: 14px;
border-radius: 7px;
font-size: 10px;
color: white;
text-align: center;
white-space: nowrap;
}
.uni-tab-list-item-badge-dot {
background-color: rgb(225, 80, 80);
width: 8px;
height: 8px;
border-radius: 4px;
margin-left: 7px;
}
</style>
@@ -0,0 +1,159 @@
<template>
<view :style="{ width: props.width + 'px', height: props.height + 'px' }" class="uni-tab-list-item-midbutton-root">
<view class="uni-tab-list-item-midbutton-slot">
<slot></slot>
</view>
</view>
</template>
<script setup lang="ts">
/**
* 选项卡中间按钮
* @description 选项卡中间悬浮按钮组件
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
type Props = {
/**
* @description 中间按钮宽度,单位 px
* @default 90
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
width?: number,
/**
* @description 中间按钮高度,单位 px
* @default 90
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
height?: number
}
const props = withDefaults(defineProps<Props>(), {
width: 90,
height: 90
})
const registerMidButton = inject<(width : number, height : number) => void>('uniTabListRegisterMidButton', (width : number, height : number) => {})
onMounted(() => {
registerMidButton(props.width, props.height)
})
</script>
<style>
.uni-tab-list-item-midbutton-root {
width: 90px;
height: 90px;
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: visible;
}
.uni-tab-list-item-midbutton-slot {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: visible;
}
</style>
@@ -0,0 +1,222 @@
<template>
<view class="uni-tab-bar-root"> <!-- 如果 tab 是一个非全屏的组件,需要套一层。如果是全屏场景,这一层可以省略 -->
<slot></slot>
</view>
</template>
<script setup lang="ts">
/**
* 选项卡
* @description 选项卡容器组件
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
type Props = {
/**
* @description 当前激活的选项卡索引
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
activeIndex: number,
/**
* @description tab-content 的高度是否通到tab-bar下面
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
tabContentHeightFull?: boolean
}
// 定义组件属性
const props = withDefaults(defineProps<Props>(), {
tabContentHeightFull: false
})
// 定义事件
const emit = defineEmits<{
/**
* @description 选项卡变化时触发,参数为当前激活的选项卡索引,类型为 number
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
change: [index: number]
}>()
// 当前激活的索引
const currentIndex = ref<number>(props.activeIndex)
const tabContentHeightFull = ref<boolean>(props.tabContentHeightFull)
// tab-content 的数量计数器
const contentCount = ref<number>(0)
// 监听 activeIndex 变化
watch((): number => {
return props.activeIndex
}, (newIndex: number) => {
currentIndex.value = newIndex
})
watch(() : boolean => {
return props.tabContentHeightFull
}, () => {
tabContentHeightFull.value = props.tabContentHeightFull
})
// 切换选项卡
function changeTab(index: number): void {
if (currentIndex.value != index) {
currentIndex.value = index
emit('change', index)
}
}
// 注册 tab-content
function registerContent() : number {
const index = contentCount.value
contentCount.value++
return index
}
// 通过 provide 向子组件提供数据和方法
provide('uniTabBarCurrentIndex', currentIndex)
provide('uniTabBarChangeTab', changeTab)
provide('uniTabBarRegisterContent', registerContent)
provide('uniTabContentHeightFull', tabContentHeightFull)
</script>
<style>
.uni-tab-bar-root {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
flex: 1;
min-height: 0px;
position: relative;
overflow: visible;
}
</style>
+103
View File
@@ -0,0 +1,103 @@
{
"id": "uni-tab-bar",
"displayName": "uni-tab-bar 选项卡",
"version": "1.0.2",
"description": "底部选项卡组件集",
"isExt": true,
"extUrl": "https://ext.dcloud.net.cn/plugin?id=27849",
"examples": [
{
"name": "tab-bar-dark",
"description": "暗色风格",
"url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-dark.uvue"
},
{
"name": "tab-bar-custom",
"description": "自定义样式",
"url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-custom.uvue"
},
{
"name": "tab-bar-blur",
"description": "半透明毛玻璃",
"url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-blur.uvue"
},
{
"id": "uni-tab-midbutton",
"name": "tab-bar-midbutton",
"description": "中间按钮",
"url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-midbutton.uvue"
},
{
"id": "uni-tab-midbutton",
"name": "tab-bar-midbutton-notch",
"description": "中间按钮镂空",
"url": "https://gitcode.com/dcloud/hello-uni-app-x/blob/prod_alpha/pages/uni-ui/tab-bar/tab-bar-midbutton-notch.uvue"
}
],
"keywords": [
"uni-ui",
"uniui",
"自定义选项卡",
"uni-app-x",
"tab-bar"
],
"repository": "https://gitcode.com/dcloud/uni-ui-x",
"engines": {
"HBuilderX": "^5.07",
"uni-app": "",
"uni-app-x": "^5.07"
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"type": "component-vue",
"darkmode": "x",
"i18n": "x",
"widescreen": "x",
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "√",
"aliyun": "x",
"alipay": "x"
},
"client": {
"uni-app-x": {
"web": {
"safari": "√",
"chrome": "√"
},
"app": {
"android": "√",
"ios": "√",
"harmony": "√"
},
"mp": {
"weixin": "√"
}
}
}
}
}
}
+93
View File
@@ -0,0 +1,93 @@
自定义tab-bar组件, 包括一组前端组件:
- `uni-tab`:最外层容器,负责激活索引、内容区高度和切换事件
- `uni-tab-bar`:底部 tab 容器,负责底栏布局、安全区和中间按钮占位
- `uni-tab-item`:普通 tab 项,负责插槽展示和 badge
- `uni-tab-midbutton`:中间悬浮按钮占位容器
- `uni-tab-content`:上方内容区域容器,负责内容注册、懒渲染和显示切换
每个选项卡内容,不再是页面,而变成了uni-tab-content组件的子组件。
自定义tab-bar组件的使用方式是纯前端的、也是跨平台的。这与pages.json中的tabBar完全不同:
1. pages.json中的tabBar相关的api无法应用于自定义tab-bar,比如uni.switchTab、uni.showTabBar、uni.hideTabBar、uni.showTabBarRedDot、uni.hideTabBarRedDot、uni.setTabBarBadge、uni.removeTabBarBadge、uni.setTabBarStyle、uni.setTabBarItem。
2. pages.json中的tabBar页面,生命周期仍然是页面生命周期。但自定义tab-bar的uni-tab-content内容不触发页面生命周期,只触发组件生命周期。
当然,自定义tab-bar组件拥有更强的功能、更灵活的控制方式。比如下凹式midbutton。在uni-app x的非小程序平台中,性能并不会比pages.json中的tabBar差。
### 基础用法
```vue
<template>
<uni-tab style="flex: 1" :active-index="activeIndex" @change="handleChange">
<uni-tab-content>
<scroll-view style="flex: 1">
<view>
<text>内容一</text>
</view>
</scroll-view>
</uni-tab-content>
<uni-tab-content>
<view>
<text>内容二</text>
</view>
</uni-tab-content>
<uni-tab-bar>
<uni-tab-item>
<text>首页</text>
</uni-tab-item>
<uni-tab-item badge-text="8">
<text>消息</text>
</uni-tab-item>
</uni-tab-bar>
</uni-tab>
</template>
```
底栏主体高度默认为 `50px`。如果需要自定义,请在 `uni-tab` 上设置 `--uni-tab-bar-height` CSS 变量:
```vue
<uni-tab style="flex: 1; --uni-tab-bar-height: 40px" :active-index="activeIndex" @change="handleChange">
...
</uni-tab>
```
### 组件 API
#### uni-tab
##### 事件
| 事件名 | 返回参数 | 说明 |
| --- | --- | --- |
| `change` | `index: number` | 点击普通 tab 项切换后触发,返回新的激活索引 |
#### uni-tab-bar
无专用属性和API,可直接通过 `class` / `style` 自定义底栏外观。
#### uni-tab-content
无专用属性和API,可直接通过 `class` / `style` 自定义外观。
多个选项卡内容揉在一个uni-tab中比较乱,一般推荐uni-tab-content下放一个子组件,子组件作为独立uvue文件。
各个uni-tab-content的子组件,在显示和隐藏的没有生命周期。
如需监听显示/隐藏需要通过provide/inject方式从外层传入响应式变量activeIndex,然后在子组件中watch这个activeIndex。
### 使用注意事项
1. `uni-tab` 所在的页面不应该滚动,应该在pages.json中配置页面style为`disableScroll:true`。
2. 屏幕变化适配,Android需HBuilderX 5.09+支持。之前版本组件里无法监听页面的onResize。
3. `uni-tab-content` 与 `uni-tab-item` 的数量必须一致,顺序也必须一一对应,否则切换后的内容会错位。
4. `uni-tab` 默认占满页面的宽度和高度,即 `style="flex: 1"`
5. 自定义底栏高度时,请在 `uni-tab` 上设置带长度单位的 `--uni-tab-bar-height` CSS 变量,例如 `--uni-tab-bar-height: 40px`。底栏和需要显式计算高度的内容区会共用该变量。
6. 如果底栏存在镂空、下凹或透明边缘,需要让底部内容透出来,请在 `uni-tab` 上开启 `tab-content-height-full`,此时内容区会延伸到 `uni-tab-bar` 下方。
7. `uni-tab-content` 采用“首次激活再渲染”的策略;第一次切入前不会创建实例,切走后只隐藏不销毁,适合保留 tab 内部状态。
8. `uni-tab-item` 只负责普通 tab 项注册与展示,不提供单独点击事件;请统一监听 `uni-tab` 的 `change`。
9. 使用 `uni-tab-midbutton` 时,中间按钮左右两侧的普通 `uni-tab-item` 数量需要相等,也就是普通项总数必须为偶数,才能保持居中均分布局。
10. `uni-tab-midbutton` 只负责预留中间按钮位置,不参与激活索引计算;中间按钮点击行为需要在插槽内部自行处理。
11. `badge-text` 传空字符串时显示红点,传 `'0'` 时不显示;如果需要自定义 badge 外观,请使用 `badge-class` 覆盖样式。
12. `uni-tab-bar`、`uni-tab-item`、`uni-tab-content` 的根节点都支持直接挂 `class` / `style`,推荐把视觉差异放在页面侧处理,不要改组件内部逻辑。
13. 微信小程序提供了一种webview方式渲染的底部tabBar方式,来进行tabBar自定义。这种方式仅微信小程序支持,与本组件无关。
+10
View File
@@ -0,0 +1,10 @@
## 中间凸起
非下沉的,中间凸起的midbutton页面,缺少一个平滑的过渡曲线
## 左右滑
Android常见,微信
避免和页面内的内容冲突
## tab-bar高斯模糊
等view支持高斯模糊的css后,补一个示例页面即可