TabBar.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <template>
  2. <view class="nana-tab-bar" :style="{
  3. ...themeStyles.tabBar.value,
  4. ...innerStyle,
  5. ...(fixed ? {
  6. position: 'fixed',
  7. bottom: 0,
  8. left: 0,
  9. right: 0,
  10. } : {}),
  11. paddingBottom: xbarSpace ?
  12. `${safeAreaBottom}px`
  13. : themeStyles.tabBar.value.paddingBottom
  14. }">
  15. <slot name="background" />
  16. <slot />
  17. </view>
  18. </template>
  19. <script setup lang="ts">
  20. import { onMounted, onUpdated, provide, toRefs } from 'vue';
  21. import { useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
  22. import { DynamicColor, DynamicSize } from '../theme/ThemeTools';
  23. import { useChildLinkParent } from '../composeabe/ChildItem';
  24. export interface TabBarProps {
  25. /**
  26. * 自定义外层样式
  27. */
  28. innerStyle?: ViewStyle;
  29. /**
  30. * 选中颜色
  31. */
  32. activeColor?: string;
  33. /**
  34. * 未选中样式
  35. */
  36. inactiveColor?: string;
  37. /**
  38. * 标签文字样式
  39. */
  40. textStyle?: TextStyle;
  41. /**
  42. * 选中的标签名。
  43. * @default ''
  44. */
  45. selectedTabIndex?: number,
  46. /**
  47. * 预留底部安全区间距
  48. * @default false
  49. */
  50. xbarSpace?: boolean,
  51. /**
  52. * 是否固定在底部
  53. * @default false
  54. */
  55. fixed?: boolean,
  56. }
  57. defineOptions({
  58. options: {
  59. virtualHost: true,
  60. styleIsolation: "shared",
  61. }
  62. })
  63. const theme = useTheme();
  64. const emit = defineEmits([ 'update:selectedTabIndex' ])
  65. const props = withDefaults(defineProps<TabBarProps>(), {
  66. activeColor: 'primary',
  67. inactiveColor: 'text.second',
  68. })
  69. const themeStyles = theme.useThemeStyles({
  70. tabBar: {
  71. backgroundColor: DynamicColor('TabBarBackgroundColor', 'white'),
  72. borderTopStyle: 'solid',
  73. borderTopWidth: DynamicSize('TabBarBorderTopWidth', 2),
  74. borderTopColor: DynamicColor('TabBarBorderTopColor', 'border.light'),
  75. paddingTop: DynamicSize('TabBarPaddingVertical', 20),
  76. paddingBottom: DynamicSize('TabBarPaddingVertical', 20),
  77. paddingLeft: DynamicSize('TabBarPaddingHorizontal', 0),
  78. paddingRight: DynamicSize('TabBarPaddingHorizontal', 0),
  79. },
  80. });
  81. const {
  82. getPosition,
  83. resetCounter,
  84. getLength,
  85. } = useChildLinkParent({
  86. getPositionExtra(index) {
  87. return {}
  88. },
  89. });
  90. provide('TabBarContext', {
  91. topProps: toRefs(props),
  92. selectTab(index: number) {
  93. if (props.selectedTabIndex != index)
  94. emit('update:selectedTabIndex', index);
  95. },
  96. getPosition: () => getPosition().index,
  97. getCount: getLength,
  98. resetCounter,
  99. })
  100. const systemInfo = uni.getWindowInfo();
  101. const safeAreaBottom = systemInfo.safeAreaInsets?.bottom || 0;// 底部安全区距离
  102. onMounted(() => {
  103. resetCounter();
  104. });
  105. onUpdated(() => {
  106. resetCounter();
  107. });
  108. </script>
  109. <style>
  110. .nana-tab-bar {
  111. display: flex;
  112. position: relative;
  113. flex-direction: row;
  114. justify-content: space-around;
  115. align-items: flex-end;
  116. }
  117. </style>