TabBarItem.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <template>
  2. <Touchable
  3. center
  4. direction="column"
  5. :innerStyle="{
  6. ...innerStyle,
  7. flexBasis: size,
  8. }"
  9. :touchable="true"
  10. @click="handleClick"
  11. >
  12. <slot>
  13. <view v-if="props.hump">
  14. <Icon v-bind="iconProps" icon=""/>
  15. </view>
  16. <Badge
  17. :containerStyle="{
  18. ...(props.hump ? {
  19. position: 'absolute',
  20. height: theme.resolveSize(iconProps.size),
  21. bottom: theme.resolveSize(active ? humpHeight[0] : humpHeight[1]),
  22. left: '50%',
  23. transform: 'translateX(-50%)',
  24. } : {}),
  25. }"
  26. :content="props.badge === -1 ? '' : ((typeof props.badge === 'undefined' || props.badge === 0) ? 0 : props.badge)"
  27. :offset="{ x: 3, y: 0 }"
  28. v-bind="props.badgeProps"
  29. >
  30. <slot name="icon" :selected="active" :iconProps="iconProps">
  31. <Icon v-bind="iconProps" />
  32. </slot>
  33. </Badge>
  34. <Text
  35. v-if="text"
  36. :innerStyle="{
  37. ...themeStyles.tabText.value,
  38. ...props.textStyle
  39. }"
  40. :text="props.text"
  41. :color="color"
  42. />
  43. </slot>
  44. </Touchable>
  45. </template>
  46. <script setup lang="ts">
  47. import type { IconProps } from '@/components/basic/Icon.vue';
  48. import { useTheme, type TextStyle } from '@/components/theme/ThemeDefine';
  49. import { computed, inject, onMounted, onUpdated, ref } from 'vue';
  50. import { DynamicSize } from '../theme/ThemeTools';
  51. import Text from '@/components/basic/Text.vue';
  52. import Icon from '@/components/basic/Icon.vue';
  53. import Badge, { type BadgeProps } from '../display/Badge.vue';
  54. import Touchable from '../feedback/Touchable.vue';
  55. export interface TabBarItemProps {
  56. /**
  57. * 标签图标
  58. */
  59. icon?: string;
  60. /**
  61. * 图标透传样式
  62. */
  63. iconProps?: IconProps;
  64. /**
  65. * 标签图标大小
  66. * @default 23
  67. */
  68. iconSize?: number;
  69. /**
  70. * 标签文字
  71. */
  72. text?: string;
  73. /**
  74. * 标签文字样式
  75. */
  76. textStyle?: TextStyle;
  77. /**
  78. * 标签标记。为 0 或者 未定义时不显示,为 -1时显示圆点,为大于0的数时显示数字标记
  79. */
  80. badge?: number;
  81. /**
  82. * 指定当前标签是否凸起。凸起状态下可以使用 renderIcon 回调渲染自定义图片,图片不会把Tabbar撑开而是会溢出,可以实现凸起按钮的效果。
  83. * @default false
  84. */
  85. hump?: boolean;
  86. /**
  87. * 指定当前标签凸起的高度,数组第0位是选中时的凸起高度,第1位是未选中时的凸起高度。
  88. */
  89. humpHeight?: number[];
  90. /**
  91. * 自定义徽标的属性,传入的对象会被透传给 Badge 组件的 props
  92. */
  93. badgeProps?: BadgeProps;
  94. /**
  95. * 是否懒加载当前标签,仅在标签模式中有效
  96. * @default false
  97. */
  98. lazy?: boolean;
  99. /**
  100. * 自定义样式
  101. */
  102. innerStyle?: object,
  103. }
  104. defineOptions({
  105. options: {
  106. virtualHost: true
  107. }
  108. })
  109. const emit = defineEmits([ 'click' ]);
  110. const theme = useTheme();
  111. const props = withDefaults(defineProps<TabBarItemProps>(), {
  112. activeColor: 'primary',
  113. inactiveColor: 'text.second',
  114. active: false,
  115. iconSize: 50,
  116. humpHeight: () => ([ 50, 50 ])
  117. })
  118. const {
  119. topProps,
  120. selectTab,
  121. getPosition,
  122. getCount,
  123. } = inject('TabBarContext', {}) as any;
  124. const position = computed(() => getPosition());
  125. const active = computed(() => topProps.selectedTabIndex.value === position.value);
  126. const color = computed(() => theme.resolveThemeColor(
  127. active.value ? topProps.activeColor.value : topProps.inactiveColor.value
  128. ));
  129. const iconProps = computed(() => ({
  130. ...props.iconProps,
  131. icon: props.icon ?? '',
  132. size: props.iconSize,
  133. color: color.value,
  134. }));
  135. const themeStyles = theme.useThemeStyles({
  136. tabItem: {
  137. flexDirection: 'column',
  138. alignItems: 'center',
  139. justifyContent: 'center',
  140. },
  141. tabText: {
  142. fontSize: DynamicSize('TabBarItemTextFontSize', 22),
  143. marginTop: DynamicSize('TabBarItemTextMarginTop', 0),
  144. },
  145. });
  146. function handleClick() {
  147. selectTab(position.value);
  148. emit('click');
  149. }
  150. const size = ref('');
  151. onUpdated(() => {
  152. size.value = `${100 / getCount()}%`
  153. });
  154. onMounted(() => {
  155. size.value = `${100 / getCount()}%`
  156. });
  157. </script>