添加bar组件

This commit is contained in:
2025-11-05 17:34:23 +08:00
parent 7314f23529
commit 3cb63ffca7
223 changed files with 23185 additions and 2 deletions

View File

@@ -0,0 +1,120 @@
@import '@/uni_modules/lime-style/index.scss';
@import '@/uni_modules/lime-style/mixins/ellipsis.scss';
/* #ifdef uniVersion >= 4.75 */
$use-css-var: true;
/* #endif */
$item: #{$prefix}-tabbar-item;
// 尺寸变量
$tabbar-height: create-var(tabbar-height, 40px);
$tabbar-font-size: create-var(tabbar-font-size, $font-size-md);
$tabbar-min-font-size: create-var(tabbar-font-size, $font-size-sm);
// 颜色变量
$tabbar-color: create-var(tabbar-color, $text-color-1);
$tabbar-bg-color: create-var(tabbar-bg-color, $bg-color-container);
$tabbar-active-color: create-var(tabbar-active-color, $primary-color);
$tabbar-active-bg-color: create-var(tabbar-active-bg-color, $primary-color-1);
// 间距变量(新增)
$tabbar-item-margin-x: create-var(tabbar-item-margin-x, 0); // 水平margin
$tabbar-item-margin-y: create-var(tabbar-item-margin-y, 8px); // 垂直margin
$tabbar-item-padding-x: create-var(tabbar-item-padding-x, 12px); // 水平padding
$tabbar-item-padding-y: create-var(tabbar-item-padding-y, 0); // 垂直padding
$tabbar-item-crowded-padding-x: create-var(tabbar-item-crowded-padding-x, 8px); // 拥挤状态水平padding
/* #ifdef MP */
:host {
flex: 1;
}
/* #endif */
.#{$item} {
/* #ifndef UNI-APP-X && APP */
box-sizing: border-box;
user-select: none;
display: flex;
/* #endif */
flex: 1;
height: $tabbar-height;
position: relative;
// margin: $tabbar-item-margin-y $tabbar-item-margin-x; // 使用拆分后的margin变量
@include margin($tabbar-item-margin-y $tabbar-item-margin-x);
background-color: $tabbar-bg-color;
// padding: $tabbar-item-padding-y $tabbar-item-padding-x; // 使用拆分后的padding变量
@include padding($tabbar-item-padding-y $tabbar-item-padding-x);
overflow: visible;
&__icon {
&-wrap {
overflow: visible;
}
}
&--crowded {
// padding: $tabbar-item-padding-y $tabbar-item-crowded-padding-x; // 拥挤状态使用专用变量
@include padding($tabbar-item-padding-y $tabbar-item-padding-x);
}
&--round {
border-radius: 99px;
}
&__content {
box-sizing: border-box;
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 16rpx;
overflow: visible;
&--checked {
/* #ifndef UNI-APP-X && APP */
color: $tabbar-active-color;
/* #endif */
/* #ifndef UNI-APP-X */
.l-tabbar-item__text {
color: $tabbar-active-color;
font-weight: 700;
}
/* #endif */
}
&--disabled {
opacity: 0.5;
}
&--tag {
border-radius: 99px;
}
&--tag#{&}--checked {
background-color: $tabbar-active-bg-color;
}
}
&__icon {
color: $tabbar-color;
}
&__text {
color: $tabbar-color;
&--small {
font-size: $tabbar-min-font-size;
padding-top: 4px;
}
&--only {
font-size: $tabbar-font-size;
}
&--checked {
color: $tabbar-active-color;
font-weight: 700;
}
&--ellipsis {
@include ellipsisLn(1);
}
}
}

View File

