Vue2 전자상거래 프런트 엔드 프로젝트 - 장바구니에 추가 기능 및 장바구니 페이지 완성

Vue2 전자상거래 프런트 엔드 프로젝트 - 장바구니에 추가 기능 및 장바구니 페이지 완성

1. 장바구니에 담기

1. 경로가 점프하기 전에 제품 데이터를 서버로 전송하라는 요청을 보냅니다.

(1) 인터페이스 문서를 준수합니다.

실제로 여기서는 기존 항목의 ID와 수량만 백엔드에 전달하면 되며 백엔드는 데이터를 반환할 필요가 없습니다.

이미지 설명을 추가해주세요

이미지 설명을 추가해주세요

(2) 쓰기 인터페이스

이제 우리 모두에게 친숙한 인터페이스를 작성할 시간입니다.

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

(3) 발송 데이터

1. 장바구니에 담기 버튼에 클릭 이벤트 추가

이미지 설명을 추가해주세요

2. 액션을 보낸 후 객체 형태로 데이터를 전달합니다. 첫 번째 키-값 쌍은 제품 ID(검색에서 세부 라우팅으로 이동할 때 전달되는 params 매개변수)이고 두 번째 키-값 쌍은 쇼핑입니다. 자동차 수(이전에 Detail 구성 요소의 데이터에 저장했습니다)

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

3. 작업 측면에서는 구조 분해 및 값 할당을 통해 인터페이스가 호출되고 데이터가 서버(백엔드 데이터베이스)로 전달됩니다.

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

(4) 서버가 상품 데이터를 수신했는지 확인

여기서 요청이 성공했는지, 즉 장바구니에 담을 상품의 데이터를 서버가 받았는지 확인하는 것이 필요하며, 성공하면 라우팅 점프를 수행하고 매개변수를 전달해야 합니다. 실패하면 사용자에게 메시지를 표시해야 합니다.

dispatch이 함수는 호출될 것이며 actions, 이 비동기 함수를 호출하면 객체가 반환됩니다 promise. 이 Promise 객체의 상태와 결과 값은 이 비동기 함수의 반환 값에 따라 달라집니다.

1. Promise가 아닌 객체가 반환되면 성공이고 값이 반환값이 됨
2. Promise 객체가 반환되면 상태 및 결과값은 Promise에 따라 다름
3. 반환값이 작성되지 않고 반환값이 되는 경우 대기 후에 실패한 Promise가 발생하면 예외가 발생하고, 예외가 발생하면 비동기 함수는 실패한 Promise를 반환합니다.
4. 반환 값이 작성되지 않고 Wait 후에 Promise가 성공하면 undefed가 반환됩니다. async 함수는 정의되지 않은 값으로 성공적인 Promise를 반환합니다.

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. 요청 성공 후 경로 점프

라우팅 부품은 일반적으로 pages폴더에 저장되고, 일반 부품은 일반적으로 components폴더에 저장됩니다.

(1) 경로 생성 및 라우팅 규칙 구성

1. 먼저 경로를 만들어 보겠습니다.

이미지 설명을 추가해주세요

2. 라우팅 규칙 구성src/router/routes.js

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

(2) 경로 점프 및 패스 매개변수(로컬 저장 연습)

3. 라우팅 점프 및 매개변수 전송을 위한 코드 작성

이미지 설명을 추가해주세요

여기에는 skuInfo 개체와 제품 수량 shopCarNum이라는 두 가지 값이 전달되어야 합니다(사실 skuInfo를 전달할 필요가 전혀 없으며 창고에 가서 읽으면 됩니다). 매개변수를 전달할 때 params 및 query를 사용하지 않는 것이 가장 좋습니다. 전달된 개체는 skuInfo여야 하므로 개체가 전달되면 주소 표시줄이 깨질 수 있습니다. 여기에 채택된 솔루션은 다음과 같습니다. 세션 저장 - 검토하려면 여기를 클릭하세요.

商品数量shopCarNum1. 숫자가 왜곡되지 않기 때문에 query 를 사용하는 것이 간단합니다 .
2. 세션 저장소(로컬 저장소도 허용됨)를 사용하여 거기로 가져옵니다 skuInfo(사실 웨어하우스에서 직접 읽거나 쿼리를 사용할 수 있습니다.) 이는 주소 표시줄의 잘못된 문자에 지나지 않습니다.)

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);
      }
    },

