CustomTabBar.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <template>
  2. <view class="tab-bar">
  3. <view
  4. v-for="(tab, index) in tabs"
  5. :key="index"
  6. class="tab-item"
  7. @click="switchTab(tab, index)"
  8. >
  9. <view class="tab-inner" :class="{ active: current === index }">
  10. <view class="tab-icon">
  11. <view class="icon-stack">
  12. <AppIcon class="icon-layer base" :name="tab.icon" :size="24" :color="inactiveColor" />
  13. <AppIcon class="icon-layer active-layer" :name="tab.icon" :size="24" :color="activeColor" />
  14. </view>
  15. </view>
  16. <text class="tab-text" :class="{ active: current === index }">{{ tab.text }}</text>
  17. </view>
  18. </view>
  19. </view>
  20. </template>
  21. <script setup>
  22. import { ref, watch, onMounted, getCurrentInstance } from 'vue'
  23. import { hapticMedium } from '../utils/haptics.js'
  24. const instance = getCurrentInstance()
  25. const activeColor = '#C6171E'
  26. const inactiveColor = '#8C8C8C'
  27. const tabs = [
  28. { pagePath: '/pages/index/index', text: '首页', icon: 'home' },
  29. { pagePath: '/pages/order/list', text: '订单', icon: 'clipboard' },
  30. { pagePath: '/pages/device/list', text: '设备', icon: 'monitor' },
  31. { pagePath: '/pages/finance/index',text: '财务', icon: 'dollar' },
  32. ]
  33. const current = ref(0)
  34. const getCurrentRoute = () => {
  35. const pages = getCurrentPages()
  36. if (pages.length > 0) {
  37. const route = '/' + pages[pages.length - 1].route
  38. const idx = tabs.findIndex(t => t.pagePath === route)
  39. if (idx !== -1) current.value = idx
  40. }
  41. }
  42. onMounted(() => getCurrentRoute())
  43. watch(() => instance?.proxy?.$route, () => getCurrentRoute(), { immediate: true })
  44. const switchTab = (tab, index) => {
  45. // 重复点击当前 Tab 不切换、不触发触感
  46. if (current.value === index) return
  47. hapticMedium()
  48. uni.switchTab({ url: tab.pagePath })
  49. }
  50. </script>
  51. <style scoped>
  52. .tab-bar {
  53. position: fixed;
  54. bottom: 0;
  55. left: 0;
  56. right: 0;
  57. display: flex;
  58. align-items: stretch;
  59. background: #FFFFFF;
  60. height: 104rpx;
  61. padding: 8rpx 16rpx;
  62. padding-bottom: calc(8rpx + env(safe-area-inset-bottom));
  63. box-shadow: 0 -2px 20px rgba(0, 0, 0, 0.06);
  64. z-index: 999;
  65. }
  66. .tab-item {
  67. flex: 1;
  68. display: flex;
  69. align-items: center;
  70. justify-content: center;
  71. transition: transform 0.12s cubic-bezier(0.4, 0, 1, 1);
  72. }
  73. .tab-item:active {
  74. transform: scale(0.92);
  75. }
  76. .tab-inner {
  77. display: flex;
  78. flex-direction: column;
  79. align-items: center;
  80. justify-content: center;
  81. gap: 4rpx;
  82. padding: 8rpx 24rpx;
  83. border-radius: 20rpx;
  84. transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  85. min-width: 120rpx;
  86. }
  87. .tab-inner.active {
  88. background: rgba(198, 23, 30, 0.06);
  89. }
  90. .tab-icon {
  91. display: flex;
  92. align-items: center;
  93. justify-content: center;
  94. }
  95. /* 双层图标:灰色/红色各渲染一份,src 永不切换,避免 image 重新解码导致的闪烁 */
  96. .icon-stack {
  97. position: relative;
  98. width: 48rpx;
  99. height: 48rpx;
  100. }
  101. .icon-layer {
  102. position: absolute;
  103. left: 0;
  104. top: 0;
  105. transition: opacity 0.22s ease, transform 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
  106. }
  107. .icon-layer.active-layer {
  108. opacity: 0;
  109. transform: scale(0.55);
  110. }
  111. .tab-inner.active .icon-layer.base {
  112. opacity: 0;
  113. }
  114. .tab-inner.active .icon-layer.active-layer {
  115. opacity: 1;
  116. transform: scale(1);
  117. }
  118. .tab-text {
  119. font-size: 22rpx;
  120. font-weight: 600;
  121. color: #8C8C8C;
  122. transition: color 0.25s;
  123. line-height: 1;
  124. }
  125. .tab-text.active {
  126. color: #C6171E;
  127. font-weight: 700;
  128. }
  129. </style>