@@ -0,0 +1,166 @@
<template>
<view class="l-tabbar-item" :class="classes">
<view :class="contentClass" :style="[contentStyle]" @click="toggle">
<view class="l-tabbar-item__icon-wrap" :style="[iconStyle]" v-if="icon != null || $slots['icon'] != null">
<template v-if="(icon != null || $slots['icon'] != null) && badgeProps?.['dot'] == true || badgeProps?.['content'] != null">
<l-badge
:dot="badgeProps?.['dot'] == true"
:max="badgeProps?.['max'] ?? 99"
:offset="badgeProps?.['offset'] ?? [0, 0]"
:content="badgeProps?.['content']">
<slot name="icon">
<l-icon class="l-tabbar-item__icon"
:class="{'l-tabbar-item__text--checked': isChecked}"
:name="icon" :size="iconSize" :color="colorStyle['color']">
</l-icon>
</slot>
</l-badge>
</template>
<template v-else-if="(icon != null || $slots['icon'] != null)">
<slot name="icon">
<l-icon class="l-tabbar-item__icon"
:class="{'l-tabbar-item__text--checked': isChecked}"
:name="icon" :size="iconSize" :color="colorStyle['color']">
</l-icon>
</slot>
</template>
</view>
<text
v-if="label != null || $slots['default'] != null"
class="l-tabbar-item__text"
:class="{
'l-tabbar-item__text--small': icon != null,
'l-tabbar-item__text--only': icon == null,
'l-tabbar-item__text--checked': isChecked,
'l-tabbar-item__text--ellipsis': ellipsis
}"
:style="[colorStyle, textStyle]">
<slot>{{label}}</slot>
</text>
<slot name="extra"></slot>
</view>
</view>
</template>
<script lang="uts" setup>
/**
* TabbarItem 底部导航项组件
* @description 用于构建Tabbar的单个导航项必须作为Tabbar的子组件使用
* <br>插件类型LTabbarItemComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-tabbar
*
* @property {Object} badgeProps 徽标配置支持uni-badge所有属性
* @property {string} icon 图标名称支持uni-icons或自定义图标
* @property {string} value 唯一标识符用于v-model绑定
* @property {string} label 文本标签
* @property {boolean} disabled 是否禁用默认false
* @property {boolean} ellipsis 是否超一行省略默认false
*/
import { TabbarProvide } from '../l-tabbar/type';
import { TabbarItemProps } from './type';
import {addUnit} from '@/uni_modules/lime-shared/addUnit'
const themeVars = inject('limeConfigProviderThemeVars', computed(()=> ({})))
const name = 'l-tabbar-item';
const props = withDefaults(defineProps<TabbarItemProps>(), {
disabled: false,
ellipsis: false
})
const instance = getCurrentInstance()!
const parent = inject<TabbarProvide|null>('limeTabbar', null);
const index = ref(0)
const currentName = computed(():string=> props.value ?? `${index.value}`)
// @ts-ignore
const crowded = computed((): boolean => parent != null && parent.children.value.length > 3)
const isChecked = computed((): boolean => currentName.value == parent?.activeValue.value);
const classes = computed(():Map<string, any>=>{
const cls = new Map<string, any>()
const split = parent?.props.split ?? false;
const shape = parent?.props.shape ?? false;
cls.set(`${name}--split`, split);
cls.set(`${name}--${shape}`, true);
cls.set(`${name}--text-only`, props.icon == null)
cls.set(`${name}--crowded`, crowded.value)
return cls
})
const contentClass = computed(():Map<string, any>=> {
const cls = new Map<string, any>()
const theme = parent?.props.theme;
cls.set(`${name}__content`, true)
cls.set(`${name}__content--checked`, isChecked.value)
cls.set(`${name}__content--disabled`, props.disabled)
cls.set(`${name}__content--${theme}`, true)
return cls
})
const contentStyle = computed(():UTSJSONObject=>{
const style = {}
const activeBgColor = parent?.props.activeBgColor;
if(activeBgColor != null && isChecked.value){
style.set('background', activeBgColor)
}
return style
})
const colorStyle = computed(():UTSJSONObject => {
const style = {}
// #ifdef APP
// 安卓 深层文本样式不生效 bug https://issues.dcloud.net.cn/pages/issues/detail?id=11930
const activeColor:any|null = parent?.props.activeColor ?? themeVars.value['tabbarActiveColor'] ?? '#3283ff'
const color:any|null = parent?.props.color ?? themeVars.value['tabbarColor'] ?? 'rgba(0,0,0,0.88)'
// #endif
// #ifndef APP
const activeColor = parent?.props.activeColor
const color = parent?.props.color
// #endif
if(activeColor != null && isChecked.value){
style.set('color', `${activeColor}`)
}
if(color != null && !isChecked.value){
style.set('color', `${color}`)
}
return style
})
const textStyle = computed(():UTSJSONObject=>{
const style = {}
const fontSize = parent?.props.fontSize
if(fontSize != null){
style.set('font-size', fontSize)
}
return style
})
// @ts-ignore
const iconSize = computed(():string => addUnit(parent?.props.iconSize ?? (props.icon !=null && instance.slots['default'] == null ? 24 : 20))!)
const iconStyle = computed(():Map<string, any> =>{
const style = new Map<string, any>()
style.set('height', iconSize.value);
return style
})
const toggle = ()=>{
if(props.disabled || isChecked.value || parent == null) return
parent.updateChild(currentName.value)
// parent!.activeValue.value = currentName.value
}
onMounted(()=>{
if(parent == null) return
parent.defaultIndex.value +=1;
index.value = parent.defaultIndex.value;
parent.children.value.push(instance.proxy! as LTabbarItemComponentPublicInstance)
})
onUnmounted(()=>{
// @ts-ignore
if(parent == null) return
const _index = parent.children.value.indexOf(instance.proxy! as LTabbarItemComponentPublicInstance);
parent.children.value.splice(_index, 1);
})
</script>
<style lang="scss">
@import './index-u';
</style>