알아채다:

  • 로컬 저장소는 문자열 형식만 저장할 수 있으므로 개체를 문자열로 변환해야 합니다.JSON.stringify()
  • JSON.parse()로컬 저장소 데이터를 얻으려면 내부의 데이터를 사용하기 전에 내부 문자열을 개체 형식으로 변환해야 합니다 .

이미지 설명을 추가해주세요

그런 다음 페이지에 해당 데이터를 넣습니다.

이미지 설명을 추가해주세요

이미지 설명을 추가해주세요

2. 장바구니 페이지에서 거래를 완료합니다.

클릭하여 제품 세부정보를 확인하고 바로 되돌아가세요. 데이터 웨어하우스가 이미 있으므로 요청을 다시 보낼 필요가 없습니다.

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

다음 단계는 클릭해서 장바구니 결제로 이동한 후, 장바구니 결제 페이지로 점프하고
, 장바구니의 정적 부분을 변경한 후, 경로를 등록하고, 경로 점프를 작성하는 것입니다.

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

이미지 설명을 추가해주세요

1. 방문자 ID 생성

백엔드는 userTempId귀하가 누구인지 확인하기 위해 호출된 요청 헤더 필드를 사용하고 해당 데이터를 귀하에게 반환하는 논리를 여기에 작성해야 합니다.

일반적으로 일반적인 논리는 각 사용자가 자신의 토큰을 갖고 장바구니에 추가하기 위해 클릭한 후 사용자 제품 테이블에 데이터 행을 추가하는 것입니다. 이를 얻으려면 토큰 매개변수를 전달해야 합니다. 해당 제품 목록. 여기서 시뮬레이션을 위해 백엔드는 useTempId 필드를 작성했습니다. 새로 고칠 때 방문자 ID(고유 ID)를 제공하고 이 필드를 로컬 브라우저 방문자로 직접 사용하므로 요청할 때 매개변수를 전달할 필요가 없습니다. 장바구니 데이터.

방문자 ID를 무작위로 생성하는 방법에는 nanoid, uuid, timestamp 등 여러 가지가 있는데 여기서는 uuid를 사용합니다. 영구 저장소가 필요한 경우 localStorage를 사용할 수 있습니다.

1. 페이지 진입 시 먼저 사용자 ID로 타임스탬프를 무작위로 생성합니다.

이미지 설명을 추가해주세요

이미지 설명을 추가해주세요

2. 데이터를 요청할 때 요청 인터셉터의 요청 값으로 id를 사용합니다 userTempId. 즉, 이 ID를 요청 헤더에 넣고 서버에 전달합니다.

이미지 설명을 추가해주세요

3. 이렇게 하면 로컬 저장소에 있는 ID를 수동으로 삭제하지 않는 한, 해당 ID의 장바구니 데이터를 매번 얻을 수 있습니다.

2. 해당 장바구니 데이터를 얻습니다.

쓰기 인터페이스

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

연속된 세 단어로 요청을 보냅니다.

이미지 설명을 추가해주세요

이미지 설명을 추가해주세요

마지막으로 페이지에 데이터를 표시합니다.
이미지 설명을 추가해주세요

이미지 설명을 추가해주세요

3. 체크한 품목의 총 가격을 계산합니다.

이는 계산하기 쉽습니다. isChecked 속성을 사용하여 선택한 항목(isChecked=1)의 가격만 계산하고 forEach를 반복합니다.

이미지 설명을 추가해주세요

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

4. 전체선택과 상품티킹 연계

(1) 전체선택 버튼 선택 여부

모두 선택 버튼의 선택 여부는 각 체크박스의 선택 여부, 즉 각 요소의 isChecked가 1인지 여부에 따라 달라집니다.

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

(2) 단일 상품의 선택 상태 수정

isChecked단일 상품의 상태를 수정하려면 해당 필드 수정 요청을 보내야 합니다. 이는 이 필드가 총 가격을 계산하는 데 사용되기 때문입니다. 체크된 경우 총 가격을 계산해야 하며, 해당 필드는 수정되지 않습니다 . 체크하지 않으면 계산됩니다.
제품 확인 상태를 수정하기 위한 인터페이스는 skuId(제품 ID), isChecked(제품 선택 상태)라는 두 매개변수를 전달해야 합니다.

