Vue2 e-commerce front-end project - complete the add-to-cart function and shopping cart page

Vue2 e-commerce front-end project - complete the add-to-cart function and shopping cart page

1. Add to shopping cart

1. Before the route jumps, send a request to send the product data to the server.

(1) Observe interface documents

In fact, here you only need to pass the ID and quantity of the existing items to the backend, and the backend does not need to return data.

Please add image description

Please add image description

(2) Write interface

It’s time to write interfaces that we are all familiar with:

src/api/index.js
// 将产品添加到购物车中
export const reqAddShopCart = (skuId, skuNum) => {
    
    
  return requests({
    
    
    url: `/cart/addToCart/{skuId}/{skuNum}`,
    method: "post",
  });

(3) dispatch data

1. Add a click event to the add to cart button

Please add image description

2. Send actions, and then pass the data in the form of objects. The first key-value pair is the product id (the params parameter passed when jumping from Search to Detail routing), and the second key-value pair is shopping. Number of cars (we have stored it in the data of the Detail component before)

    // 加入购物车的回调函数
    addShopcar() {
    
    
      // 派发actions
      // 1、发请求——将产品加入到数据库(通知服务、传递参数器)
      this.$store.dispatch("getShopCart", {
    
    
        skuId: this.$route.params.skuid,
        skuNum: this.skuNum,
      });
      // 2、服务器存储成功——进行路由跳转
      // 3、失败——给用户进行提示
    },

3. On the actions side, through destructuring and assigning values, the interface is called and the data is passed to the server (back-end database)

const actions = {
    
    
  ......
  async getShopCart({
     
      commit }, {
     
      skuId, skuNum }) {
    
    
    let result = await reqAddShopCart(skuId, skuNum);
    console.log("添加到购物车的信息" + result);
  },
};

(4) Determine whether the server has received product data

Here it is necessary to determine whether the request has been successful, that is, whether the server has received the data of the product to be added to the shopping cart. If it is successful, it must perform a routing jump and pass parameters. If it fails, it must prompt the user.

dispatchThe function in this will be called actions, and calling this async function will return an promiseobject. The status and result value of this Promise object depend on the return value of this async function.

1. If a non-Promise object is returned, it is successful and the value is the return value;
2. If a Promise object is returned, the status and result value depend on the Promise
3. If the return value is not written and the await is followed by a failed Promise, Then an exception will be thrown. Since an exception is thrown, the async function returns a failed Promise.
4. If the return value is not written and await is followed by a successful Promise, then undefined will be returned. The async function returns a successful Promise with the value undefined.

const actions = {
    
    
  ......
  //加入购物车
  //异步请求,把商品id和数量发送给服务器
  async getShopCart({
     
      commit }, {
     
      skuId, skuNum }) {
    
    
    //其实这里不用try-catch,因为那边已经try了,不写return默认返回undefined(成功的Primise)
    //如果await后成功则返回undefined(没写return,返回成功的Promise)
    //如果失败则抛出异常(返回失败的Promise)
    let result = await reqAddShopCart(skuId, skuNum);
    console.log("添加到购物车的信息" + result);
    //这里只是把购物车数据给服务器,但是服务器不需要返回什么东西。所以这里我们不用再三连环了,通过dispatch把数据给服务器就已经好了
    // if (result.code == 200) {
    
    
    //   commit("GETSHOPCART", result.data);
    // }
  },
};
  methods: {
    
    
    ......
    // 加入购物车的回调函数
    async addShopcar() {
    
    
      // 派发actions
      // 1、发请求——将产品加入到数据库(通知服务、传递参数器)
      try {
    
    
        // 2、服务器存储成功——进行路由跳转
        this.$store.dispatch("getShopCart", {
    
    
          skuId: this.$route.params.skuid,
          skuNum: this.skuNum,
        });
      } catch (error) {
    
    
        // 3、失败——给用户进行提示
        console.log("请求失败", error.message);
      }
    },
  },

2. Route jump after successful request

Routing components are usually stored in pagesfolders, and general components are usually stored in componentsfolders.

(1) Create routes and configure routing rules

1. So let’s create the route first

Please add image description

2. Configure routing rulessrc/router/routes.js

  {
    
    
    name: "addcat",
    path: "/addcartsuccess",
    component: AddCartSuccess,
    meta: {
    
    
      showFooter: true,
  },

(2)Route jump and pass parameters (practice local storage)

3. Write the code for routing jumps and parameter transfers

Please add image description

Two values ​​need to be passed here: the skuInfo object and the product quantity shopCarNum (in fact, there is no need to pass skuInfo at all, just go to the warehouse to read it). It is best not to use params and query when passing parameters, because the object passed needs to be skuInfo, so if the object is passed, the address bar may be garbled. The solution adopted here is: session storage - click here to review

1. It’s simple to use query 商品数量shopCarNum, because the numbers will not be garbled.
2. Use session storage (local storage is also acceptable) to bring it skuInfothere (in fact, just read it directly from the warehouse; or you can use query, which is nothing more than garbled characters in the address bar)

async addShopcar() {
    
    
      // 派发actions
      // 1、发请求——将产品加入到数据库(通知服务、传递参数器)
      try {
    
    
        // 2、服务器存储成功——进行路由跳转
        await this.$store.dispatch("getShopCart", {
    
    
          skuId: this.$route.params.skuid,
          skuNum: this.skuNum,
        });
        // 进行路由跳转并传参
        // 一些简单的数据可以通过query形式传参
        // 此处的产品信息比较复杂,是一个对象,所以我们可以采用会话存储sessionStorage
        // 本地存储和会话存储一般存的是字符串,所以我们把对象转化成JSON字符串进行存储
        sessionStorage.setItem("SKUINFO", JSON.stringify(this.skuInfo));
        this.$router.push({
    
     name: "addcat", query: {
    
     skuNum: this.skuNum } });
      } catch (error) {
    
    
        // 3、失败——给用户进行提示
        console.log("请求失败", error.message);
      }
    },

Notice:

  • Local storage can only store string format, so the object needs to be converted to a string.JSON.stringify()
  • To obtain local storage data, we need to convert the string inside into object format JSON.parse()before we can use the data inside.

Please add image description

Then put the corresponding data on the page

Please add image description

Please add image description

2. Complete the business on the shopping cart page

Just click to view the product details and jump back directly. The data warehouse is already there and there is no need to re-send the request.

<router-link class="sui-btn btn-xlarge" :to="`/detail/${skuInfo.id}`">
    查看商品详情
</router-link>

The next step is to click to go to the shopping cart checkout, jump to the shopping cart checkout page
, change the static part of the shopping cart, then register the route, and write a route jump.

<router-link to="/shopcart">去购物车结算 > </router-link>

Please add image description

1. Generate visitor ID

The backend should have written logic here to use a userTempIdrequest header field called to determine who you are, and then return the corresponding data to you.

Generally speaking, the normal logic should be that each user has his own token, and then after clicking to add to the shopping cart, add a row of data to the user-product table; when reading the shopping cart to retrieve data, the user token parameter should be passed to obtain it. The corresponding product list. For simulation here, the backend has written the useTempId field. When refreshing, we will give it a visitor ID (unique ID), and it will use this field directly as a local browser visitor, so there is no need to pass parameters when requesting shopping cart data.

There are many ways to randomly generate visitor IDs, including nanoid, uuid, and timestamp. Here I use uuid. And if we need persistent storage, we can use localStorage.

1. When entering the page, first randomly generate a timestamp as the user's ID.

Please add image description

Please add image description

2. When requesting data, use the id as the value in the request in the request interceptor userTempId. That is, put this ID in the request header and pass it to the server.

Please add image description

3. This is done. As long as the ID in the local storage is not manually cleared, the shopping cart data of the ID can be obtained every time.

2. Obtain the corresponding shopping cart data

Write interface

// 获取购物车列表数据
export const reqCartList = () => {
    
    
  return requests({
    
    
    url: "/cart/cartList",
    method: "get",
  });
};

Send a request, three consecutive words.

Please add image description

Please add image description

Finally, display the data on the page
Please add image description

Please add image description

3. Calculate the total price of the checked items

This is easy to calculate. Use the isChecked attribute to only calculate the price of the selected (isChecked=1), and just loop through forEach.

Please add image description

  computed: {
    
    
    ......
    // 计算购买商品的总价
    totalPrice() {
    
    
      let totalPrice = 0;
      this.cartInfoList.forEach((element) => {
    
    
        if (element.isChecked == 1) {
    
    
          totalPrice += element.skuNum * element.skuPrice;
        }
      });
      return totalPrice;
    },

4. Linkage between selecting all and product ticking

(1) Whether the Select All button is selected

Whether the Select All button is selected depends on whether each check box is selected, that is, whether the isChecked of each element is 1.

    // 判断全选框是否勾选(若每个产品都勾选了,也就是isCheck都等于一,则勾选)
    isAllChecked() {
    
    
      // every:遍历每个元素
      // 只要有一个不等于1就返回false
      return this.cartInfoList.every((item) => item.isChecked == 1);
    },

(2) Modify the selected status of a single product

To modify the status of a single product, you need to send a request to modify isCheckedthe field. This is because this field is used to calculate the total price. We need to calculate the total price if it is checked, and it will not be calculated if it is unchecked.
The interface to modify the product check status needs to pass two parameters: skuId (product id), isChecked (product selected status)

The following are still the steps we are familiar with.

Write interface:

// 修改购物车产品选中状态
export const reqUpdateCheck = (skuId, isChecked) => {
    
    
  return requests({
    
    
    url: `/cart/checkCart/${
      
      skuId}/${
      
      isChecked}`,
    method: "get",
  });
};

Three-link vuex calling interface:

const actions = {
    
    
  // 修改购物车某个产品的选中状态
  async updateChecked({
     
      commit }, {
     
      skuId, isChecked }) {
    
    
    let result = await reqUpdateCheck(skuId, isChecked);
    if (result.code == 200) {
    
    
      return "ok";
    } else {
    
    
      return Promise.reject(new Error("fail"));
    }
  },
};

Each shopping cart product button is configured with a click event (or switching event). If the current check status is 1 (checked), then change it to 0 (unchecked), and vice versa.

Add event in html:

Please add image description

// 修改某个产品的勾选状态
    async updateChecked(cart, $event) {
    
    
      // 带过去的isChecked原本是布尔值,但是我们需要的应该是0或者1
      // console.log(event.target.checked);
      try {
    
    
        // 如果修改成功,再次获取服务器数据
        let checked = event.target.checked ? "1" : "0";
        await this.$store.dispatch("updateChecked", {
    
    
          skuId: cart.skuId,
          isChecked,
        });
        this.getData();
      } catch (error) {
    
    
        alert("修改失败" + error);
      }
    },

(3) When you click to select all, the status of all products will switch accordingly.

Add a click event to the all-selected checkbox. Generally speaking, the main idea is to dispatch a request when clicking Select All. This request needs to change the check status of each product to the current status of the All Select box.
Therefore, you need to traverse the shopping cart data in actions and dispatch requests (of course, it is the same when writing in components, that is the standard point), capture with try-catch, if all requests are successful, then use Promise.all to get the success flag

  // 修改购物车某个产品的选中状态
  async updateChecked({
     
      commit }, {
     
      skuId, isChecked }) {
    
    
    let result = await reqUpdateCheck(skuId, isChecked);
    if (result.code == 200) {
    
    
      return "ok";
    } else {
    
    
      return Promise.reject(new Error("fail"));
    }
  },
  // 点击全选按钮修改所有商品的状态
  changeAllChecked({
     
      dispatch, state }, isChecked) {
    
    
    let promiseAll = [];
    state.cartList[0].cartInfoList.forEach((el) => {
    
    
      try {
    
    
        let promise = dispatch("updateChecked", {
    
    
          skuId: el.skuId,
          isChecked,
        });
        promiseAll.push(promise);
      } catch (error) {
    
    
        console.log(`${
      
      el.skuNum}修改失败`, err);
      }
    });
    return Promise.all(promiseAll);
  },

Then go to the component and add a click event to Select All.

    // 修改全部产品的选中状态
    async changeAllChecked() {
    
    
      try {
    
    
        let isChecked = event.target.checked ? "1" : "0";
        await this.$store.dispatch("changeAllChecked", isChecked);
        this.getData();
      } catch (error) {
    
    
        console.log("全选修改失败", error);
      }
    },

5. Delete shopping cart data

(1) Delete a single product

This is relatively simple. Not much to say, just write the interface - three links - dispatch actions.

  1. Write interface
//删除购物车商品的接口
// /api/cart/deleteCart/{skuId}
export const reqDeleteGoodById = (skuId) => {
    
    
    return requests({
    
    
        url: `/cart/deleteCart/${
      
      skuId}`,
        method: 'delete',
    })
}

When writing interfaces, you should pay attention. Generally, urls with parameters should use backticks (usually the one above the tab on the computer keyboard) instead of quotation marks.

  1. three links
  // 删除购物车产品
  async deleteCartGood({
     
      commit }, skuId) {
    
    
    let result = await reqDeleteCart(skuId);
    console.log("被删除的产品信息" + result);
    if (result.code == 200) {
    
    
      return "ok";
    } else {
    
    
      return Promise.reject(new Error("fail"));
    }
  },
  1. Add click event and dispatch action to re-request and display

First add a click event at the corresponding location:

Please add image description

    // 删除某个购物车产品
    async deleteCartById(cart) {
    
    
      try {
    
    
        // 如果删除成功,再次发请求获取数据进行展示
        await this.$store.dispatch("deleteCartGood", cart.skuId);
        this.getData();
      } catch (error) {
    
    
        console.log("删除失败", error);
      }
    },

(2) Delete all selected products

This logic is somewhat similar to clicking Select All to modify the check status of each product.

  1. In vuex, the shopping cart data is traversed to find out which one is selected, and then the deletion request is sent in sequence. The result of whether the deletion is successful or not is passed to the component using Promise.all
// 删除某个购物车产品
async deleteCartGood({
     
      commit }, skuId) {
    
    
  let result = await reqDeleteCart(skuId);
  console.log("被删除的产品信息" + result);
  if (result.code == 200) {
    
    
    return "ok";
  } else {
    
    
    return Promise.reject(new Error("faile"));
  }
},
// 删除购物车所有被选中的产品
deleteAllCartGood(context) {
    
    
  // context身上有dispatch、commit、getters、state等数据
  // 获取购物车中全部的产品
  // console.log(context.getters.cartList.cartInfoList);
  let PromiseArr = [];
  context.getters.cartList.cartInfoList.forEach((el) => {
    
    
    let promise =
      el.isChecked==1 ? context.dispatch("deleteCartGood", el.skuId): "";
    // 将每一次返回的promise添加到数组中
    PromiseArr.push(promise);
  });
  // 只要有一个失败都返回失败的结果
  return Promise.all(PromiseArr);
},
  1. Click the Delete Selected Product button to take effect, and the request is dispatched in the method.
// 删除被选中的产品
// 这个回调函数收集不到cart.skuId,因为它不在v-for内
async deleteAllCheckedCart() {
    
    
  try {
    
    
    // 派发一个action
    await this.$store.dispatch("deleteAllCartGood");
    this.getData();
  } catch (error) {
    
    
    alert(error.message);
  }
},

6. Add or subtract the number of items in the shopping cart (difficulty)

(1) Analyze

Please add image description

There are three main places to modify the quantity of goods, plus sign, minus sign, and user input. Then every time you change it, you actually have to resend the request. The interface for modifying the quantity of products is the same as the interface for adding to the shopping cart (bring the product ID and quantity to be modified).

Please add image description

Pay attention to the skuNum parameter type here. It does not mean that the modified quantity is directly transmitted to the server, but the difference from the original quantity. The rule for parameters is that passing a positive number represents an increase, and passing a negative number represents a decrease.

(2) Method of configuring requests

First find the location of the modified quantity:

<li class="cart-list-con5">
  <a
    href="javascript:void(0)"
    class="mins"
    @click="handler('mins', -1, cart)"  //减一
    >-</a
  >
  <input
    autocomplete="off"
    type="text"
    minnum="1"
    class="itxt"
    :value="cart.skuNum"
    @change="handler('change', $event.target.value * 1, cart)"  //直接编辑修改
  />
  <a
    href="javascript:void(0)"
    class="plus"
    @click="handler('plus', 1, cart)"  //加一
    >+</a
  >
</li>

Read the notes carefully:

  methods: {
    
    
    // 获取个人购物车
    getData() {
    
    
      this.$store.dispatch("getCartList");
    },
    // 修改某个产品数量
    //有个bug,如果连续点减号,那么可能会变成负数
    //这是因为连续快速点击,请求还来不及发送,数据没改,所以每次disnum都是-1
    //解决办法就是节流,给服务器一些缓冲的时间,防止数据不同步出现上述bug
    handler: throttle(async function (type, disNum, cart) {
    
    
      // type:为了区分三个元素
      // 目前disNum形参:+变化量(1) -变化量(-1)
      // cart:哪一个产品
      // console.log("派发action,通知服务器修改个数" + type);
      // 向服务器发请求,修改数量
      switch (type) {
    
    
        // 加
        case "plus":
          // 带给服务器变化的量
          disNum = 1;
          break;
        case "mins":
          // 判断产品是否大于1,大于1才能减
          if (cart.skuNum > 1) {
    
    
            disNum = -1;
          } else {
    
    
            disNum = 0;
          }
          // 上面这判断可以用三元表达式disNum=cart.skuNum>1?-1:0
          break;
        case "change":
          // 如果用户输入的是非数字字符,则保留原数量不变,也就是disNum=0
          if (isNaN(disNum) || disNum < 1) {
    
    
            disNum = 0;
          } else {
    
    
            // 正常情况(避免用户输入小数,转化为整数)
            disNum = parseInt(disNum) - cart.skuNum; //需要传的是它们的差值(接口规定)
          }
          break;
      }
      // 派发action
      try {
    
    
        await this.$store.dispatch("getShopCart", {
    
    
          skuId: cart.skuId,
          skuNum: disNum,
        });
        //如果成功,就再次请求数据刷新页面
        this.getData();
      } catch {
    
    
        alert("修改数量失败", err);
      }
    }, 800),
  },

At this point, the notes for the shopping cart module have basically been sorted out. The next note is the login registration module.

Guess you like

Origin blog.csdn.net/weixin_56498069/article/details/132927750
Recommended