View File

@@ -0,0 +1,166 @@
<template>
<view class="l-tabbar-item" :class="classes">
<view :class="contentClass" :style="[contentStyles]" @click="toggle">
<view class="l-tabbar-item__icon-wrap" :style="[iconStyle]" v-if="icon">
<!-- <l-badge
v-if="(icon || $slots.icon) && (badgeProps.dot || badgeProps.content)"
:dot="badgeProps.dot || false"
:max="badgeProps.max || 99"
:content="badgeProps.content || 0">
<slot icon="icon"></slot>
<l-icon :name="icon"></l-icon>
</l-badge>
<slot icon="icon" v-else-if="$slots.icon"></slot>
<l-icon v-else-if="icon" :name="icon"></l-icon> -->
<template v-if="(icon || $slots.icon) && (badgeProps.dot || badgeProps.content)" >
<l-badge
:dot="badgeProps.dot || false"
:max="badgeProps.max || 99"
:offset="badgeProps.offset || [0, 0]"
:content="badgeProps.content || 0">
<slot name="icon">
<l-icon :name="icon" ></l-icon>
</slot>
</l-badge>
</template>
<template v-else-if="icon || $slots.icon">
<slot name="icon">
<l-icon :name="icon"></l-icon>
</slot>
</template>
</view>
<view class="l-tabbar-item__text"
:class="{
'l-tabbar-item__text--small': !!icon,
'l-tabbar-item__text--only': !icon,
'l-tabbar-item__text--ellipsis': ellipsis
}">
<slot>{{label}}</slot>
</view>
<slot name="extra"></slot>
</view>
</view>
</template>
<script lang="ts">
// @ts-nocheck
/**
* TabbarItem 底部导航项组件
* @description 用于构建Tabbar的单个导航项必须作为Tabbar的子组件使用
* <br>插件类型LTabbarItemComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-tabbar
*
* @property {Object} badgeProps 徽标配置支持uni-badge所有属性
* @property {string} icon 图标名称支持uni-icons或自定义图标
* @property {string} value 唯一标识符用于v-model绑定
* @property {string} label 文本标签
* @property {boolean} disabled 是否禁用默认false
* @property {boolean} ellipsis 是否超一行省略默认false
*/
import {ref, defineComponent, computed, inject, getCurrentInstance, onUnmounted} from '@/uni_modules/lime-shared/vue';
import tabbarProps from './props'
import {addUnit} from '@/uni_modules/lime-shared/addUnit'
const name = `l-tabbar-item`
export default defineComponent({
name,
props: tabbarProps,
setup(props) {
const context = getCurrentInstance()
const { split, shape, theme, defaultIndex, activeValue, updateChild, children, activeColor, color, activeBgColor, iconSize, fontSize } = inject('tab-bar');
defaultIndex.value += 1
const currentName = props.value || defaultIndex.value
children.value.push(context.uid)
// 拥挤
const crowded = computed(() => children.value.length > 3)
const isChecked = computed(() => {
return currentName === activeValue.value;
});
const classes = computed(() => {
const cls = [`${name}--${shape.value}`];
if(split.value) {
cls.push(`${name}--split`)
}
if(!props.icon) {
cls.push(`${name}--text-only`)
}
if(crowded.value) {
cls.push(`${name}--crowded`)
}
return cls.join(' ')
// return {
// // [`${name}`]: true,
// [`${name}--split`]: split.value,
// [`${name}--text-only`]: !props.icon,
// [`${name}--crowded`]: crowded.value,
// [`${name}--${shape.value}`]: true,
// }
})
const contentClass = computed(() => {
const cls = [`${name}__content`, `${name}__content--${theme.value}`];
if(isChecked.value) {
cls.push(`${name}__content--checked`)
}
if(props.disabled) {
cls.push(`${name}__content--disabled`)
}
return cls.join(' ')
// return {
// [`${name}__content`]: true,
// [`${name}__content--checked`]: isChecked.value,
// [`${name}__content--disabled`]: props.disabled,
// [`${name}__content--${theme.value}`]: true,
// }
})
const contentStyles = computed(() => {
const style:Record<string, string> = {}
if(fontSize.value) {
style['font-size'] = fontSize.value
}
if(activeBgColor.value) {
style['background'] = activeBgColor.value
}
if(activeColor.value || color.value) {
style['color'] = isChecked.value ? activeColor.value : color.value
}
if(activeColor.value) {
style['--l-tabbar-active-color'] = activeColor.value
}
if(color.value) {
style['--l-tabbar-color'] = color.value
}
return style
})
const iconStyle = computed(() => {
const size = addUnit(iconSize.value || (props.icon && !context.slots.default ? 24 : 20))
return {
height: size,
fontSize: size
}
})
const toggle = () => {
if(props.disabled) return
updateChild(currentName);
}
onUnmounted(() => {
const index = children.value.indexOf(context.uid);
children.value.splice(index, 1);
})
return {
classes,
contentClass,
contentStyles,
iconStyle,
toggle
}
}
})
</script>
<style lang="scss">
@import './index-u';
</style>

