client-tab-bar.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. <template>
  2. <view class="client-tab-bar">
  3. <view class="client-tab-bar__inner">
  4. <view
  5. v-for="item in tabs"
  6. :key="item.key"
  7. class="tab-item"
  8. @click="onTab(item)"
  9. >
  10. <view class="tab-icon-wrap">
  11. <image
  12. class="tab-icon"
  13. :src="active === item.key ? item.selectedIconPath : item.iconPath"
  14. mode="aspectFit"
  15. />
  16. </view>
  17. <text class="tab-text" :class="{ 'tab-text--on': active === item.key }">{{ item.text }}</text>
  18. </view>
  19. </view>
  20. </view>
  21. </template>
  22. <script setup lang="ts">
  23. /**
  24. * 非 tabBar 页面底部占位:与 pages.json 中客户端 tab 一致,避免 H5 子路由下只有空白条。
  25. * 图标使用项目内 static/tabbar 下 PNG(与 pages.json 原生 tabBar 一致;微信小程序不支持 SVG 作原生 tab 图)。
  26. * 固定在视口底部,主内容区底部操作栏需留出高度:calc(100rpx + env(safe-area-inset-bottom))
  27. */
  28. const props = defineProps<{
  29. active: 'home' | 'order' | 'cart' | 'profile'
  30. }>()
  31. const tabs = [
  32. {
  33. key: 'home' as const,
  34. text: '首页',
  35. pagePath: '/pages/client/home/home',
  36. iconPath: '/static/tabbar/首页.png',
  37. selectedIconPath: '/static/tabbar/首页-active.png'
  38. },
  39. {
  40. key: 'order' as const,
  41. text: '订单',
  42. pagePath: '/pages/client/order/order',
  43. iconPath: '/static/tabbar/订单.png',
  44. selectedIconPath: '/static/tabbar/订单-active.png'
  45. },
  46. {
  47. key: 'cart' as const,
  48. text: '购物车',
  49. pagePath: '/pages/client/cart/cart',
  50. iconPath: '/static/tabbar/购物车.png',
  51. selectedIconPath: '/static/tabbar/购物车-active.png'
  52. },
  53. {
  54. key: 'profile' as const,
  55. text: '我的',
  56. pagePath: '/pages/client/profile/profile',
  57. iconPath: '/static/tabbar/我的.png',
  58. selectedIconPath: '/static/tabbar/我的-active.png'
  59. }
  60. ]
  61. function onTab(item: (typeof tabs)[number]) {
  62. if (item.key === props.active) return
  63. uni.switchTab({
  64. url: item.pagePath,
  65. fail: () => {
  66. uni.reLaunch({ url: item.pagePath })
  67. }
  68. })
  69. }
  70. </script>
  71. <style lang="scss" scoped>
  72. .client-tab-bar {
  73. position: fixed;
  74. left: 0;
  75. right: 0;
  76. bottom: 0;
  77. /* 低于 Vant Popup / 业务浮层,避免挡住弹窗 */
  78. z-index: 500;
  79. background: #ffffff;
  80. /* 与首页原生 tabBar 边界线更一致 */
  81. border-top: 1rpx solid rgba(0, 0, 0, 0.2);
  82. padding-bottom: constant(safe-area-inset-bottom);
  83. padding-bottom: env(safe-area-inset-bottom);
  84. box-sizing: border-box;
  85. }
  86. .client-tab-bar__inner {
  87. display: flex;
  88. align-items: center;
  89. justify-content: space-around;
  90. height: 88rpx;
  91. box-sizing: border-box;
  92. }
  93. .tab-item {
  94. flex: 1;
  95. display: flex;
  96. flex-direction: column;
  97. align-items: center;
  98. justify-content: center;
  99. padding: 2rpx 0 0;
  100. text-align: center;
  101. }
  102. .tab-icon-wrap {
  103. width: 42rpx;
  104. height: 42rpx;
  105. display: flex;
  106. align-items: center;
  107. justify-content: center;
  108. }
  109. .tab-icon {
  110. width: 42rpx;
  111. height: 42rpx;
  112. display: block;
  113. margin: 0 auto;
  114. }
  115. .tab-text {
  116. width: 100%;
  117. margin-top: 6rpx;
  118. font-size: 18rpx;
  119. color: #000000;
  120. font-weight: 400;
  121. line-height: 1.15;
  122. text-align: center;
  123. }
  124. .tab-text--on {
  125. color: #0a9f6d;
  126. font-weight: 700;
  127. }
  128. </style>