index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. <template>
  2. <view>
  3. <view class="car_list">
  4. <view v-for="(pItem, pIndex) in cartListByGroup" :key="pIndex">
  5. <view class="business_name" v-if="pItem.products.length !== 0">
  6. <image @click="selectBussiness(pIndex)" class="checkbox" :src="pItem.checked ? '../../static/icon/checked.png' : '../../static/icon/checkbox.png'"></image>&nbsp;
  7. {{ pItem.business_name }}
  8. </view>
  9. <view class="product_group">
  10. <view v-for="(item, index) in pItem.products" @longpress="deleteCar(pIndex, index)" :key="index" style="margin-bottom: 20rpx">
  11. <SwipeAction @clickItem="clickItem" :index="index" ref="swipeAction" :pIndex="pIndex">
  12. <view class="car_item">
  13. <view class="check_label" @click="checkedItem(pIndex, index, item.id)">
  14. <image class="checkbox" src="@/static/icon/checkbox_disabled.png" style="width: 30rpx; height: 30rpx" v-if="item.product_status !== 0 || item.stock == 0" />
  15. <image class="checkbox" :src="item.checked ? '../../static/icon/checked.png' : '../../static/icon/checkbox.png'" v-else></image>
  16. </view>
  17. <view class="box_left">
  18. <navigator :url="'/pages/product/index?product_id=' + item.product_id">
  19. <image class="car_image" :src="item.thumb" mode=""></image>
  20. <view class="product_status" v-if="item.product_status !== 0">
  21. {{ item.stock == 0 ? "已售罄" : "已下架" }}
  22. </view>
  23. </navigator>
  24. </view>
  25. <view class="box_center">
  26. <navigator :url="'/pages/product/index?product_id=' + item.product_id" class="car_name">{{ item.name }}</navigator>
  27. <navigator :url="'/pages/product/index?product_id=' + item.product_id" class="car_spec">{{ item.spec }}</navigator>
  28. <view v-if="item.promo_title" class="promo_title">{{ item.promo_title }}</view>
  29. <navigator :url="'/pages/product/index?product_id=' + item.product_id" class="car_price">
  30. <text class="price">¥{{ item.price }}</text>
  31. <text class="market_price">¥{{ item.market_price }}</text>
  32. </navigator>
  33. </view>
  34. <view class="box_right">
  35. <view class="buy_num_box">
  36. <button class="buy_num_sub" @click="changeQuantity(pIndex, index, -1)" data-eventsync="true">
  37. <image class="sub_icon" src="../../static/icon/sub_icon.png" mode=""></image>
  38. </button>
  39. <input type="number" class="buy_num" placeholder="数量" v-model="item.buy_num" @blur="changeQuantity(pIndex, index, 0)" />
  40. <button class="buy_num_add" @click="changeQuantity(pIndex, index, +1)" data-eventsync="true">
  41. <image class="add_icon" src="../../static/icon/add_icon.png" mode=""></image>
  42. </button>
  43. </view>
  44. </view>
  45. </view>
  46. </SwipeAction>
  47. </view>
  48. </view>
  49. </view>
  50. </view>
  51. <Empty v-if="cartList.length == 0" text="----- 还没有产品啦 -----" />
  52. <view class="to_bottom" v-if="cartList.length"> -----到底啦-----</view>
  53. <view class="bottom_box">
  54. <view class="check_all_label" @click="checkAll()">
  55. <image class="checkbox" :src="checkedAll ? '../../static/icon/checked.png' : '../../static/icon/checkbox.png'"></image>
  56. <text class="checkall">全选</text>
  57. </view>
  58. <view class="price_box">
  59. 合计:<text class="price_total">¥{{ priceTotal }}</text>
  60. </view>
  61. <view class="to_order" @click="toOrder()">预约</view>
  62. </view>
  63. </view>
  64. </template>
  65. <script>
  66. import Empty from "@/components/Empty/Empty.vue";
  67. import SwipeAction from "@/components/SwipeAction/SwipeAction.vue";
  68. export default {
  69. components: { Empty, SwipeAction },
  70. data() {
  71. return {
  72. // 请求参数
  73. requestParam: {},
  74. // 是否全选
  75. checkedAll: false,
  76. // 总价
  77. priceTotal: "0.00",
  78. // 是否请求中
  79. isReqing: false,
  80. //购物车列表
  81. cartListByGroup: [],
  82. cartList: [],
  83. };
  84. },
  85. onLoad() {
  86. // #ifdef MP-WEIXIN
  87. //分享按钮
  88. uni.showShareMenu({
  89. withShareTicket: true,
  90. menus: ["shareAppMessage", "shareTimeline"],
  91. });
  92. // #endif
  93. },
  94. onShareAppMessage(obj) {
  95. // 获取分享信息
  96. let shareList = getApp().globalData.shareList;
  97. // 获取分享信息
  98. let shareObj = {
  99. title: "药优惠 得积分 兑豪礼",
  100. path: "/pages/index/index",
  101. imageUrl: "",
  102. };
  103. // 循环列表
  104. for (let i in shareList) {
  105. if (shareList[i].pages == "pages/car/index") {
  106. shareObj.path = shareList[i].path ? shareList[i].path : shareObj.path;
  107. shareObj.title = shareList[i].title ? shareList[i].title : shareObj.title;
  108. shareObj.imageUrl = shareList[i].image_url ? shareList[i].image_url : shareObj.imageUrl;
  109. }
  110. }
  111. // 返回分享信息
  112. return shareObj;
  113. },
  114. onShow() {
  115. // 登录提示
  116. if (!this.$checkAccess.alterLogin()) return;
  117. // 请求中,不允许刷新
  118. if (this.isReqing) return;
  119. // 设置请求中
  120. this.isReqing = true;
  121. // 非全选
  122. this.checkedAll = 0;
  123. // 请求列表
  124. this.$http.request("api/shop_cart/get_list", this.requestParam).then((re) => {
  125. // 设置非请求中
  126. this.isReqing = false;
  127. // 成功结果
  128. if (re.code == "success") {
  129. // 赋值
  130. this.cartListByGroup = this.formatGroupedData(re.data);
  131. this.cartList = re.data;
  132. // 计算价格
  133. this.priceHandler();
  134. }
  135. });
  136. },
  137. onPullDownRefresh() {
  138. // 登录提示
  139. if (!this.$checkAccess.alterLogin()) return;
  140. // 请求列表
  141. this.$http.request("api/shop_cart/get_list", this.requestParam).then((re) => {
  142. if (re.code == "success") {
  143. // 赋值
  144. this.cartListByGroup = this.formatGroupedData(re.data);
  145. this.cartList = re.data;
  146. // 计算价格
  147. this.priceHandler();
  148. }
  149. });
  150. uni.stopPullDownRefresh();
  151. },
  152. onReachBottom() {},
  153. methods: {
  154. // 转换为数组格式
  155. formatGroupedData(list) {
  156. const groupedData = list.reduce((result, item) => {
  157. const { business_id, business_name } = item;
  158. // 如果该 business_id 的分组不存在,则初始化该分组
  159. if (!result[business_id]) {
  160. result[business_id] = {
  161. business_id,
  162. business_name,
  163. products: [],
  164. checked: false,
  165. };
  166. }
  167. // 将商品添加到对应的 business_id 分组
  168. result[business_id].products.push(item);
  169. return result;
  170. }, {});
  171. return groupedData;
  172. },
  173. //选择商业公司下面的所有商品
  174. selectBussiness(index) {
  175. this.cartListByGroup[index].checked = !this.cartListByGroup[index].checked;
  176. this.cartListByGroup[index].products.forEach((item) => {
  177. item.checked = this.cartListByGroup[index].checked;
  178. });
  179. // 计算价格
  180. this.priceHandler();
  181. },
  182. // 数量调整
  183. changeQuantity(pIndex, index, number) {
  184. // 如果不是0.表示两侧按钮点击,0表示输入的修改
  185. if (number != 0) {
  186. // 计算个数
  187. this.cartListByGroup[pIndex].products[index].buy_num = this.cartListByGroup[pIndex].products[index].buy_num + number;
  188. }
  189. // 如果大于库存
  190. if (this.cartListByGroup[pIndex].products[index].buy_num > this.cartListByGroup[pIndex].products[index].stock) {
  191. // 设置为库存
  192. this.cartListByGroup[pIndex].products[index].buy_num = this.cartListByGroup[pIndex].products[index].stock;
  193. // 提示
  194. uni.showToast({
  195. title: "购买数量不能大于库存",
  196. icon: "none",
  197. });
  198. return;
  199. }
  200. // 如果小于1.设置为1
  201. if (this.cartListByGroup[pIndex].products[index].buy_num < 1) {
  202. // 恢复1
  203. this.cartListByGroup[pIndex].products[index].buy_num = 1;
  204. // 提示
  205. uni.showToast({
  206. title: "数量不可小于1",
  207. icon: "none",
  208. });
  209. return;
  210. }
  211. // 请求列表
  212. this.$http
  213. .request("api/shop_cart/edit", {
  214. id: this.cartListByGroup[pIndex].products[index].id,
  215. buy_num: this.cartListByGroup[pIndex].products[index].buy_num,
  216. })
  217. .then((re) => {
  218. if (re.code == "success") {
  219. // 计算价格
  220. this.priceHandler();
  221. } else {
  222. uni.showToast({
  223. title: re.msg,
  224. icon: "none",
  225. });
  226. }
  227. });
  228. },
  229. // 删除购物车
  230. deleteCar(pIndex, index) {
  231. uni.showModal({
  232. title: "是否删除?",
  233. success: (re) => {
  234. if (re.confirm) {
  235. // 请求列表
  236. this.$http
  237. .request("api/shop_cart/del", {
  238. id: this.cartListByGroup[pIndex].products[index].id,
  239. })
  240. .then((re) => {
  241. // 如果删除成功的话
  242. if (re.code == "success") {
  243. this.cartListByGroup[pIndex].products.splice(index, 1);
  244. // 计算价格
  245. this.priceHandler();
  246. if (this.$refs.swipeAction.length) {
  247. this.$refs.swipeAction[this.$refs.swipeAction.length - 1]?.reset();
  248. }
  249. }
  250. });
  251. } else {
  252. this.$refs.swipeAction[this.$refs.swipeAction.length - 1]?.reset();
  253. }
  254. },
  255. });
  256. },
  257. checkedItem(pIndex, index, product_id) {
  258. const product = this.cartListByGroup[pIndex].products[index];
  259. // 如果商品不可选中,直接返回
  260. if (product.product_status !== 0 || product.stock === 0) {
  261. return;
  262. }
  263. // 切换选中状态
  264. const isChecked = (product.checked = !product.checked);
  265. // 找到对应商家组中的商品并更新选中状态
  266. const productInGroup = this.cartListByGroup[pIndex].products.find((item) => item.id === product_id);
  267. if (productInGroup) {
  268. productInGroup.checked = isChecked;
  269. }
  270. // 检查该商家组内所有商品是否都已选中
  271. const checkedBussinessAll = this.cartListByGroup[pIndex].products.every((item) => item.checked);
  272. // 更新商家组的全选状态
  273. this.cartListByGroup[pIndex].checked = checkedBussinessAll;
  274. // 检查是否所有商品都已选中
  275. let checkedAll = true;
  276. for (const key in this.cartListByGroup) {
  277. if (!this.cartListByGroup[key].checked) {
  278. checkedAll = false;
  279. }
  280. }
  281. // 更新全选状态
  282. this.checkedAll = checkedAll ? 1 : 0;
  283. // 计算价格
  284. this.priceHandler();
  285. },
  286. checkAll() {
  287. // 设置全选/单选
  288. this.checkedAll = this.checkedAll ? 0 : 1;
  289. // 循环处理
  290. for (const index in this.cartListByGroup) {
  291. this.cartListByGroup[index].checked = this.checkedAll;
  292. this.cartListByGroup[index].products.forEach((item) => (item.checked = this.checkedAll));
  293. }
  294. // 计算价格
  295. this.priceHandler();
  296. },
  297. priceHandler() {
  298. // 总价格
  299. let priceTotal = 0;
  300. // 循环处理
  301. for (const index in this.cartListByGroup) {
  302. this.cartListByGroup[index].products.forEach((item) => {
  303. if (item.checked) {
  304. priceTotal = this.$decimal.add(priceTotal, this.$decimal.mul(item.price, item.buy_num));
  305. }
  306. });
  307. }
  308. // 小数点处理
  309. this.priceTotal = priceTotal.toFixed(2);
  310. },
  311. toOrder() {
  312. // 等待支付的信息
  313. let waitList = [];
  314. // 循环处理
  315. for (let index in this.cartListByGroup) {
  316. // 如果选中的
  317. for (const key in this.cartListByGroup[index].products) {
  318. if (this.cartListByGroup[index].products[key].checked) {
  319. // 如果库存不足
  320. if (this.cartListByGroup[index].products[key].buy_num < 1) {
  321. uni.showToast({ icon: "none", title: "选择的产品至少需要1个" });
  322. return;
  323. }
  324. // 如果库存不足
  325. if (this.cartListByGroup[index].products[key].buy_num > this.cartListByGroup[index].products[key].stock) {
  326. uni.showToast({ icon: "none", title: "产品库存不足" });
  327. return;
  328. }
  329. waitList.push(this.cartListByGroup[index].products[key].id);
  330. }
  331. }
  332. }
  333. // 如果没有选择
  334. if (!waitList.length) {
  335. uni.showToast({ icon: "none", title: "请选择需要结算的产品" });
  336. return;
  337. }
  338. // 如果没有选择
  339. if (waitList.length > 99) {
  340. uni.showToast({ icon: "none", title: "这么多产品一个预约单写不下哦" });
  341. return;
  342. }
  343. uni.navigateTo({
  344. url: "/pages/car/order?cart_ids=" + waitList.join(","),
  345. });
  346. },
  347. clickItem(e) {
  348. this.deleteCar(e.pIndex, e.index);
  349. },
  350. },
  351. };
  352. </script>
  353. <style lang="less">
  354. .car_list {
  355. display: block;
  356. overflow: hidden;
  357. margin: 0rpx auto;
  358. margin-top: 20rpx;
  359. padding-bottom: 110rpx;
  360. .business_name {
  361. padding: 8rpx 10rpx;
  362. border-bottom: 1px solid #f3f3f3;
  363. font-size: 24rpx;
  364. z-index: 1;
  365. display: flex;
  366. align-items: center;
  367. background-color: #fff;
  368. .checkbox {
  369. width: 40rpx;
  370. height: 40rpx;
  371. }
  372. }
  373. .car_item {
  374. height: 180rpx;
  375. display: block;
  376. background: #ffffff;
  377. margin: 0rpx auto;
  378. margin-bottom: 20rpx;
  379. padding: 20rpx 0rpx 0;
  380. position: relative;
  381. .delete_btn {
  382. position: absolute;
  383. top: 0;
  384. right: 0;
  385. bottom: 0;
  386. width: 80px;
  387. background-color: #f44336;
  388. color: #fff;
  389. display: flex;
  390. justify-content: center;
  391. align-items: center;
  392. }
  393. .swipe-content {
  394. transition: transform 0.3s ease;
  395. }
  396. .check_label {
  397. float: left;
  398. width: 40rpx;
  399. height: 40rpx;
  400. display: flex;
  401. align-items: center;
  402. justify-content: center;
  403. margin-top: 10rpx;
  404. padding: 50rpx 20rpx;
  405. .checkbox {
  406. float: left;
  407. width: 40rpx;
  408. height: 40rpx;
  409. }
  410. }
  411. .box_left {
  412. float: left;
  413. width: 140rpx;
  414. height: 200rpx;
  415. margin-top: 10rpx;
  416. position: relative;
  417. .car_image {
  418. width: 140rpx;
  419. height: 140rpx;
  420. border-radius: 5rpx;
  421. }
  422. .product_status {
  423. position: absolute;
  424. width: 100%;
  425. height: 40rpx;
  426. bottom: 60rpx;
  427. display: flex;
  428. justify-content: center;
  429. align-items: center;
  430. font-size: 24rpx;
  431. background-color: #999999;
  432. }
  433. }
  434. .box_center {
  435. float: left;
  436. width: 300rpx;
  437. margin-left: 25rpx;
  438. .car_name {
  439. max-height: 60rpx;
  440. font-size: 30rpx;
  441. line-height: 30rpx;
  442. overflow: hidden;
  443. white-space: nowrap; /* 不换行 */
  444. overflow: hidden; /* 隐藏超出的内容 */
  445. text-overflow: ellipsis; /* 用省略号表示被隐藏的部分 */
  446. }
  447. .promo_title {
  448. max-height: 80rpx;
  449. font-size: 20rpx;
  450. line-height: 40rpx;
  451. overflow: hidden;
  452. padding: 0rpx 0rpx;
  453. color: #dd524d;
  454. }
  455. .car_spec {
  456. color: #999999;
  457. font-size: 24rpx;
  458. max-height: 60rpx;
  459. line-height: 60rpx;
  460. overflow: hidden;
  461. }
  462. .car_price {
  463. font-size: 30rpx;
  464. line-height: 60rpx;
  465. .price {
  466. color: red;
  467. }
  468. .market_price {
  469. font-size: 24rpx;
  470. color: #999999;
  471. margin-left: 10rpx;
  472. padding-left: 10rpx;
  473. text-decoration: line-through;
  474. border-left: 2rpx solid #dddddd;
  475. }
  476. }
  477. }
  478. .box_right {
  479. float: right;
  480. width: 185rpx;
  481. padding-right: 15rpx;
  482. .buy_num_box {
  483. float: right;
  484. color: #333333;
  485. overflow: hidden;
  486. font-size: 24rpx;
  487. margin-top: 70rpx;
  488. text-align: center;
  489. .buy_num_sub {
  490. float: left;
  491. border: none;
  492. height: 36rpx;
  493. background: none;
  494. text-align: center;
  495. line-height: 36rpx;
  496. padding: 10rpx 10rpx;
  497. .sub_icon {
  498. width: 22rpx;
  499. height: 22rpx;
  500. display: block;
  501. }
  502. }
  503. .buy_num_sub::after {
  504. border: none;
  505. background: none;
  506. }
  507. .buy_num {
  508. float: left;
  509. width: 90rpx;
  510. height: 36rpx;
  511. font-size: 24rpx;
  512. min-height: 36rpx;
  513. line-height: 36rpx;
  514. padding: 0rpx 0rpx;
  515. border-radius: 8rpx;
  516. border: 2rpx solid #dddddd;
  517. }
  518. .buy_num_add {
  519. float: left;
  520. border: none;
  521. height: 36rpx;
  522. background: none;
  523. text-align: center;
  524. padding: 10rpx 10rpx;
  525. line-height: 36rpx;
  526. .add_icon {
  527. width: 22rpx;
  528. height: 22rpx;
  529. display: block;
  530. }
  531. }
  532. .buy_num_add::after {
  533. border: none;
  534. background: none;
  535. }
  536. }
  537. }
  538. }
  539. }
  540. .bottom_box {
  541. z-index: 999;
  542. left: 0rpx;
  543. width: 100%;
  544. height: 100rpx;
  545. display: block;
  546. position: fixed;
  547. overflow: hidden;
  548. background: #ffffff;
  549. padding: 0rpx 35rpx;
  550. bottom: var(--window-bottom);
  551. .check_all_label {
  552. float: left;
  553. width: 120rpx;
  554. height: 40rpx;
  555. font-size: 24rpx;
  556. line-height: 40rpx;
  557. padding: 30rpx 0rpx;
  558. .checkbox {
  559. float: left;
  560. width: 40rpx;
  561. height: 40rpx;
  562. }
  563. .checkbox.active {
  564. border: 2rpx solid red;
  565. .checkbox_active {
  566. background-color: #e03519;
  567. }
  568. }
  569. .checkall {
  570. float: left;
  571. height: 40rpx;
  572. display: block;
  573. margin-left: 10rpx;
  574. line-height: 40rpx;
  575. }
  576. }
  577. .price_box {
  578. float: left;
  579. width: 400rpx;
  580. display: block;
  581. color: #666666;
  582. font-size: 26rpx;
  583. text-align: right;
  584. line-height: 100rpx;
  585. margin-right: 20rpx;
  586. .price_total {
  587. color: red;
  588. font-size: 30rpx;
  589. }
  590. }
  591. .to_order {
  592. float: left;
  593. width: 140rpx;
  594. height: 60rpx;
  595. display: block;
  596. color: #ffffff;
  597. font-size: 28rpx;
  598. margin-top: 20rpx;
  599. line-height: 60rpx;
  600. padding: 0rpx 0rpx;
  601. text-align: center;
  602. border-radius: 30rpx;
  603. background-color: #e03519;
  604. }
  605. }
  606. </style>