View File

@@ -0,0 +1,28 @@
// @ts-nocheck
export default {
/** 图标右上角提示信息 */
badgeProps: {
type: Object,
default: () => ({})
},
/** 图标名称 */
icon: {
type: String,
},
/** 标识符 */
value: {
type: [String, Number],
default: null
},
disabled: {
type: Boolean,
default: false
},
label: {
type: String
},
ellipsis: {
type: Boolean,
default: false
}
};

View File

@@ -0,0 +1,26 @@
// @ts-nocheck
// #ifndef UNI-APP-X
type UTSJSONObject = Object
// #endif
export interface TabbarItemProps {
/**
* 图标右上角提示信息
*/
badgeProps?: UTSJSONObject;
/**
* 图标名称
*/
icon?: string;
/**
* 标识符
*/
value?: string;
label?: string;
disabled: boolean;
/**
* 文本超过时是否显示省略号
*/
ellipsis: boolean;
}

View File

@@ -0,0 +1,83 @@
@import '@/uni_modules/lime-style/index.scss';
@import '@/uni_modules/lime-style/mixins/hairline.scss';
/* #ifdef uniVersion >= 4.75 */
$use-css-var: true;
/* #endif */
$tabbar: #{$prefix}-tabbar;
$tabbar-bg-color: create-var(tabbar-bg-color, $bg-color-container);
$tabbar-border-color: create-var(tabbar-border-color, $border-color-1);
$tabbar-z-index: create-var(tabbar-z-index, 10);
$tabbar-round-margin-x: create-var(tabbar-round-margin-x, $spacer);
/* #ifndef UNI-APP-X && APP */
$tabbar-round-shadow: create-var(tabbar-round-shadow, $shadow-3);
/* #endif */
/* #ifdef UNI-APP-X && APP */
$tabbar-round-shadow: $shadow-3;
/* #endif */
.#{$tabbar} {
/* #ifndef UNI-APP-X */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
position: relative;
// font-size: 16px;
background-color: $tabbar-bg-color;
overflow: visible;
&--fixed {
position: fixed;
left: 0;
bottom: 0;
right: 0;
z-index: $tabbar-z-index;
}
&--round {
margin-left: $tabbar-round-margin-x;
margin-right: $tabbar-round-margin-x;
border-radius: 999px;
box-shadow: $tabbar-round-shadow;
}
/* #ifndef UNI-APP-X && APP */
&--normal#{&}--bordered {
// z-index: 1;
// border-top: 1rpx solid $tabbar-border-color;
border-top-width: 1rpx;
border-top-style: solid;
border-top-color: $tabbar-border-color;
}
/* #endif */
/* #ifndef UNI-APP-X && APP */
&--normal#{&}--bordered::before {
// z-index: 1;
@include hairline-top($color: $tabbar-border-color);
}
/* #endif */
&--normal#{&}--safe {
/* #ifdef UNI-APP-X */
padding-bottom: var(--uni-safe-area-inset-bottom);
/* #endif */
/* #ifndef UNI-APP-X */
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
/* #endif */
}
&--fixed#{&}--round#{&}--safe {
/* #ifdef UNI-APP-X */
bottom: var(--uni-safe-area-inset-bottom);
/* #endif */
/* #ifndef UNI-APP-X */
bottom: constant(safe-area-inset-bottom);
bottom: env(safe-area-inset-bottom);
/* #endif */
}
}

View File

