vue 实现购物车列表右滑删除功能

本文主要实现购物车列表右滑删除功能。

一、购物车页面cart.vue

侧滑按钮宽度0.6rem,如果需要体验感好点的可以加个动画属性。

<template>
  <div>
    <nav-header :showBackBtn="false" @handleBtnClick="goList('searchList')">
      <span slot="title">购物车</span>
    </nav-header>
    <div class="content">
      <mescroll-vue
        ref="mescroll"
        :down="mescrollDown"
        :up="mescrollUp"
        @init="mescrollInit"
        class="mescroll"
        v-if="islogin"
      >
        <div class="cart-wrapper">
          <div class="cart-list">
            <div
              :style="item.deleteSlider"
              class="cart-info"
              v-for="(item,index) in cartList"
              :key="index"
            >
              <div class="cart-del" @click="isDel(item.id)" v-if="item.disX > 59">删除</div>
              <div
                class="cart-item space-between"
                @touchstart="start($event,item)"
                @touchmove="move($event,item)"
                @touchend="end($event,item)"
              >
                <div class="select-wrapper" @click="checkChange(item)">
                  <div class="select-btn">
                    <div class="active" v-if="item.checked"></div>
                  </div>
                </div>
                <div class="item-img">
                  <img class="item-img-item" :src="item.goods_pic" />
                </div>
                <div class="item-info">
                  <div class="item-name" v-text="item.goods_name"></div>
                  <div class="item-param">
                    <span>已选:"</span>
                    <span v-text="item.spec_name"></span>
                    <span>" "</span>
                    <span v-text="item.mat_name"></span>
                    <span>"</span>
                  </div>
                  <div class="item-price">
                    <span>¥</span>
                    <span v-text="item.price"></span>
                  </div>
                  <div class="select-number-wrapper nowrap">
                    <div class="btn-del" @click="editBuyNum(item,1)">-</div>
                    <input class="number" type="number" v-model="item.buy_num" disabled />
                    <div class="btn-add" @click="editBuyNum(item,0)">+</div>
                  </div>
                </div>
              </div>
              <div class="cart-param">
                <span class="param-count">
                  共
                  <span v-text="item.buy_num"></span>
                  件商品 合计:
                </span>
                <span class="price" v-text="'¥'+item.price*item.buy_num"></span>
                <span class="param-count">运费¥0</span>
              </div>
            </div>
          </div>
          <div class="cart-nav space-between">
            <div class="select-wrapper" @click="allCheck">
              <div class="select-btn">
                <div class="active" v-if="cartAllCheck"></div>
              </div>
              <div class="select-tip">全选</div>
            </div>
            <div class="cart-total nowrap">
              <div class="price-express">
                <div class="total-price">
                  <span class="price-tip">合计:</span>
                  <span class="price" v-text="'¥' + cartTotalPrice"></span>
                </div>
                <div class="express">
                  <span class="express-price">含运费¥0</span>
                </div>
              </div>
              <div class="btn-next" @click="go('ContentPurchasing')">下一步</div>
            </div>
          </div>
        </div>
      </mescroll-vue>
      <div class="cart-tip" v-else>
        <span>请先登录后操作,</span>
        <span class="login-tip" @click="go('Login')">立即登录</span>
      </div>
    </div>
    <!-- 确认框 -->
    <confirm-dialog :isShow="isShowDialog" @ok="okDiolog" @cancel="close">
      <span slot="text" v-text="confirmTips"></span>
    </confirm-dialog>
  </div>
</template>

