19、购物车案例

1、Count.vue
<template>
  <div
    class="number-container d-flex justify-content-center align-items-center"
  >
    <!-- 减 1 的按钮 -->
    <button @click="sub" type="button" class="btn btn-light btn-sm">-</button>
    <!-- 购买的数量 -->
    <span class="number-box">{{ num }}</span>
    <!-- 加 1 的按钮 -->
    <button @click="add" type="button" class="btn btn-light btn-sm">+</button>
  </div>
</template>

<script>
import bus from "@/components/eventBus.js";
export default {
  props: {
    id: {
      type: Number,
      required: true,
    },
    num: {
      type: Number,
      required: 1,
    },
  },
  methods: {
    add() {
      // 将该对象发送给App组件
      const obj = { id: this.id, value: this.num + 1 };
      // 通过bus发送数据
      bus.$emit("shareAdd", obj);
    },
    sub() {
      // 商品数量等于1
      if (this.num === 1) {
        return;
      }
      const obj = { id: this.id, value: this.num - 1 };
      bus.$emit("shareSub", obj);
    },
  },
};
</script>

<style lang="less" scoped>
.number-box {
  min-width: 30px;
  text-align: center;
  margin: 0 5px;
  font-size: 12px;
}

.btn-sm {
  width: 30px;
}
</style>
2、Header.vue
<template>
  <div class="header-container">{{ title }}</div>
</template>

<script>
export default {
  props: {
    title: {
      default: "",
    },
  },
};
</script>

<style lang="less" scoped>
.header-container {
  font-size: 12px;
  height: 45px;
  width: 100%;
  background-color: #1d7bff;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #fff;
  position: fixed;
  top: 0;
  z-index: 999;
}
</style>

3、Goods.vue
<template>
  <div class="goods-container">
    <!-- 左侧图片 -->
    <div class="thumb">
      <div class="custom-control custom-checkbox">
        <!-- 复选框 -->
        <input
          type="checkbox"
          class="custom-control-input"
          :id="'cb' + id"
          :checked="state"
          @change="stateChange"
        />
        <label class="custom-control-label" :for="'cb' + id">
          <!-- 商品的缩略图 -->
          <img :src="picture" alt="" />
        </label>
      </div>
    </div>
    <!-- 右侧信息区域 -->
    <div class="goods-info">
      <!-- 商品标题 -->
      <h6 class="goods-title">{{ title }}</h6>
      <div class="goods-info-bottom">
        <!-- 商品价格 -->
        <span class="goods-price">¥{{ price }}</span>
        <!-- 商品的数量 -->
        <Count :num="count" :id="id"></Count>
      </div>
    </div>
  </div>
</template>

<script>
import Count from "@/components/Counter/Counter.vue";
export default {
  components: {
    Count,
  },
  props: {
    // 商品的id
    // 子组件中商品的勾选状态变化之后,需要告诉父组件
    // 通过id修改对应商品的勾选状态
    id: {
      required: true,
      type: Number,
    },
    title: {
      default: "",
      type: String,
    },
    // 要渲染的图片
    picture: {
      default: "",
      type: String,
    },
    // 每件商品的价格
    price: {
      default: 0,
      type: Number,
    },
    // 商品勾选的状态
    state: {
      default: true,
      type: Boolean,
    },
    // 商品的购买数量
    count: {
      default: 1,
      type: Number,
    },
  },
  methods: {
    // 只要复选框的选中状态发生变化,就会调用该方法
    stateChange(e) {
      // 复选框的最新状态
      const newState = e.target.checked;
      // 触发自定义事件
      this.$emit("state-change", { id: this.id, value: newState });
    },
  },
};
</script>

<style lang="less" scoped>
.goods-container {
  + .goods-container {
    border-top: 1px solid #efefef;
  }
  padding: 10px;
  display: flex;
  .thumb {
    display: flex;
    align-items: center;
    img {
      width: 100px;
      height: 100px;
      margin: 0 10px;
    }
  }

  .goods-info {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;
    .goods-title {
      font-weight: bold;
      font-size: 12px;
    }
    .goods-info-bottom {
      display: flex;
      justify-content: space-between;
      .goods-price {
        font-weight: bold;
        color: red;
        font-size: 13px;
      }
    }
  }
}
</style>
4、Footer.vue
<template>
  <div class="footer-container">
    <!-- 左侧的全选 -->
    <div class="custom-control custom-checkbox">
      <input
        type="checkbox"
        class="custom-control-input"
        id="cbFull"
        :checked="selectAll"
        @change="allChange"
      />
      <label class="custom-control-label" for="cbFull">全选</label>
    </div>

    <!-- 中间的合计 -->
    <div>
      <span>合计:</span>
      <span class="total-price">¥{{ amount }}</span>
    </div>

    <!-- 结算按钮 -->
    <button type="button" class="btn btn-primary btn-settle">
      结算({{ allNum }})
    </button>
  </div>