@@ -0,0 +1,131 @@
<template>
<view ref="rootRef" :class="classes" :style="[styles, lStyle]">
<slot></slot>
</view>
<view v-if="fixed && placeholder" :style="{height: rootHeight + 'px'}"></view>
</template>
<script lang="uts" setup>
/**
* Tabbar 底部导航栏组件
* @description 用于实现应用底部导航功能,支持多种样式风格和交互效果
* <br>插件类型LTabbarComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-tabbar
*
* @property {boolean} bordered 显示外边框默认true
* @property {boolean} fixed 固定在底部默认true
* @property {boolean} safeAreaInsetBottom 适配iPhoneX底部安全区默认true
* @property {'normal' | 'round'} shape 标签栏形状(默认:"normal"
* @value normal
* @value round
* @property {boolean} split 显示分割线默认true
* @property {'normal' | 'tag'} theme 选项风格(默认:"normal"
* @value normal
* @value tag
* @property {string} value 当前选中值(兼容旧版)
* @property {string} defaultValue 默认选中值(非受控属性)
* @property {string} modelValue 当前选中值推荐使用v-model
* @property {boolean} placeholder 固定时生成占位元素默认false
* @property {string} activeColor 选中项颜色(默认:主题色)
* @property {string} color 未选中项颜色(默认:"#7d7e80"
* @property {string} lStyle 自定义样式CSS字符串
* @property {string} activeBgColor 选中项背景色
* @property {string} bgColor 背景颜色
* @property {number} zIndex 层级默认100
* @property {string} iconSize 图标尺寸支持CSS单位
* @property {string} fontSize 文字大小支持CSS单位
* @event {Function} change 选项变化时触发(返回选中值)
*/
import { addUnit } from '@/uni_modules/lime-shared/addUnit';
import { TabbarProps, TabbarProvide } from './type'
const name = 'l-tabbar'
const emit = defineEmits(['change', 'update:modelValue'])
const props = withDefaults(defineProps<TabbarProps>(), {
bordered: true,
fixed: true,
safeAreaInsetBottom: true,
shape: 'normal',
theme: 'normal',
split: true,
placeholder: true,
// #ifdef APP-ANDROID || APP-IOS
// color: 'rgba(0,0,0,0.8)',
// activeColor: '#3283ff'
// #endif
});
const innerValue = ref('0');
const activeValue = computed({
set(value: string) {
emit('change', value)
emit('update:modelValue', value)
innerValue.value = value
},
get():string {
return props.value ?? props.modelValue ?? innerValue.value
}
} as WritableComputedOptions<string>);
const defaultIndex = ref(-1)
// @ts-ignore
const children = ref<LTabbarItemComponentPublicInstance[]>([])
const rootRef = ref<UniElement|null>(null)
const rootHeight = ref(0)
// const safeAreaBottom = uni.getWindowInfo().safeAreaInsets.bottom;
const classes = computed(():Map<string, any>=>{
const cls = new Map<string, any>();
cls.set(`${name}`, true)
cls.set(`${name}--bordered`, props.bordered)
cls.set(`${name}--fixed`, props.fixed)
cls.set(`${name}--safe`, props.safeAreaInsetBottom)
cls.set(`${name}--${props.shape}`, true)
return cls
})
const styles = computed(():Map<string, any>=>{
const style = new Map<string, any>();
if(props.zIndex != null) {
style.set('z-index', props.zIndex!)
}
if(props.bgColor != null) {
style.set('background', props.bgColor!)
}
// if(props.safeAreaInsetBottom && safeAreaBottom > 0){
// const size = addUnit(safeAreaBottom)
// if (props.shape == 'normal') {
// style.set('padding-bottom', size)
// }
// if (props.fixed && props.shape == 'round') {
// style.set('bottom', size)
// }
// }
return style
})
const updateChild = (currentValue: string)=>{
activeValue.value = currentValue
}
provide('limeTabbar', {
props,
defaultIndex,
activeValue,
updateChild,
children
} as TabbarProvide)
onMounted(()=>{
if (!props.placeholder) return;
nextTick(()=>{
rootRef.value?.getBoundingClientRectAsync()?.then(res => {
rootHeight.value = res.height
})
// rootHeight.value = rootRef.value?.offsetHeight??0
})
})
</script>
<style lang="scss">
@import './index-u';
</style>

View File

