cart.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <template>
  2. <Theme>
  3. <view class="wrap">
  4. <Navbar fixed leftShow leftIconColor="var(--bg)">
  5. <template #center>
  6. <view class="nav_title">
  7. <trans _t="购物车" />
  8. </view>
  9. <view class="nav_title" v-if="cartNum">({{ cartNum }})</view>
  10. </template>
  11. <template #right>
  12. <view class="nav_right" @click.stop="delCart" v-if="ids.length">
  13. <trans _t="删除" />
  14. </view>
  15. </template>
  16. </Navbar>
  17. <view class="tab" id="tabs">
  18. <Tab :active="actvieNum" :tabList="tabList" @confirm="tabClick" />
  19. </view>
  20. <view
  21. class="content"
  22. :style="{ '--height': footerHeight + tabbarHeight + tabHeight + 'px' }"
  23. >
  24. <view class="cont_wrap">
  25. <shop-model
  26. :list="cartList"
  27. ref="shopRef"
  28. @getIds="getIds"
  29. :isFree="actvieNum"
  30. :key="actvieNum"
  31. />
  32. </view>
  33. <view
  34. class="footer"
  35. id="footer"
  36. :style="{ '--tabbarHeight': tabbarHeight + 'px' }"
  37. >
  38. <view class="footer_left">
  39. <up-checkbox
  40. :label="t('全部')"
  41. :disabled="!cartList.length"
  42. activeColor="var(--black)"
  43. shape="circle"
  44. labelSize="14"
  45. labelColor="#676969"
  46. iconSize="16"
  47. name="agree"
  48. usedAlone
  49. v-model:checked="selectAllChecked"
  50. @change="selectAllChange"
  51. >
  52. </up-checkbox>
  53. </view>
  54. <view class="footer_right">
  55. <view class="total_info">
  56. <view class="total_price">
  57. <!-- {{ t(`已选${money.total_num}件,合计:`) }} -->
  58. <text
  59. >{{ symbol.symbol }} {{ Moneyhtml(money.total_price) }}</text
  60. >
  61. </view>
  62. <view class="total_desc">
  63. <trans _t="199包邮" v-if="actvieNum == 1" />
  64. <trans _t="不包括运费" v-else />
  65. </view>
  66. </view>
  67. <view
  68. class="total_btn"
  69. @click.stop="submit"
  70. :style="{
  71. opacity: canCheckout ? 1 : 0.5,
  72. 'pointer-events': canCheckout ? 'auto' : 'none',
  73. }"
  74. >
  75. <trans _t="去结算" />
  76. </view>
  77. </view>
  78. </view>
  79. </view>
  80. </view>
  81. <Tabbar page="shop" @getTabbarHeight="getTabbarHeight" />
  82. </Theme>
  83. </template>
  84. <script setup>
  85. import { computed, ref, onMounted, nextTick, watch } from "vue";
  86. import Tabbar from "@/components/tabbar";
  87. import Navbar from "@/components/navbar";
  88. import Tab from "@/components/tabs";
  89. import { useUserStore, useShopStore, useSystemStore } from "@/store";
  90. import { SHOP_CART_DEL } from "@/api";
  91. import { t } from "@/locale";
  92. import { onShow } from "@dcloudio/uni-app";
  93. import shopModel from "./components/shopModel";
  94. import { query, Moneyhtml } from "@/utils";
  95. const useUser = useUserStore();
  96. const useShop = useShopStore();
  97. const useSystem = useSystemStore();
  98. const symbol = computed(() => useSystem.getSymbol);
  99. const cartList = computed(() => useShop.getCartList);
  100. const cartNum = computed(() => useShop.getCartNum);
  101. const selectAllChecked = ref(true);
  102. const shopRef = ref(null);
  103. const footerHeight = ref(0);
  104. const tabbarHeight = ref(0);
  105. const ids = ref([]);
  106. const idsObj = ref({});
  107. const actvieNum = ref(0);
  108. const tabHeight = ref(0);
  109. const tabList = ["代购", "包邮"];
  110. const userInfo = computed(() => useUser.getuserInfo);
  111. const token = computed(() => useUser.getToken);
  112. const money = computed(() => {
  113. if (!cartList.value.length) {
  114. selectAllChecked.value = false;
  115. }
  116. let result = cartList.value.reduce(
  117. (acc, seller) => {
  118. const sellerId = seller.seller_id;
  119. if (idsObj.value[sellerId]) {
  120. const totalPrice = seller.goods
  121. .filter((good) => idsObj.value[sellerId].includes(good.id))
  122. .reduce((sum, good) => sum + parseFloat(good.price) * good.total, 0);
  123. acc.total_price = (acc.total_price - 0 + (totalPrice - 0)).toFixed(2);
  124. const totalNum = seller.goods
  125. .filter((good) => idsObj.value[sellerId].includes(good.id))
  126. .reduce((sum, good) => sum + good.total, 0);
  127. acc.total_num = acc.total_num - 0 + (totalNum - 0);
  128. }
  129. return acc;
  130. },
  131. {
  132. total_price: 0,
  133. total_num: 0,
  134. orginal_price: 0,
  135. }
  136. );
  137. return result;
  138. });
  139. const canCheckout = computed(() => {
  140. if (!ids.value.length) return false;
  141. const total = parseFloat(money.value.total_price || 0);
  142. if (actvieNum.value === 1) {
  143. return total >= 199;
  144. }
  145. return true;
  146. });
  147. watch(
  148. () => cartList.value,
  149. (list) => {
  150. const hasData = Array.isArray(list) && list.length > 0;
  151. if (!hasData) {
  152. selectAllChecked.value = false;
  153. return;
  154. }
  155. nextTick(() => {
  156. selectAllChecked.value = true;
  157. selectAllChange(true);
  158. });
  159. },
  160. { immediate: true, deep: true }
  161. );
  162. const selectAllChange = (e) => {
  163. shopRef.value && shopRef.value.allStatus(e);
  164. };
  165. const getIds = (arr, flag) => {
  166. ids.value = Object.values(arr).flat(1);
  167. idsObj.value = arr;
  168. selectAllChecked.value = flag;
  169. };
  170. const submit = () => {
  171. if (!canCheckout.value) return;
  172. uni.navigateTo({
  173. url: `/pages/shop/shopConfirm?comfirmId=${ids.value.join(",")}&isFree=${
  174. actvieNum.value
  175. }`,
  176. });
  177. };
  178. const delCart = () => {
  179. removeCart();
  180. };
  181. const removeCart = async () => {
  182. try {
  183. let id = (ids.value.length && ids.value.join(",")) || "";
  184. await SHOP_CART_DEL(id);
  185. useShop.setCartList(actvieNum.value);
  186. shopRef.value && shopRef.value.allStatus(false);
  187. } catch (error) {
  188. toast(error.msg);
  189. }
  190. };
  191. const getHeight = async () => {
  192. try {
  193. const res = await query("#footer");
  194. const resTab = await query("#tabs");
  195. nextTick(() => {
  196. tabHeight.value = resTab.height;
  197. footerHeight.value = res.height;
  198. });
  199. } catch (error) {}
  200. };
  201. const getTabbarHeight = (height) => {
  202. tabbarHeight.value = height;
  203. };
  204. const tabClick = (item, index) => {
  205. if (actvieNum.value == index) return;
  206. actvieNum.value = index;
  207. useShop.setCartList(index);
  208. };
  209. onMounted(() => {
  210. getHeight();
  211. });
  212. onShow(() => {
  213. token.value && useShop.setCartList(actvieNum.value);
  214. setTimeout(() => {
  215. getHeight();
  216. }, 0);
  217. });
  218. </script>
  219. <style lang="less" scoped>
  220. @import url("@/style.less");
  221. .wrap {
  222. min-height: 100bh;
  223. background: var(--bg);
  224. overflow: hidden;
  225. .flex();
  226. flex-direction: column;
  227. :deep(.u-navbar__content) {
  228. background-color: var(--black) !important;
  229. }
  230. .nav_title {
  231. color: var(--light);
  232. }
  233. .nav_right {
  234. color: var(--primary);
  235. .size(24rpx);
  236. font-weight: 500;
  237. }
  238. .content {
  239. flex-grow: 1;
  240. height: calc(100vh - 44px - var(--height));
  241. overflow-y: scroll;
  242. .flex();
  243. flex-direction: column;
  244. .cont_wrap {
  245. flex-grow: 1;
  246. overflow: hidden scroll;
  247. }
  248. .footer {
  249. .flex_position(space-between);
  250. flex-wrap: wrap;
  251. padding: 8px 12px;
  252. background-color: var(--light);
  253. box-shadow: 0 -4px 6px #0000000d;
  254. position: fixed;
  255. bottom: var(--tabbarHeight);
  256. // bottom: calc(var(--tabbarHeight) + constant(safe-area-inset-bottom));
  257. // bottom: calc(var(--tabbarHeight) + env(safe-area-inset-bottom));
  258. left: 0;
  259. right: 0;
  260. box-sizing: border-box;
  261. &_left {
  262. }
  263. &_right {
  264. .ver();
  265. .total_info {
  266. .total_price {
  267. text-align: right;
  268. color: var(--red);
  269. .size();
  270. font-weight: 700;
  271. line-height: 48rpx;
  272. }
  273. .total_desc {
  274. color: var(--text-01);
  275. .size(24rpx);
  276. line-height: 40rpx;
  277. text-align: right;
  278. .icon-font {
  279. .size();
  280. }
  281. }
  282. }
  283. .total_btn {
  284. .size(28rpx);
  285. font-weight: 700;
  286. height: 38px;
  287. margin-left: 16rpx;
  288. min-width: 180rpx;
  289. background-color: var(--black);
  290. color: var(--light);
  291. border-radius: 16rpx;
  292. padding: 16rpx 30rpx;
  293. text-align: center;
  294. }
  295. }
  296. }
  297. }
  298. }
  299. </style>