Estuche para carrito de compras Vue

1. Efecto del caso

2. Pasos de implementación

① Inicializar la estructura básica del proyecto

Componente EsHeader del paquete

2.1 Crear y registrar el componente EsHeader

2.2 Componente de encabezado es del paquete

③Solicitar datos de la lista de productos basados ​​en axios (solicitud GET, la dirección es https://www.escook.cn/api/cart)

 

④ Encapsulación de componentes EsFooter

4.1 Crear y registrar el componente EsFooter

 

4.2 Componente de pie de página del paquete

4.2.0 Requisitos de embalaje

4.2.1 El diseño básico de los componentes de renderizado

 

 4.2.2 Encapsular la cantidad del atributo personalizado

4.2.3 Encapsular el total de atributos personalizados

4.2.4 Encapsular atributo personalizado lleno

4.2.5 Encapsular evento personalizado fullChange

Componentes del paquete EsGoods

5.1 Crear y registrar el componente EsGoods

 

5.2 Encapsulación de componentes de es-goods

5.2.0 Requisitos de embalaje

5.2.1 El diseño básico de los componentes de renderizado

 

5.2.2 Encapsular ID de atributo personalizado

 

5.2.3 Encapsulando otros atributos

5.2.4 Encapsular estado de evento personalizado Cambiar

⑥ Encapsulación de componentes  EsCounter

6.1 Estadísticas dinámicas del precio total de los productos verificados

 

6.2 Estadísticas dinámicas del número total de productos controlados

6.3 Realizar la función de seleccionar todo

⑦ Encapsulación de componentes de contador es

 

 

 

principal.js

import { createApp } from 'vue'
import App from './App.vue'
import './assets/bootstrap-3.4.1-dist/css/bootstrap.css'
import './index.css'
//导入axios
import axios from 'axios'
const app = createApp(App);
//配置请求的根路径
axios.defaults.baseURL = 'https://applet-base-api-t.itheima.net';
//将axios挂栽为全局的$http自定义属性
app.config.globalProperties.$http = axios
app.mount('#app')

aplicación.vue 

<template>
  <div class="app-container">
    <es-header title="购物车案例"></es-header>
    <EsGoods
      v-for="item in goodslist"
      :key="item.goods_id"
      :id="item.goods_id"
      :thumb="item.goods_img"
      :title="item.goods_name"
      :price="item.goods_price"
      :count="item.goods_count"
      :checked="item.goods_state"
      @stateChange="onGoodsStateChange"
      @countChange="onGoodsCountChange"
    ></EsGoods>
    <EsFooter
      @fullChange="onFullStateChange"
      :amount="amount"
      :total="total"
    ></EsFooter>
  </div>
</template>

<script>
import EsHeader from "./components/es-header/EsHeader.vue";
import EsFooter from "./components/es-footer/EsFoote.vue";
import EsGoods from "./components/es-goods/EsGoods.vue";

export default {
  name: "MyApp",
  components: { EsHeader, EsFooter, EsGoods },
  data() {
    return {
      //商品列表数据
      goodslist: [],
    };
  },
  created() {
    this.getGoodsList();
  },
  methods: {
    //获取商品列表数据的方法
    async getGoodsList() {
      //1.通过组件实例this访问到全局挂栽的$http属性,并发起Ajax数据请求
      const { data: res } = await this.$http.get("/api/cart");

      if (res.status !== 200) return alert("数据请求失败!");
      this.goodslist = res.list;
      console.log(this.goodslist);
    },
    //监听选中状态变化的事件
    onFullStateChange(isFull) {
      this.goodslist.forEach((x) => (x.goods_state = isFull));
    },
    onGoodsStateChange(e) {
      const findResult = this.goodslist.find((x) => x.goods_id === e.id);
      if (findResult) {
        findResult.goods_state = e.value;
      }
    },
    //监听商品数量变化的事件
    onGoodsCountChange(e) {
      const findResult = this.goodslist.find((x) => x.goods_id === e.id);
      if (findResult) {
        findResult.goods_count = e.value;
      }
    },
  },
  computed: {
    //已勾选商品的总价
    amount() {
      //1.定义商品总价格
      let a = 0;
      //2.循环累加商品总价格
      this.goodslist
        .filter((x) => x.goods_state)
        .forEach((x) => {
          a += x.goods_price * x.goods_count;
        });
      //3.返回累加的结果
      return a;
    },
    //已勾选商品的总数量
    total() {
      let t = 0;
      this.goodslist
        .filter((x) => x.goods_state)
        .forEach((x) => {
          t += x.goods_count;
        });
      return t;
    },
  },
};
</script>
<style lang="less" scoped>
.app-container {
  padding-top: 45px;
}
</style>

 EsHeader.vue

<template>
  <div
    :style="{ backgroundColor: bgcolor, color: color, fontSize: fsize + 'px' }"
    class="header-container"
  >
    {
   
   { title }}
  </div>
</template>

<script>
export default {
  name: "EsHeader",
  props: {
    title: {
      type: String,
      default: "es-header",
    },
    bgcolor: {
      type: String,
      default: "#007BFF",
    },
    color: {
      type: String,
      default: "#ffffff",
    },
    fsize: {
      type: Number,
      default: 12,
    },
  },
};
</script>

<style lang="less" scoped>
.header-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 45px;
  text-align: center;
  line-height: 45px;
  z-index: 999;
}
</style>

 EsFooter.vue