@@ -0,0 +1,115 @@
<template>
<view>
<view class="l-tabbar" :class="tabBarClass" :style="[styles]">
<slot></slot>
</view>
<view v-if="fixed && placeholder" :style="{height: rootHeight + 'px'}"></view>
</view>
</template>
<script lang="ts">
// @ts-nocheck
/**
* Tabbar 底部导航栏组件
* @description 用于实现应用底部导航功能,支持多种样式风格和交互效果
* <br>插件类型LTabbarComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-tabbar
*
* @property {boolean} bordered 显示外边框默认true
* @property {boolean} fixed 固定在底部默认true
* @property {boolean} safeAreaInsetBottom 适配iPhoneX底部安全区默认true
* @property {'normal' | 'round'} shape 标签栏形状(默认:"normal"
* @value normal
* @value round
* @property {boolean} split 显示分割线默认true
* @property {'normal' | 'tag'} theme 选项风格(默认:"normal"
* @value normal
* @value tag
* @property {string} value 当前选中值(兼容旧版)
* @property {string} defaultValue 默认选中值(非受控属性)
* @property {string} modelValue 当前选中值推荐使用v-model
* @property {boolean} placeholder 固定时生成占位元素默认false
* @property {string} activeColor 选中项颜色(默认:主题色)
* @property {string} color 未选中项颜色(默认:"#7d7e80"
* @property {string} lStyle 自定义样式CSS字符串
* @property {string} activeBgColor 选中项背景色
* @property {string} bgColor 背景颜色
* @property {number} zIndex 层级默认100
* @property {string} iconSize 图标尺寸支持CSS单位
* @property {string} fontSize 文字大小支持CSS单位
* @event {Function} change 选项变化时触发(返回选中值)
*/
import { ref, defineComponent, computed, Ref, provide, toRefs, onMounted, getCurrentInstance } from '@/uni_modules/lime-shared/vue';
import TabbarProps from './props'
import { getRect } from '@/uni_modules/lime-shared/getRect'
const name = `l-tabbar`
export default defineComponent({
// name,
props: TabbarProps,
emits: ['change', 'input', 'update:modelValue'],
setup(props, { emit }) {
const instance = getCurrentInstance()!.proxy!
const innerValue = ref('0');
const activeValue = computed({
set(value: string) {
innerValue.value = value
emit('change', value)
emit('update:modelValue', value)
// #ifdef VUE2
emit('input', value)
// #endif
},
get():string {
return props.value ?? props.modelValue ?? innerValue.value
}
});
const defaultIndex : Ref<number> = ref(-1);
const rootHeight = ref(0);
const children = ref([])
const tabBarClass = computed(() => [
// `${name}`,
{
[`${name}--bordered`]: props.bordered,
[`${name}--fixed`]: props.fixed,
[`${name}--safe`]: props.safeAreaInsetBottom,
},
`${name}--${props.shape}`,
]);
const styles = computed(()=>{
const style:Record<string, string> = {};
if(props.zIndex) {
style['z-index'] = props.zIndex!
}
if(props.bgColor) {
style['background'] = props.bgColor!
}
return style
})
const updateChild = (currentValue: number | string) => {
activeValue.value = currentValue;
};
onMounted(() => {
if(!(props.placeholder && props.fixed)) return;
getRect(`.${name}`, instance).then(res => {
rootHeight.value = res.height
})
})
provide('tab-bar', {
...toRefs(props),
defaultIndex,
activeValue,
updateChild,
children
});
return {
rootHeight,
styles,
tabBarClass
}
}
})
</script>
<style lang="scss">
@import './index-u';
</style>

View File

@@ -0,0 +1,94 @@
// @ts-nocheck
export default {
/** 是否显示外边框 */
bordered: {
type: Boolean,
default: true,
},
/** 是否固定在底部 */
fixed: {
type: Boolean,
default: true,
},
/** 是否为 iPhoneX 留出底部安全距离 */
safeAreaInsetBottom: {
type: Boolean,
default: true,
},
/** 当前选中标签的索引 */
value: {
type: [String, Number],
default: undefined,
},
modelValue: {
type: [String, Number],
default: undefined,
},
/** 当前选中标签的索引,非受控属性 */
defaultValue: {
type: [String, Number],
default: undefined,
},
/** 固定在底部时,是否在标签位置生成一个等高的占位元素 */
placeholder: {
type: Boolean,
default: true,
},
/** 标签栏的形状 */
shape: {
type: String,
default: 'normal',
validator(val) {
if (!val) return true;
return ['normal', 'round'].includes(val);
},
},
/** 选项风格 */
theme: {
type: String,
default: 'normal',
validator(val) {
if (!val) return true;
return ['normal', 'tag'].includes(val);
},
},
/** 是否需要分割线 */
split: {
type: Boolean,
default: false,
},
/** 选中标签的颜色 */
activeColor: {
type: String,
default: null,
},
/** 未选中标签的颜色 */
color: {
type: String,
default: null,
},
lStyle: {
type: [String, Object],
default: null,
},
bgColor: {
type: String,
default: null,
},
activeBgColor: {
type: String,
default: null,
},
zIndex: {
type: Number,
default: null
},
iconSize: {
type: String,
default: null
},
fontSize: {
type: String,
default: null
}
}

View File

