tabbar.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <template>
  2. <Theme>
  3. <view class="cu-bar tabbar foot ZIndex tabbars">
  4. <view
  5. class="action"
  6. @click="NavChange(item, index)"
  7. v-for="(item, index) in navList"
  8. :key="index"
  9. :data-cur="item.name"
  10. >
  11. <!-- 图标和数量显示 -->
  12. <view class="cuIcon-cu-image _item">
  13. <view>
  14. <i
  15. class="icon-font"
  16. :class="item.icon"
  17. :style="
  18. pageCur == item.name
  19. ? { color: 'var(--primary)' }
  20. : { color: '#c3cad9' }
  21. "
  22. v-if="item.icon"
  23. ></i>
  24. <image
  25. class="img"
  26. :src="pageCur == item.name ? item.img_active : item.img"
  27. mode="heightFix"
  28. v-else
  29. ></image>
  30. </view>
  31. <!-- 数量徽章 -->
  32. <view class="entry_num" v-if="item.num > 0">{{ item.num }}</view>
  33. </view>
  34. <view :class="pageCur == item.name ? 'tabbar_active' : 'tabbar_item'">
  35. {{ t(item.title) }}
  36. </view>
  37. </view>
  38. </view>
  39. </Theme>
  40. </template>
  41. <script setup>
  42. import { onMounted, computed, watch, ref, nextTick } from "vue";
  43. import { useTabbarStore, useMessageStore, useShopStore } from "@/store";
  44. // 导入图片资源(保持不变)
  45. import channel from "@/static/tabbar/channel.png";
  46. import home from "@/static/tabbar/home.png";
  47. import home_active from "@/static/tabbar/home_active.png";
  48. import cart from "@/static/tabbar/cart.png";
  49. import cart_active from "@/static/tabbar/cart_active.png";
  50. import ship from "@/static/tabbar/ship.png";
  51. import ship_active from "@/static/tabbar/ship_active.png";
  52. import chat from "@/static/tabbar/chat.png";
  53. import chat_active from "@/static/tabbar/chat_active.png";
  54. import user from "@/static/tabbar/user.png";
  55. import user_active from "@/static/tabbar/user_active.png";
  56. import { t } from "@/locale";
  57. import { storeToRefs } from "pinia";
  58. import { query } from "@/utils";
  59. import { onShow } from "@dcloudio/uni-app";
  60. const useTabbar = useTabbarStore();
  61. const useShop = useShopStore();
  62. const useMessage = useMessageStore();
  63. const { globalMap } = storeToRefs(useMessage);
  64. const unreadCount = ref(0);
  65. // 1. 定义购物车数量计算属性(响应式)
  66. const cartNum = computed(() => useShop.getCartNum);
  67. const props = defineProps({
  68. page: {
  69. type: String,
  70. default: "index",
  71. },
  72. });
  73. const emit = defineEmits(["getTabbarHeight"]);
  74. // 2. 将navList定义为ref响应式数组(关键修改)
  75. const navList = ref([
  76. {
  77. name: "index",
  78. title: "tabbar.首页",
  79. img: home,
  80. img_active: home_active,
  81. num: 0,
  82. },
  83. {
  84. name: "shop",
  85. title: "tabbar.购物车",
  86. img: cart,
  87. img_active: cart_active,
  88. num: cartNum.value, // 初始值
  89. },
  90. {
  91. name: "chat",
  92. title: "tabbar.咨询",
  93. img: chat,
  94. img_active: chat_active,
  95. num: unreadCount.value,
  96. },
  97. {
  98. name: "order",
  99. title: "tabbar.订单",
  100. img: ship,
  101. img_active: ship_active,
  102. num: 0,
  103. },
  104. {
  105. name: "user",
  106. title: "tabbar.我的",
  107. img: user,
  108. img_active: user_active,
  109. num: 0,
  110. },
  111. ]);
  112. // 3. 监听cartNum变化,实时更新navList中购物车项的num(关键修改)
  113. watch(
  114. cartNum,
  115. (newNum) => {
  116. // 找到购物车对应的项并更新数量
  117. const cartItem = navList.value.find((item) => item.name === "shop");
  118. if (cartItem) {
  119. cartItem.num = newNum;
  120. }
  121. },
  122. { immediate: true } // 初始化时立即执行一次
  123. );
  124. // 监听咨询消息数量(保持不变)
  125. watch(
  126. () => globalMap.value.serviceChannel,
  127. (newVal) => {
  128. if (newVal) {
  129. unreadCount.value = newVal.unreadCount;
  130. const chatItem = navList.value.find((item) => item.name === "chat");
  131. if (chatItem) {
  132. chatItem.num = unreadCount.value;
  133. }
  134. }
  135. },
  136. { deep: true, immediate: true }
  137. );
  138. const pageCur = computed(() => useTabbar.setPageCur);
  139. const NavChange = (item, index) => {
  140. if (item.name == pageCur.value) return;
  141. if (item.name == "shop") {
  142. uni.switchTab({
  143. url: `/pages/shop/cart`,
  144. success: () => {
  145. useTabbar.getPageCur(item.name);
  146. useTabbar.getPageIndex(index);
  147. },
  148. });
  149. } else {
  150. uni.switchTab({
  151. url: `/pages/${item.name}/index`,
  152. success: () => {
  153. useTabbar.getPageCur(item.name);
  154. useTabbar.getPageIndex(index);
  155. },
  156. });
  157. }
  158. };
  159. const getHeight = async () => {
  160. try {
  161. const res = await query(".tabbar");
  162. nextTick(() => {
  163. emit("getTabbarHeight", res.height);
  164. });
  165. } catch (error) {}
  166. };
  167. onMounted(() => {
  168. let num = navList.value.findIndex((item) => item.name == props.page);
  169. if (num >= 0) {
  170. useTabbar.getPageCur(props.page);
  171. }
  172. });
  173. onShow(() => {
  174. setTimeout(() => {
  175. getHeight();
  176. }, 200);
  177. });
  178. </script>
  179. <!-- 样式部分保持不变 -->
  180. <style>
  181. .ZIndex {
  182. z-index: 2000;
  183. bottom: 0;
  184. }
  185. /deep/ .cu-bar .content {
  186. cursor: pointer;
  187. pointer-events: unset;
  188. }
  189. .cu-tag.badge:not([class*="bg-"]) {
  190. background-color: var(--red) !important;
  191. color: var(--black);
  192. position: absolute;
  193. top: -10rpx;
  194. right: 32%;
  195. }
  196. </style>
  197. <style lang="less" scoped>
  198. @import "@/static/css/theme.less";
  199. @import url("@/style.less");
  200. .tabbar {
  201. background-color: var(--light);
  202. bottom: 0;
  203. }
  204. .action {
  205. display: flex !important;
  206. justify-content: center;
  207. align-items: center;
  208. flex-direction: column;
  209. }
  210. ._item {
  211. width: max-content !important;
  212. position: relative;
  213. .icon-font {
  214. font-size: 24px;
  215. }
  216. ._item {
  217. height: 24px;
  218. vertical-align: top;
  219. }
  220. .entry_num {
  221. position: absolute;
  222. top: -12rpx;
  223. right: -14rpx;
  224. background-color: var(--danger);
  225. border: 1px solid var(--light);
  226. color: var(--light);
  227. width: 36rpx;
  228. height: 36rpx;
  229. border-radius: 50%;
  230. .size(18rpx);
  231. .flex_center();
  232. }
  233. }
  234. .tabbar_item {
  235. color: var(--text-01);
  236. font-size: 10px;
  237. line-height: 12px;
  238. margin: 2px auto 0;
  239. }
  240. .tabbar_active {
  241. font-size: 10px;
  242. line-height: 12px;
  243. margin: 2px auto 0;
  244. color: var(--text-02);
  245. }
  246. .foot {
  247. box-shadow: 0 -4px 6px #0000000d;
  248. }
  249. .cont_num {
  250. position: absolute;
  251. @width: 32rpx;
  252. width: @width;
  253. height: @width;
  254. border-radius: 50%;
  255. display: flex;
  256. align-items: center;
  257. justify-content: center;
  258. color: var(--light);
  259. background-color: var(--red);
  260. font-size: 24rpx;
  261. z-index: 2;
  262. right: -18rpx;
  263. top: -6rpx;
  264. }
  265. </style>