index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  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: 32rpx;
  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. .business_icon {
  373. width: 48rpx;
  374. height: 48rpx;
  375. margin-right: 10rpx;
  376. }
  377. }
  378. .car_item {
  379. height: 180rpx;
  380. display: block;
  381. background: #ffffff;
  382. margin: 0rpx auto;
  383. margin-bottom: 20rpx;
  384. padding: 20rpx 0rpx 0;
  385. position: relative;
  386. .delete_btn {
  387. position: absolute;
  388. top: 0;
  389. right: 0;
  390. bottom: 0;
  391. width: 80px;
  392. background-color: #f44336;
  393. color: #fff;
  394. display: flex;
  395. justify-content: center;
  396. align-items: center;
  397. }
  398. .swipe-content {
  399. transition: transform 0.3s ease;
  400. }
  401. .check_label {
  402. float: left;
  403. width: 40rpx;
  404. height: 40rpx;
  405. display: flex;
  406. align-items: center;
  407. justify-content: center;
  408. margin-top: 10rpx;
  409. padding: 50rpx 20rpx;
  410. .checkbox {
  411. float: left;
  412. width: 40rpx;
  413. height: 40rpx;
  414. }
  415. }
  416. .box_left {
  417. float: left;
  418. width: 140rpx;
  419. height: 200rpx;
  420. margin-top: 10rpx;
  421. position: relative;
  422. .car_image {
  423. width: 140rpx;
  424. height: 140rpx;
  425. border-radius: 5rpx;
  426. }
  427. .product_status {
  428. position: absolute;
  429. width: 100%;
  430. height: 40rpx;
  431. bottom: 60rpx;
  432. display: flex;
  433. justify-content: center;
  434. align-items: center;
  435. font-size: 24rpx;
  436. background-color: #999999;
  437. }
  438. }
  439. .box_center {
  440. float: left;
  441. width: 300rpx;
  442. margin-left: 25rpx;
  443. .car_name {
  444. max-height: 60rpx;
  445. font-size: 30rpx;
  446. line-height: 30rpx;
  447. overflow: hidden;
  448. white-space: nowrap; /* 不换行 */
  449. overflow: hidden; /* 隐藏超出的内容 */
  450. text-overflow: ellipsis; /* 用省略号表示被隐藏的部分 */
  451. }
  452. .promo_title {
  453. max-height: 80rpx;
  454. font-size: 20rpx;
  455. line-height: 40rpx;
  456. overflow: hidden;
  457. padding: 0rpx 0rpx;
  458. color: #dd524d;
  459. }
  460. .car_spec {
  461. color: #999999;
  462. font-size: 24rpx;
  463. max-height: 60rpx;
  464. line-height: 60rpx;
  465. overflow: hidden;
  466. }
  467. .car_price {
  468. font-size: 30rpx;
  469. line-height: 60rpx;
  470. .price {
  471. color: red;
  472. }
  473. .market_price {
  474. font-size: 24rpx;
  475. color: #999999;
  476. margin-left: 10rpx;
  477. padding-left: 10rpx;
  478. text-decoration: line-through;
  479. border-left: 2rpx solid #dddddd;
  480. }
  481. }
  482. }
  483. .box_right {
  484. float: right;
  485. width: 185rpx;
  486. padding-right: 15rpx;
  487. .buy_num_box {
  488. float: right;
  489. color: #333333;
  490. overflow: hidden;
  491. font-size: 24rpx;
  492. margin-top: 70rpx;
  493. text-align: center;
  494. .buy_num_sub {
  495. float: left;
  496. border: none;
  497. height: 36rpx;
  498. background: none;
  499. text-align: center;
  500. line-height: 36rpx;
  501. padding: 10rpx 10rpx;
  502. .sub_icon {
  503. width: 22rpx;
  504. height: 22rpx;
  505. display: block;
  506. }
  507. }
  508. .buy_num_sub::after {
  509. border: none;
  510. background: none;
  511. }
  512. .buy_num {
  513. float: left;
  514. width: 90rpx;
  515. height: 36rpx;
  516. font-size: 24rpx;
  517. min-height: 36rpx;
  518. line-height: 36rpx;
  519. padding: 0rpx 0rpx;
  520. border-radius: 8rpx;
  521. border: 2rpx solid #dddddd;
  522. }
  523. .buy_num_add {
  524. float: left;
  525. border: none;
  526. height: 36rpx;
  527. background: none;
  528. text-align: center;
  529. padding: 10rpx 10rpx;
  530. line-height: 36rpx;
  531. .add_icon {
  532. width: 22rpx;
  533. height: 22rpx;
  534. display: block;
  535. }
  536. }
  537. .buy_num_add::after {
  538. border: none;
  539. background: none;
  540. }
  541. }
  542. }
  543. }
  544. }
  545. .bottom_box {
  546. z-index: 999;
  547. left: 0rpx;
  548. width: 100%;
  549. height: 100rpx;
  550. display: block;
  551. position: fixed;
  552. overflow: hidden;
  553. background: #ffffff;
  554. padding: 0rpx 35rpx;
  555. bottom: var(--window-bottom);
  556. .check_all_label {
  557. float: left;
  558. width: 120rpx;
  559. height: 40rpx;
  560. font-size: 24rpx;
  561. line-height: 40rpx;
  562. padding: 30rpx 0rpx;
  563. .checkbox {
  564. float: left;
  565. width: 40rpx;
  566. height: 40rpx;
  567. }
  568. .checkbox.active {
  569. border: 2rpx solid red;
  570. .checkbox_active {
  571. background-color: #e03519;
  572. }
  573. }
  574. .checkall {
  575. float: left;
  576. height: 40rpx;
  577. display: block;
  578. margin-left: 10rpx;
  579. line-height: 40rpx;
  580. }
  581. }
  582. .price_box {
  583. float: left;
  584. width: 400rpx;
  585. display: block;
  586. color: #666666;
  587. font-size: 26rpx;
  588. text-align: right;
  589. line-height: 100rpx;
  590. margin-right: 20rpx;
  591. .price_total {
  592. color: red;
  593. font-size: 30rpx;
  594. }
  595. }
  596. .to_order {
  597. float: left;
  598. width: 140rpx;
  599. height: 60rpx;
  600. display: block;
  601. color: #ffffff;
  602. font-size: 28rpx;
  603. margin-top: 20rpx;
  604. line-height: 60rpx;
  605. padding: 0rpx 0rpx;
  606. text-align: center;
  607. border-radius: 30rpx;
  608. background-color: #e03519;
  609. }
  610. }
  611. </style>