@@ -0,0 +1,68 @@
// @ts-nocheck
// #ifndef APP-ANDROID
import type { ComputedRef } from '@/uni_modules/lime-shared/vue';
type ComputedRefImpl<T> = ComputedRef<T>
// #endif
export interface TabbarProps {
/**
* 是否显示外边框 true
*/
bordered : boolean;
/**
* 是否固定在底部 true
*/
fixed : boolean;
/**
* 是否为 iPhoneX 留出底部安全距离 true
*/
safeAreaInsetBottom : boolean;
/**
* 标签栏的形状 normal
*/
shape : 'normal' | 'round';
/**
* 是否需要分割线 true
*/
split : boolean;
/**
* 选项风格 normal
*/
theme : 'normal' | 'tag';
/**
* 当前选中标签的索引
*/
value ?: string;
/**
* 当前选中标签的索引,非受控属性
*/
defaultValue ?: string;
/**
* 当前选中标签的索引
*/
modelValue ?: string;
/** 固定在底部时,是否在标签位置生成一个等高的占位元素 */
placeholder:boolean;
// #ifdef APP-ANDROID || APP-IOS
activeColor ?: string;
color ?: string;
// #endif
// #ifndef APP-ANDROID || APP-IOS
activeColor ?: string;
color ?: string;
// #endif
lStyle ?: string|UTSJSONObject;
activeBgColor ?: string;
bgColor?: string
zIndex?:number;
iconSize?: string;
fontSize?: string;
}
export type TabbarProvide = {
defaultIndex : Ref<number>,
props: LTabbarComponentPublicInstance,
activeValue: ComputedRefImpl<string>,
updateChild: (value: string) => void,
children: Ref<LTabbarItemComponentPublicInstance[]>
}

View File