</template>

<script>
export default {
  props: {
    // 定义一个全选的状态
    selectAll: {
      type: Boolean,
      default: false,
    },
    amount: {
      type: Number,
      default: 0,
    },
    // 已勾选商品的总数量
    allNum: {
      type: Number,
      default: 0,
    },
  },
  methods: {
    allChange(e) {
      // 触发自定义事件
      this.$emit("all-change", e.target.checked);
    },
  },
};
</script>

<style lang="less" scoped>
.footer-container {
  font-size: 12px;
  height: 50px;
  width: 100%;
  border-top: 1px solid #efefef;
  position: fixed;
  bottom: 0;
  background-color: #fff;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10px;
}

.custom-checkbox {
  display: flex;
  align-items: center;
}

#cbFull {
  margin-right: 5px;
}

.btn-settle {
  height: 80%;
  min-width: 110px;
  border-radius: 25px;
  font-size: 12px;
}

.total-price {
  font-weight: bold;
  font-size: 14px;
  color: red;
}
</style>

5、App.vue
<template>
  <div class="app-container">
    <!-- 页面头部 -->
    <Header :title="msg"></Header>
    <Goods
      :id="item.id"
      :title="item.goods_name"
      :picture="item.goods_img"
      :price="item.goods_price"
      :state="item.goods_state"
      v-for="item in lists"
      :key="item.id"
      :count="item.goods_count"
      @state-change="getNewState"
    ></Goods>
    <!-- 页面尾部 -->
    <Footer
      :amount="amt"
      :selectAll="allState"
      @all-change="changeAll"
      :allNum="totalNum"
    ></Footer>
  </div>
</template>

<script>
// 导入需要的组件
import Header from "@/components/Header/Header.vue";
import Goods from "@/components/Goods/Goods.vue";
import Footer from "@/components/Footer/Footer.vue";
import bus from "@/components/eventBus.js";
// 导入axios请求库
import axios from "axios";
export default {
  components: {
    Header,
    Goods,
    Footer,
  },
  data() {
    return {
      msg: "购物车案例",
      lists: [],
    };
  },
  methods: {
    async initCartList() {
      const { data: res } = await axios.get("https://www.escook.cn/api/cart");
      // console.log(res);
      if (res.status === 200) {
        this.lists = res.list;
      }
      // console.log(this.lists);
    },
    // 接收子组件传递过来的数据
    getNewState(val) {
      // console.log(val);
      // 循环遍历的每一项
      this.lists.some((item) => {
        // console.log(item.id);
        // console.log(val.id);
        if (item.id === val.id) {
          item.goods_state = val.value;
          console.log(item.goods_state);
          return true;
        }
      });
    },
    // 点击全选或者全不选
    changeAll(val) {
      console.log(val);
      this.lists.forEach((item) => (item.goods_state = val));
    },
  },
  computed: {
    // 动态计算出全选的状态是true还是false
    allState() {
      console.log(this.lists);
      // every():如果循环的每一项都为true,则返回true,否则返回false
      return this.lists.every((item) => item.goods_state);
    },
    // 已勾选商品的总价格
    amt() {
      // 1. 先过滤已经勾选的商品
      // 2. 再reduce累加求和
      return this.lists
        .filter((item) => item.goods_state)
        .reduce(
          (total, item) => (total += item.goods_price * item.goods_count),
          0
        );
    },
    // 已勾选商品的总数量
    totalNum() {
      return this.lists
        .filter((item) => item.goods_state)
        .reduce((sum, item) => (sum += item.goods_count), 0);
    },
  },
  created() {
    // 调用请求数据的方法
    this.initCartList();
    // 接收数据
    bus.$on("shareAdd", (val) => {
      this.lists.some((item) => {
        if (item.id === val.id) {
          item.goods_count = val.value;
          return true;
        }
      });
    });
    bus.$on("shareSub", (val) => {
      this.lists.some((item) => {
        if (item.id === val.id) {
          item.goods_count = val.value;
          return true;
        }
      });
    });
  },
};
</script>

<style lang="less" scoped>
.app-container {
  padding-top: 45px;
  padding-bottom: 50px;
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值