<template>
  <div class="footer-container">
    <!-- 全选区域 -->
    <div class="custom-control custom-checkbox">
      <input
        type="checkbox"
        class="custom-control-input aaa"
        id="fullCheck"
        :checked="isfull"
        @change="onCheckBoxChange"
      />
      <label class="custom-control-label" for="fullCheck">全选</label>
    </div>
    <!-- 合计区域 -->
    <div>
      <span>合计:</span>

      <span class="amount">¥{
   
   { amount.toFixed(2) }}</span>
    </div>
    <!-- 结算按钮 -->
    <button
      type="button"
      class="btn btn-primary btn-settle"
      :disabled="total === 0"
    >
      结算({
   
   { total }})
    </button>
  </div>
</template>

<script>
export default {
  name: "EsFooter",
  emits: ["fullChange"],
  props: {
    //以勾选商品的总价格
    amount: {
      type: Number,
      default: 0,
    },
    //已勾选商品的总数量
    total: {
      type: Number,
      default: 0,
    },
    //全选按钮的选中状态
    isfull: {
      type: Boolean,
      default: false,
    },
  },
  methods: {
    onCheckBoxChange(e) {
      this.$emit("fullChange", e.target.checked);
    },
  },
};
</script>

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

.amount {
  font-weight: bold;
  color: #f10404;
}

.btn-settle {
  min-width: 90px;
  height: 38px;
  border-radius: 19px;
}
</style>

 EsGoods.view

<template>
  <div>
    <div class="goods-container">
      <div class="left">
        <div class="custom-control custom-checkbox">
          <input
            type="checkbox"
            class="custom-control-input"
            :id="id"
            :checked="checked"
            @change="onCheckBoxChange"
          />
          <label class="custom-control-label" :for="id">
            <img :src="thumb" alt="商品图片" class="thumb"
          /></label>
        </div>
      </div>

      <!-- 右侧信息区域 -->
      <div class="right">
        <!-- 商品名称 -->
        <div class="top">{
   
   { title }}</div>
        <div class="bottom">
          <!-- 商品价格 -->
          <div class="price">¥{
   
   { price.toFixed(2) }}</div>
          <!-- 商品数量 -->
          <div class="count">
            <EsCounter :num="count" :min="1" @numChange="getNumbe"></EsCounter>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import EsCounter from "../es-counter/EsCounter.vue";
export default {
  name: "EsGoods",
  props: {
    //商品的id
    id: {
      type: [String, Number],
      required: true,
    },
    //商品的缩略图
    thumb: {
      type: String,
      required: true,
    },
    //商品的名称
    title: {
      type: String,
      required: true,
    },
    //单价
    price: {
      type: Number,
      required: true,
    },
    //数量
    count: {
      type: Number,
      required: true,
    },
    //勾选的状态
    checked: {
      type: Boolean,
      required: true,
    },
  },
  emits: ["stateChange", "countChange"],
  methods: {
    onCheckBoxChange(e) {
      this.$emit("stateChange", {
        id: this.id,
        value: e.target.checked,
      });
    },
    //监听数量值的变化,
    getNumbe(num) {
      this.$emit("countChange", {
        id: this.id,
        value: num,
      });
    },
  },
  components: {
    EsCounter,
  },
};
</script>

<style lang="less" scoped>
.goods-container {
  border-bottom: 1px solid #efefef;

  display: flex;
  padding: 10px;

  .left {
    margin-right: 10px;

    //商品图片
    .thumb {
      display: block;
      width: 100px;
      height: 100px;
      background-color: #efefef;
    }
  }

  //右侧商品名称、单价、数量的样式
  .right {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1;

    .top {
      font-weight: bold;
    }

    .bottom {
      display: flex;
      justify-content: space-between;
      align-items: center;

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

 EsCounter.vue

<template>
  <div class="counter-container">
    <button type="button" class="btn btn-light btn-sm" @click="onSubClick">
      -
    </button>
    <input
      type="number"
      class="form-control form-control-sm ipt-num"
      v-model.number.lazy="number"
    />
    <button type="button" class="btn btn-light btn-sm" @click="onAddClick">
      +
    </button>
  </div>
</template>

<script>
export default {
  name: "EsCounter",
  data() {
    return {
      number: this.num,
    };
  },
  watch: {
    number(newVal) {
      const parseResult = parseInt(newVal);
      if (isNaN(parseResult) || parseResult < 1) {
        this.number = 1;
        return;
      }
      if (String(newVal).indexOf(".") != -1) {
        this.number = parseResult;
        return;
      }
      //触发自定义事件,把最新的number数值传递给组件的使用者
      this.$emit("numChange", this.number);
    },
  },
  emits: ["numChange"],
  props: {
    num: {
      type: Number,
      default: 0,
    },
    min: {
      type: Number,
      //min属性的值默认为NaN,表示不限制最小值
      default: NaN,
    },
  },
  methods: {
    onSubClick() {
      if (!isNaN(this.min) && this.number - 1 < this.min) return;
      this.number--;
    },
    onAddClick() {
      this.number++;
    },
  },
};
</script>

<style lang="less" scoped>
.counter-container {
  display: flex;
  .btn {
    width: 25px;
  }
  //输入框的样式
  .ipt-num {
    width: 34px;
    text-align: center;
    margin: 0 4px;
  }
}
</style>

 

Supongo que te gusta

Origin blog.csdn.net/a_xia_o/article/details/131834173
Recomendado
Clasificación