common-tabbar.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <template>
  2. <view class="tabbar" v-if="tabbarList.length > 0">
  3. <view
  4. class="tabbar-item"
  5. :class="{ active: currentTab === item.tabKey }"
  6. v-for="item in tabbarList"
  7. :key="item.id"
  8. @click="switchTab(item)"
  9. >
  10. <text class="tabbar-icon">{{ currentTab === item.tabKey ? (item.iconSelected || item.icon) : item.icon }}</text>
  11. <text class="tabbar-text">{{ item.name }}</text>
  12. <!-- 角标 -->
  13. <view v-if="item.badgeType === 'dot'" class="tabbar-badge-dot"></view>
  14. <view v-else-if="item.badgeType === 'number' && getBadgeValue(item.badgeKey) > 0" class="tabbar-badge">
  15. {{ getBadgeValue(item.badgeKey) > 99 ? '99+' : getBadgeValue(item.badgeKey) }}
  16. </view>
  17. </view>
  18. </view>
  19. </template>
  20. <script>
  21. import api from '@/utils/api.js'
  22. export default {
  23. name: 'CommonTabbar',
  24. props: {
  25. // 当前选中的tab
  26. current: {
  27. type: String,
  28. default: ''
  29. },
  30. // 客户端类型:user-用户端,matchmaker-红娘端
  31. clientType: {
  32. type: String,
  33. default: 'user'
  34. },
  35. // 角标数据(key-value形式)
  36. badgeData: {
  37. type: Object,
  38. default: () => ({})
  39. }
  40. },
  41. data() {
  42. return {
  43. tabbarList: [],
  44. currentTab: '',
  45. defaultTabbar: [
  46. { id: 1, name: '首页', icon: '🏠', iconSelected: '🏠', path: '/pages/index/index', tabKey: 'index', sortOrder: 1 },
  47. { id: 2, name: '动态', icon: '💕', iconSelected: '💕', path: '/pages/plaza/index', tabKey: 'plaza', sortOrder: 2 },
  48. { id: 3, name: '推荐', icon: '👍', iconSelected: '👍', path: '/pages/recommend/index', tabKey: 'recommend', sortOrder: 3 },
  49. { id: 4, name: '我的', icon: '👤', iconSelected: '👤', path: '/pages/mine/index', tabKey: 'mine', sortOrder: 4 }
  50. ]
  51. }
  52. },
  53. watch: {
  54. current: {
  55. immediate: true,
  56. handler(val) {
  57. this.currentTab = val
  58. }
  59. }
  60. },
  61. created() {
  62. this.loadTabbarConfig()
  63. },
  64. methods: {
  65. // 加载导航配置
  66. async loadTabbarConfig() {
  67. try {
  68. // 先尝试从缓存获取
  69. const cacheKey = `tabbar_config_${this.clientType}`
  70. const cached = uni.getStorageSync(cacheKey)
  71. const cacheTime = uni.getStorageSync(cacheKey + '_time')
  72. // 缓存有效期5分钟
  73. if (cached && cacheTime && (Date.now() - cacheTime < 5 * 60 * 1000)) {
  74. this.tabbarList = cached
  75. return
  76. }
  77. // 从服务器获取
  78. const list = await api.tabbar.getTabbarConfig(this.clientType)
  79. if (list && list.length > 0) {
  80. this.tabbarList = list
  81. // 缓存配置
  82. uni.setStorageSync(cacheKey, list)
  83. uni.setStorageSync(cacheKey + '_time', Date.now())
  84. } else {
  85. // 使用默认配置
  86. this.tabbarList = this.defaultTabbar
  87. }
  88. } catch (e) {
  89. console.error('加载导航配置失败:', e)
  90. // 使用默认配置
  91. this.tabbarList = this.defaultTabbar
  92. }
  93. },
  94. // 切换tab
  95. switchTab(item) {
  96. if (this.currentTab === item.tabKey) {
  97. return
  98. }
  99. this.currentTab = item.tabKey
  100. this.$emit('change', item)
  101. // 跳转页面
  102. uni.switchTab({
  103. url: item.path,
  104. fail: () => {
  105. // switchTab失败时尝试navigateTo
  106. uni.navigateTo({
  107. url: item.path,
  108. fail: () => {
  109. uni.redirectTo({
  110. url: item.path
  111. })
  112. }
  113. })
  114. }
  115. })
  116. },
  117. // 获取角标值
  118. getBadgeValue(key) {
  119. if (!key) return 0
  120. return this.badgeData[key] || 0
  121. },
  122. // 刷新配置(供外部调用)
  123. refreshConfig() {
  124. const cacheKey = `tabbar_config_${this.clientType}`
  125. uni.removeStorageSync(cacheKey)
  126. uni.removeStorageSync(cacheKey + '_time')
  127. this.loadTabbarConfig()
  128. }
  129. }
  130. }
  131. </script>
  132. <style scoped lang="scss">
  133. .tabbar {
  134. position: fixed;
  135. bottom: 0;
  136. left: 0;
  137. right: 0;
  138. height: 100rpx;
  139. background: #FFFFFF;
  140. display: flex;
  141. align-items: center;
  142. justify-content: space-around;
  143. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  144. padding-bottom: constant(safe-area-inset-bottom);
  145. padding-bottom: env(safe-area-inset-bottom);
  146. z-index: 999;
  147. .tabbar-item {
  148. flex: 1;
  149. display: flex;
  150. flex-direction: column;
  151. align-items: center;
  152. justify-content: center;
  153. position: relative;
  154. padding: 10rpx 0;
  155. .tabbar-icon {
  156. font-size: 44rpx;
  157. margin-bottom: 4rpx;
  158. }
  159. .tabbar-text {
  160. font-size: 22rpx;
  161. color: #999999;
  162. }
  163. &.active {
  164. .tabbar-text {
  165. color: #E91E63;
  166. font-weight: 500;
  167. }
  168. }
  169. // 数字角标
  170. .tabbar-badge {
  171. position: absolute;
  172. top: 2rpx;
  173. right: 50%;
  174. transform: translateX(30rpx);
  175. min-width: 32rpx;
  176. height: 32rpx;
  177. line-height: 32rpx;
  178. padding: 0 8rpx;
  179. background: #FF4757;
  180. color: #FFFFFF;
  181. font-size: 20rpx;
  182. border-radius: 16rpx;
  183. text-align: center;
  184. }
  185. // 红点角标
  186. .tabbar-badge-dot {
  187. position: absolute;
  188. top: 8rpx;
  189. right: 50%;
  190. transform: translateX(24rpx);
  191. width: 16rpx;
  192. height: 16rpx;
  193. background: #FF4757;
  194. border-radius: 50%;
  195. }
  196. }
  197. }
  198. </style>