<script>
import MescrollVue from "mescroll.js/mescroll.vue";
import ConfirmDialog from "@/components/confirmDialog/ConfirmDialog";
import utils from "@/assets/js/utils";
export default {
  components: {
    ConfirmDialog,
    MescrollVue,
  },
  data() {
    return {
      confirmTips: "是否删除该商品",
      isShowDialog: false,
      id: "",
      isdelId: "",
      islogin: true,
      number: 0,
      startX: 0, // 开始pos
      endX: 0, // 结束pos
      moveX: 0, // 滑动时的pos
      disX: 0, // 滑动距离
      delTablewidth: 60,
      cartList: [],
      cartTotalPrice: 0,
      cartAllCheck: false,
      cartTips: "",
      mescroll: null, //mescroll实例对象
      //下拉刷新的配置. (如果下拉刷新和上拉加载处理的逻辑是一样的,则mescrollDown可不用写了)
      mescrollDown: {
        use: false,
      },
      // 上拉加载的配置.
      mescrollUp: {
        // auto: false,
        callback: this.initCart, // 上拉回调,此处简写; 相当于 callback(page, mescroll) { }
        htmlNodata: '<p class="upwarp-nodata">没有更多数据了</p>',
        noMoreSize: 0, //如果列表已无数据,可设置列表的总数量要大于0才显示无更多数据;
      },
    };
  },
  computed: {},
  mounted() {
    this.init();
  },
  methods: {
    //mescroll初始化
    mescrollInit(mescroll) {
      this.mescroll = mescroll;
    },
    init() {
      // 初始化动态添加动画
      let userInfo = JSON.parse(localStorage.getItem("userInfo"));
      if (!userInfo) {
        this.islogin = false;
        return;
      }
    },
    initCart(page) {
      this.$showLoading();
      this.$ajax
        .post("Index/getCartGoods", {
          size: utils.pageSize,
          cp: page.num,
        })
        .then((res) => {
          let resData = res.data;
          if (resData.code == "1") {
            resData.data.forEach((e) => {
              e.goods_pic = utils.fileUrl + e.goods_pic;
              e.checked = false;
              e.startX = 0; // 开始pos
              e.endX = 0; // 结束pos
              e.moveX = 0; // 滑动时的pos
              e.disX = 0; // 滑动距离
            });
            if (page.num == 1) {
              this.cartList = [];
            }
            this.cartList = this.cartList.concat(resData.data);
            this.$nextTick(() => {
              if (!resData.data.length) {
                this.mescroll.showNoMore();
              } else {
                this.mescroll.endSuccess(resData.data.length);
              }
            });
            // this.totalPrice();
          } else {
            this.mescroll.endSuccess(false);
          }
          this.$closeLoading();
        })
        .catch((err) => {
          utils.ajaxError(this);
        });
    },
    refresh() {
      this.cartList = [];
      this.mescroll.resetUpScroll();
    },
    totalPrice() {
      if (this.cartIds().length == 0) {
        this.cartTotalPrice = 0;
        return;
      }
      let cartTotalPrice = 0;
      this.cartList.forEach((e) => {
        if (e.checked == true) {
          cartTotalPrice += e.price * e.buy_num;
        }
      });
      this.cartTotalPrice = cartTotalPrice;
    },
    isDel(id) {
      this.id = id;
      this.isShowDialog = true;
    },
    okDiolog() {
      this.deleteCartGoods(this.id);
      this.close();
    },
    close() {
      this.isShowDialog = false;
      this.id = "";
    },
    deleteCartGoods(id) {
      let data = {};
      data.cart_id = id;
      this.$ajax
        .post("Index/deleteCartGoods", data)
        .then((res) => {
          this.$toast("删除成功");
          this.refresh();
          this.$closeLoading();
        })
        .catch((err) => {
          utils.ajaxError(this);
        });
    },
    editBuyNum(item, type) {
      if (item.buy_num == 1 && type == 1) {
        this.$toast("购买数量不能小于1");
        return;
      }
      let data = {};
      data.cart_id = item.id;
      data.type = type;
      this.$ajax
        .post("Index/editBuyNum", data)
        .then((res) => {
          let resData = res.data;
          if (resData.code == "1") {
            item.buy_num = resData.data.buy_num;
            item.total_price = resData.data.total_price;
            this.$forceUpdate();
            this.totalPrice();
          }
          this.$closeLoading();
        })
        .catch((err) => {
          utils.ajaxError(this);
        });
    },
    start(e, item) {
      e = e || even;
      item.startX = 0;
      if (e.touches.length === 1) {
        item.startX = e.touches[0].clientX;
        this.isdelId = item.id;
      }
    },
    end(e, item) {
      e = e || even;
      if (item.disX < 100) {
        item.deleteSlider = "transform:translateX(0px)";
        item.disX = 0;
      } else {
        item.deleteSlider =
          "transform:translateX(-" + this.delTablewidth + "px)";
        item.disX = 60;
      }
      for (var i = 0, e; i < this.cartList.length; i++) {
        e = this.cartList[i];
        if (e.id != this.isdelId) {
          e.startX = 0; // 开始pos
          e.endX = 0; // 结束pos
          e.moveX = 0; // 滑动时的pos
          e.disX = 0; // 滑动距离
          e.deleteSlider = "transform:translateX(0px)";
        }
      }
    },
    move(e, item) {
      e = e || even;
      if (e.touches.length == 1) {
        item.moveX = e.touches[0].clientX;
        item.disX = item.startX - item.moveX + item.disX;
        if (item.disX > 0) {
          if (item.disX < 100) {
            item.deleteSlider = "transform:translateX(-" + item.disX + "px)";
          } else {
            item.deleteSlider = "transform:translateX(-100px)";
          }
        } else {
          if (item.disX < -60) {
            item.deleteSlider = "transform:translateX(60px)";
          } else {
            item.deleteSlider =
              "transform:translateX(" + Math.abs(item.disX) + "px)";
          }
        }
      }
    },
    go(path) {
      if (path == "ContentPurchasing") {
        if (this.cartIds().length == 0) {
          this.$toast("请选择商品");
          return;
        }
        this.order();
      } else {
        this.$router.push(path);
      }
    },
    goDetail(item) {
      let data = {};
      data.callId = item.callId;
      data.msgFrom = item.msgFrom;
      data.prgId = item.prgId;
      this.$ajax
        .post("common/findMainDetail", data)
        .then((res) => {
          if (res.data.code == "1") {
            this.$router.push({
              path: "/appealDetail",
              query: {
                callId: item.callId,
                msgFrom: item.msgFrom,
                prgId: item.prgId,
              },
            });
          } else if (res.data.code == "1") {
            this.$toast(res.data.msg);
            this.$showLoading();
            this.$ajax
              .post("user/findReceiveMsgList", {
                cp: 1,
                size: 3,
                msgRead: 0,
              })
              .then((res) => {
                let resData = res.data;
                if (resData.retcode) {
                  if (resData.retcode == "0") {
                    this.msgList = [];
                    this.msgList = resData.data;
                  } else {
                    this.$toast(resData.msg);
                  }
                } else {
                  this.$toast(utils.ajaxErrorTip);
                }
              })
              .catch((err) => {
                this.$toast(utils.ajaxErrorTip);
              });
          }
          this.$nextTick(() => {
            this.$closeLoading();
          });
        })
        .catch((error) => {
          this.$closeLoading();
        });
    },
    cartIds() {
      let cartIds = [];
      this.cartList.forEach((e) => {
        if (e.checked == true) {
          cartIds.push(e.id);
        }
      });
      return cartIds;
    },
    checkChange(item) {
      item.checked = !item.checked;
      this.totalPrice();
      this.$forceUpdate();
      this.cartAllCheck = this.isAllCheck();
    },
    allCheck() {
      let cartList = this.cartList;
      for (var i = 0; i < cartList.length; i++) {
        cartList[i].checked = !this.cartAllCheck;
      }
      this.cartAllCheck = this.isAllCheck();
      this.totalPrice();
    },
    isAllCheck() {
      let cartList = this.cartList;
      for (var i = 0; i < cartList.length; i++) {
        if (cartList[i].checked == false) {
          return false;
        }
      }
      return true;
    },
    order() {
      this.$showLoading();
      let cartIds = this.cartIds(),
        list = [];
      for (var i = 0; i < cartIds.length; i++) {
        let data = {
          cart_id: cartIds[i],
        };
        this.$ajax
          .post("Index/order", data)
          .then((res) => {
            let resData = res.data;
            if (resData.code == "1") {
              list.push(resData.data.id);
            }
            if (list.length == cartIds.length) {
              // this.deal(list);
              this.$router.push({
                path: "ContentInfo",
                query: {
                  orderIds: String(list),
                },
              });
            }
          })
          .catch((err) => {
            utils.ajaxError(this);
          });
      }
    },
  },
};
</script>
<style scoped lang="less">
@import "~styles/base.less";
.mescroll {
  position: fixed;
  top: 0.88rem;
  // bottom: 0;
  .px2rem(bottom, 200);
  height: auto;
  /deep/.upwarp-nodata,
  /deep/.upwarp-tip {
    .px2rem(font-size, 24);
    .px2rem(line-height, 60);
  }
}
.cart-tip {
  text-align: center;
  .px2rem(line-height, 100);
  .px2rem(height, 100);
  z-index: 1000;
  .login-tip {
    color: blue;
  }
}
.cart-wrapper {
  background-color: #f7f7f7;
  // .px2rem(padding-bottom, 200);
  .cart-list {
    padding: 0.4rem 0.2rem 0;

    .cart-info {
      transition: 0.2s;
      .px2rem(border-radius, 26);
      background-color: #fff;
      padding: 0 0.2rem;
      transform: translateX(0px);
      position: relative;
      .px2rem(margin-bottom, 40);
      .cart-del {
        transition: 0.2s;
        position: absolute;
        .px2rem(width, 100);
        .px2rem(line-height, 320);
        .px2rem(right, -120);
        .px2rem(border-radius, 26);
        height: 100%;
        top: 0;
        background-color: red;
        color: #fff;
        vertical-align: middle;
        text-align: center;
        // transform: translateX(-50px);
      }
      .cart-item {
        .px2rem(padding-top, 38);
        // .px2rem(margin-bottom, 40);
        .select-wrapper {
          .px2rem(width, 80);
          position: relative;
          .select-btn {
            .px2rem(width, 32);
            .px2rem(height, 32);
            box-sizing: border-box;
            border: 0.01rem solid #d6d6d6;
            border-radius: 50%;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            .active {
              .px2rem(width, 24);
              .px2rem(height, 24);
              border-radius: 50%;
              background-color: @baseColor;
              position: absolute;
              top: 50%;
              left: 50%;
              transform: translate(-50%, -50%);
            }
          }
        }
        .item-img {
          .px2rem(width, 198);
          .px2rem(height, 182);
          position: relative;
          overflow: hidden;
          border: 0.01rem solid #f4f4f4;
          .item-img-item {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 100%;
          }
        }
        .item-info {
          .px2rem(width, 434);
          .px2rem(padding-left, 28);
          position: relative;
          box-sizing: border-box;
          .item-name {
            .px2rem(font-size, 28);
            .px2rem(line-height, 40);
            color: #000;
          }
          .item-param {
            .px2rem(font-size, 24);
            .px2rem(line-height, 34);
            color: #999;
            margin: 0.18rem 0 0.68rem;
          }
          .item-price {
            .px2rem(font-size, 30);
            .px2rem(line-height, 40);
            color: #ff3131;
          }
          .select-number-wrapper {
            .px2rem(width, 156);
            .px2rem(height, 40);
            .px2rem(line-height, 40);
            .px2rem(font-size, 24);
            .px2rem(border-radius, 4);
            .px2rem(right, 0);
            .px2rem(bottom, 0);
            border: 0.01rem solid #d6d6d6;
            box-sizing: border-box;
            overflow: hidden;
            text-align: center;
            position: absolute;

            .btn-del,
            .btn-add {
              color: #d6d6d6;
              .px2rem(width, 44);
            }
            .number {
              text-align: center;
              .px2rem(width, 68);
              color: #000;
              border-left: 0.01rem solid #d6d6d6;
              border-right: 0.01rem solid #d6d6d6;
            }
          }
        }
      }
      .cart-param {
        .px2rem(margin-top, 14);
        .px2rem(height, 76);
        .px2rem(line-height, 76);
        border-top: 0.01rem solid #f4f4f4;
        text-align: right;
        color: #999;
        .param-count {
          .px2rem(font-size, 20);
        }
        .price {
          .px2rem(font-size, 24);
        }
      }
    }
  }
  .cart-nav {
    width: 100%;
    position: fixed;
    background-color: #fff;
    border-top: 0.01rem solid #f7f7f7;
    .px2rem(left, 0);
    .px2rem(bottom, 100);
    .px2rem(height, 100);
    .px2rem(line-height, 100);
    .select-wrapper {
      .px2rem(width, 200);
      position: relative;
      .select-btn {
        .px2rem(width, 32);
        .px2rem(height, 32);
        .px2rem(left, 64);
        box-sizing: border-box;
        border: 0.01rem solid #d6d6d6;
        border-radius: 50%;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        .active {
          .px2rem(width, 24);
          .px2rem(height, 24);
          border-radius: 50%;
          background-color: @baseColor;
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
        }
      }
      .select-tip {
        color: #999;
        .px2rem(font-size, 24);
        .px2rem(padding-left, 118);
      }
    }
    .cart-total {
      .price-express {
        .px2rem(line-height, 50);
        text-align: right;
        .total-price {
          .px2rem(margin-top, 10);
          .price-tip {
            .px2rem(font-size, 20);
            color: #999;
          }
          .price {
            color: #ff3131;
            .px2rem(font-size, 24);
          }
        }
        .express {
          .px2rem(margin-top, -20);
          .express-price {
            .px2rem(font-size, 20);
            color: #999;
          }
        }
      }
      .btn-next {
        .px2rem(width, 186);
        .px2rem(height, 70);
        .px2rem(line-height, 70);
        .px2rem(border-radius, 35);
        .px2rem(font-size, 28);
        background-color: @baseColor;
        margin: 0.2rem 0.32rem 0 0.22rem;
        text-align: center;
        color: #fff;
      }
    }
  }
}
</style>

上一篇:页面适配


下一篇:vue 实现PC端适配 lib-flexible+loader px2rem