다음은 여전히 ​​우리에게 익숙한 단계입니다.

쓰기 인터페이스:

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

3링크 vuex 호출 인터페이스:

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"));
    }
  },
};

각 장바구니 상품 버튼은 클릭 이벤트(또는 전환 이벤트)로 구성되어 있으며, 현재 확인 상태가 1(체크)이면 0(체크 해제)으로 변경되고, 그 반대도 마찬가지입니다.

HTML에 이벤트 추가:

이미지 설명을 추가해주세요

// 修改某个产品的勾选状态
    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) 전체 선택을 클릭하면 모든 상품의 상태가 그에 따라 전환됩니다.

모두 선택된 체크박스에 클릭 이벤트를 추가합니다. 일반적으로 주요 아이디어는 모두 선택을 클릭할 때 요청을 발송하는 것입니다. 이 요청은 각 제품의 확인 상태를 전체 선택 상자의 현재 상태로 변경해야 합니다.
따라서 액션 및 디스패치 요청에서 장바구니 데이터를 순회하고(물론 컴포넌트 작성 시에도 동일, 즉 기준점입니다), try-catch로 캡처하고, 모든 요청이 성공하면 Promise를 사용해야 합니다. .all 성공 플래그를 얻으려면

  // 修改购物车某个产品的选中状态
  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);
  },

그런 다음 구성 요소로 이동하여 모두 선택에 클릭 이벤트를 추가합니다.

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

5. 장바구니 데이터 삭제

(1) 단일 상품 삭제

이것은 상대적으로 간단합니다. 말할 것도 없이 인터페이스(3개의 링크)를 작성하고 액션을 전달하면 됩니다.

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

인터페이스를 작성할 때 주의해야 할 점은 일반적으로 매개변수가 있는 URL은 따옴표 대신 백틱(보통 컴퓨터 키보드 탭 위에 있는 것)을 사용해야 한다는 것입니다.

  1. 세 개의 링크
  // 删除购物车产品
  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. 다시 요청하고 표시하려면 클릭 이벤트 및 전달 작업을 추가하세요.

먼저 해당 위치에 클릭 이벤트를 추가합니다.

이미지 설명을 추가해주세요

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

(2) 선택한 상품 모두 삭제

이 논리는 모두 선택을 클릭하여 각 제품의 확인 상태를 수정하는 것과 다소 유사합니다.

  1. vuex에서는 장바구니 데이터를 순회하여 어느 것이 선택되었는지 확인한 후 순차적으로 삭제 요청을 보내고, 삭제 성공 여부에 대한 결과는 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. 선택한 제품 삭제 버튼을 클릭하여 적용하면 요청이 메서드에 전달됩니다.
// 删除被选中的产品
// 这个回调函数收集不到cart.skuId,因为它不在v-for内
async deleteAllCheckedCart() {
    
    
  try {
    
    
    // 派发一个action
    await this.$store.dispatch("deleteAllCartGood");
    this.getData();
  } catch (error) {
    
    
    alert(error.message);
  }
},

6. 장바구니에 담긴 상품의 개수를 더하거나 빼세요(난이도)

(1) 분석

이미지 설명을 추가해주세요

상품 수량을 수정하는 곳은 크게 플러스 기호, 마이너스 기호, 사용자 입력 세 곳이 있는데, 변경할 때마다 실제로 요청을 다시 보내야 합니다. 상품 수량 수정 인터페이스는 장바구니 담기 인터페이스와 동일합니다. (수정할 상품 ID와 수량 지참)

이미지 설명을 추가해주세요

여기서 skuNum 매개변수 유형에 주목하세요. 수정된 수량을 서버에 직접 전송하는 것이 아니라 원래 수량과 차이가 있다는 의미입니다. 매개변수에 대한 규칙은 양수를 전달하면 증가를 나타내고, 음수를 전달하면 감소를 의미한다는 것입니다.

(2) 요청 구성 방법

먼저 수정된 수량의 위치를 ​​찾습니다.

<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>

주의 사항을 주의 깊게 읽으십시오.

  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),
  },

이제 장바구니 모듈에 대한 메모는 기본적으로 정리되었습니다. 다음 참고사항은 로그인 등록 모듈입니다.

Supongo que te gusta

Origin blog.csdn.net/weixin_56498069/article/details/132927750
Recomendado
Clasificación