@@ -0,0 +1,249 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">标签栏</text>
<text class="demo-block__desc-text">用于在不同功能模块之间进行快速切换,位于页面底部</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text large">组件类型</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text">基础{{active}}</text>
<view class="demo-block__body">
<l-tabbar theme="tag" shape="normal" l-style="border-top-right-radius:20px; border-top-left-radius:20px" v-model="active" :fixed="false" :safeAreaInsetBottom="false" @change="change">
<l-tabbar-item icon="home">首页</l-tabbar-item>
<l-tabbar-item icon="app">应用</l-tabbar-item>
<l-tabbar-item icon="chat">聊天</l-tabbar-item>
<l-tabbar-item icon="user">我的</l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">纯文本</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item label="首页"></l-tabbar-item>
<l-tabbar-item label="应用"></l-tabbar-item>
<l-tabbar-item label="聊天"></l-tabbar-item>
<l-tabbar-item>我的</l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">图标加文字标签栏</text>
<view class="demo-block__body">
<l-tabbar v-model="active1" :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item icon="home" value="home" :badgeProps="badgeProps">首页</l-tabbar-item>
<l-tabbar-item icon="app" value="app" :badgeProps="badgeProps">应用</l-tabbar-item>
<l-tabbar-item icon="chat" value="chat">聊天</l-tabbar-item>
<l-tabbar-item icon="user" value="user">我的</l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">自定义图标{{active2}}</text>
<view class="demo-block__body">
<l-tabbar v-model="active2" :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item value="home" ellipsis>
<text class="text">首页首页首页首页首页首页</text>
<template #icon>
<image style="width: 20px;height: 20px;" v-show="active2 == 'home'"
:src="activeImg"></image>
<image style="width: 20px;height: 20px;" v-show="active2 != 'home'"
:src="inactiveImg"></image>
</template>
</l-tabbar-item>
<l-tabbar-item icon="app" value="app">应用</l-tabbar-item>
<l-tabbar-item icon="chat" value="chat">聊天</l-tabbar-item>
<l-tabbar-item icon="user" value="user">我的</l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">纯图标</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">颜色</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" activeColor="red" color="#999">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">禁用</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat" :disabled="true"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">监听切换事件</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" @change="change">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text large">组件样式</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text">默认样式</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" theme="tag" @change="change">
<l-tabbar-item icon="home" :badgeProps="{ content: 16, max:5 }"
value="home">首页</l-tabbar-item>
<l-tabbar-item icon="app" :badgeProps="badgeProps" value="app">应用</l-tabbar-item>
<l-tabbar-item icon="chat" :badgeProps="{ content: 'New', offset: [10,-2] }"
value="chat">聊天</l-tabbar-item>
<l-tabbar-item icon="user" :badgeProps="{ content: '···' }"
value="user">我的</l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">悬浮胶囊标签栏</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" theme="tag" shape="round">
<l-tabbar-item icon="home" value="home" :badgeProps="badgeProps">首页</l-tabbar-item>
<l-tabbar-item icon="app" value="app">应用</l-tabbar-item>
<l-tabbar-item icon="chat" value="chat">聊天</l-tabbar-item>
<l-tabbar-item icon="user" value="user">我的</l-tabbar-item>
</l-tabbar>
</view>
</view>
</view>
</view>
<text style="lines:1; width:40px">悬浮胶囊标签栏</text>
</view>
</view>
</template>
<script setup>
const badgeProps = { dot: true }
const active = ref('0')
const active1 = ref('home')
const active2 = ref('home')
const activeImg = 'https://fastly.jsdelivr.net/npm/@vant/assets/user-active.png'
const inactiveImg = 'https://fastly.jsdelivr.net/npm/@vant/assets/user-inactive.png'
const change = (value : string) => {
nextTick(()=>{
console.log('value', active.value, value)
})
}
setTimeout(()=>{
active.value = '2'
},1000)
</script>
<style lang="scss">
$card-bg-color: var(--doc-card-bg-color, white);
// $page-bg-color: var(--doc-page-bg-color, #f5f5f5);
$title-color: var(--doc-title-color, #000000E6);
$summary-color: var(--doc-summary-color, #00000099);
.text {
color: $summary-color;
font-size: 14px;
}
.badge {
position: absolute;
transform-origin: 100%;
background-color: #FF4D4F;
overflow: visible;
border-radius: 999px;
}
.demo-block {
margin: 32px 0 0;
overflow: visible;
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: $summary-color;
font-weight: 400;
font-size: 14px;
line-height: 16px;
padding-left: 20px;
&.large {
color: $title-color;
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: $title-color;
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: $summary-color;
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
padding-left: 20px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,207 @@
<template>
<view class="demo-block">
<text class="demo-block__title-text ultra">标签栏</text>
<text class="demo-block__desc-text">用于在不同功能模块之间进行快速切换位于页面底部</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text large">组件类型</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text">纯文本</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item label="首页"></l-tabbar-item>
<l-tabbar-item label="应用"></l-tabbar-item>
<l-tabbar-item>聊天</l-tabbar-item>
<l-tabbar-item>我的</l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">图标加文字标签栏</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item icon="home" value="home" :badgeProps="badgeProps"></l-tabbar-item>
<l-tabbar-item icon="app" value="app" :badgeProps="badgeProps"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">纯图标</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">颜色</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" activeColor="red" color="#999">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">禁用</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat" :disabled="true"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">监听切换事件</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" @change="change">
<l-tabbar-item icon="home" value="home"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text large">组件样式</text>
<view class="demo-block__body">
<view class="demo-block">
<text class="demo-block__title-text">默认样式</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" theme="tag" @change="change">
<l-tabbar-item icon="home" :badgeProps="{ content: 16, max:5 }"
value="home"></l-tabbar-item>
<l-tabbar-item icon="app" :badgeProps="badgeProps" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" :badgeProps="{ content: 'New', offset: [10,-2] }"
value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" :badgeProps="{ content: '···' }"
value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
<view class="demo-block">
<text class="demo-block__title-text">悬浮胶囊标签栏</text>
<view class="demo-block__body">
<l-tabbar :fixed="false" :safeAreaInsetBottom="false" theme="tag" shape="round">
<l-tabbar-item icon="home" value="home" :badgeProps="badgeProps"></l-tabbar-item>
<l-tabbar-item icon="app" value="app"></l-tabbar-item>
<l-tabbar-item icon="chat" value="chat"></l-tabbar-item>
<l-tabbar-item icon="user" value="user"></l-tabbar-item>
</l-tabbar>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
badgeProps: {dot: true}
}
},
methods:{
change(value){
console.log('value', value)
}
}
}
</script>
<style lang="scss">
.badge {
position: absolute;
transform-origin: 100%;
background-color: #FF4D4F;
overflow: visible;
border-radius: 999px;
}
.demo-block {
margin: 32px 0 0;
overflow: visible;
&__title {
margin: 0;
margin-top: 8px;
&-text {
color: rgba(0, 0, 0, 0.6);
font-weight: 400;
font-size: 14px;
line-height: 16px;
padding-left: 20px;
&.large {
color: rgba(0, 0, 0, 0.9);
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
&.ultra {
color: rgba(0, 0, 0, 0.9);
font-size: 24px;
font-weight: 700;
line-height: 32px;
}
}
}
&__desc-text {
color: rgba(0, 0, 0, 0.6);
margin: 8px 16px 0 0;
font-size: 14px;
line-height: 22px;
padding-left: 20px;
}
&__body {
margin: 16px 0;
overflow: visible;
.demo-block {
// margin-top: 0px;
margin: 0;
}
}
}
</style>