vite+Vue3实现购物车

一、购物车页面绘制

在这里插入图片描述
创建vite项目

 npm init vite-app item_vite

安装less

npm i less less-loader -D 

导入

bootstrap.css

bootcss

在App.vue中导入头部尾部以及内容区域

<template>
  <div id="mater container">
    <Head></Head>
    <BodyShop :listDate="listDate"></BodyShop>
    <FootShop></FootShop>
  </div>
</template>

<script>
import Head from "./components/header.vue";
import BodyShop from "./components/body.vue";
import FootShop from "./components/footer.vue";
import bus from "./EventBus/event";
export default {
  name: "App",
  components: {
    Head,
    BodyShop,
    FootShop,
  },
  data() {
    return {
      listDate: [
        {
          id: 1,
          picurl:
            "http://img14.360buyimg.com/n7/jfs/t1/28128/7/18573/161061/62e14d6cE1e18d640/3cf8ed57a7dabd5a.jpg",
          descript: "女士风衣",
          price: "188",
          count: 1,
          check: true,
        },
        {
          id: 2,
          picurl:
            "http://img12.360buyimg.com/n2/jfs/t1/29308/30/19373/53535/6308aebdE40f8ccb3/0b7c29e0e1623fdb.jpg",
          descript: "女士冲锋衣",
          price: "288",
          count: 1,
          check: false,
        },
      ],
    };
  },
  created() {
    bus.on("countChange", (count) => {
      console.log("数值发生变化");
    });
  },
};
</script>
<style lang="less" scoped>
#mater {
  height: 500px;
}
</style>

分别创建三个组件
body.vue


<template>
  <div class="cart-by-body">
    <ul>
      <li v-for="item in listDate" :key="item.id">
        <div class="picItem">
          <div class="left-check">
            <label class="radio-inline group_allcheck">
              <input type="checkbox" class="inputcheck" v-model="item.check" />
            </label>
          </div>
          <div class="pic">
            <img :src="item.picurl" alt="" />
          </div>
          <div class="right-box">
            <div class="top-title">{{ item.descript }}</div>
            <div class="bottom-bar">
              <div class="price"><span>{{ item.price }}</span>
              </div>
              <ShopCount class="ev-num"></ShopCount>
            </div>
          </div>
        </div>
      </li>
    </ul>
  </div>
</template>
<script>
import ShopCount from "./shopCountBtn.vue";
export default {
  props: {
    listDate: {
      type: Array,
      required: true,
      default: [
        {
          id: 1,
          picurl:
            "http://img14.360buyimg.com/n7/jfs/t1/28128/7/18573/161061/62e14d6cE1e18d640/3cf8ed57a7dabd5a.jpg",
          descript: "女士风衣",
          price: "188",
          count: 1,
          check: true,
        },
      ],
    },
  },
  data() {
    return {};
  },
  components: {
    ShopCount,
  },
};
</script>
<style lang="less">
.picItem {
  margin: 0 auto;
  margin-left: 0;
  margin-right: 0;
  margin-top: 40px;
  width: 95%;
  height: 100px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  .pic {
    width: 30%;
    height: 100%;
    img {
      max-width: 100%;
      height: 100%;
    }
  }
}
.left-check {
  overflow: hidden;
  width: 10%;
  .inputcheck {
    margin: 0px;
  }
}

.right-box {
  flex: 1;
  height: 100%;
  position: relative;
  overflow: hidden;
  .top-title {
    height: 60px;
    padding: 0 5px;
    text-align: left;
    width: 100%;
    overflow: hidden;
    font-size: 14px;
    word-wrap: break-word;
    font-weight: 600;
  }
  .bottom-bar {
    height: 40px;
    bottom: 0;
    left: 0;
    padding: 0 5px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    .ev-num {
      width: 50%;
    }
    .price {
      color: rgb(230, 43, 43);
      font-weight: 700;
    }
  }
}

.group_allcheck {
  padding-left: 0px !important;
  margin: 0;
}
</style>

footer.vue

<template>
  <div class="foot_container">
    <div class="box">
      <ul>
        <li>
          <label class="radio-inline group_allcheck">
            <input
              type="checkbox"
              value="全选"
              v-model="check"
              class="inputcheck"
            />
            全选
          </label>
        </li>
        <li>
          合计:
          <span class="redcolor">232.00</span>
        </li>
        <li>
          <button
            type="button"
            class="btn btn-primary resetbtn"
            @clink="submitPrice(totalPrice)"
          >
            结算(<span>{{ totalPrice }}</span
            >)
          </button>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
export default {
  props: {},
  data() {
    return {
      totalPrice: 55,
      check: false,
    };
  },
  methods: {
    submitPrice(totalPrice) {
      console.log(totalPrice);
    },
  },
};
</script>
<style lang="less" scoped>
.foot_container {
  background-color: #fff;
  z-index: 999;
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 50px;
  text-align: center;
  line-height: 50px;
  border-top: 1px solid #ccc;
}
.box {
  margin: 0 auto;
  width: 95%;
  font-size: 13px;
  ul {
    padding: 0;
    margin: 0;
    display: flex;
    justify-content: space-between;
    li {
      float: left;
      height: 50px;
    }
  }
}

.group_allcheck {
  display: flex;
  justify-content: space-between;
  align-items: center;
  .inputcheck {
    margin: 0px;
    margin-right: 4px;
  }
}
.redcolor {
  color: rgb(241, 123, 12);
  font-weight: 700;
}
.resetbtn {
  background: orange;
  border: none;
  height: 35px;
  border-radius: 19px;
  font-size: 12px;
}
</style>

header.vue

<!--
 * @Author: wangxue 3208230974@qq.com
 * @Date: 2022-09-15 16:35:16
 * @LastEditors: wangxue 3208230974@qq.com
 * @LastEditTime: 2022-09-15 17:57:14
 * @FilePath: \code-shopping-car\src\components\header.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
  <div :style="{backgroundColor:bgcolor,color:color,fontSize:fsize+'px'}" class="header_container">{{title}}</div>
</template>
<script>
export default {
  props: {
    title: {
      type: String,
      default: "购物车",
    },
    color: {
      type: String,
      default: "white",
    },
     bgcolor: {
      type: String,
      default: "#f1bb55",
    },
    fsize: {
      type: Number,
      default: "12",
    },
  },
};
</script>
<style lang="less" scoped>
.header_container{
    position: fixed;
    z-index: 999;
    top: 0;
    left: 0;
    width: 100%;
    height: 40px;
    text-align: center;
    line-height: 40px;
}
</style>

按钮单独分离一个模板
shopCountBtn.vue

<template>
  <div class="numBtn">
    <div class="cutBtn btn" @click="cutFn">-</div>
    <div class="goodCount">
      <span>{{count}}</span>
    </div>
    <div class="addBtn btn" @click="addFn">+</div>
  </div>
</template>
<script>
import bus from "../EventBus/event";
export default {
  name: "shopCount",
  props: {
   goodAccount:{
       type: Number,
       default:{
           account: 1
       }
   }
  },
  data() {
    return {
      count: this.goodAccount.account,
    };
  },
  created(){
  },
  methods: {
    cutFn() {
      if (this.count >= 2) {
        this.count = this.count - 1;
        bus.emit("countChange", this.count);
      }
    },
    addFn() {
      this.count = this.count + 1;
      bus.emit("countChange", this.count);
    },
  },
};
</script>
<style scoped lang="less">
.numBtn {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 100%;
  .goodCount {
    width: 40%;
    height: 80%;
    border: 1px solid #ccc;
    border-radius: 5px;
    color: rgb(114, 112, 112);
    line-height: 29px;
  }
  .btn {
    width: 30%;
  }
}
</style>

github

功能逻辑实现
全选非全选

    //全选功能
    updataAlllist(check) {
      this.listDate.forEach((v) => (v.check = !check));
    },
<template>
  <div class="foot_container">
    <div class="box">
      <ul>
        <li>
          <label class="radio-inline group_allcheck">
            <input
              type="checkbox"
              value="全选"
              v-model="check"
              class="inputcheck"
              @click="allCheck"
            />
            全选
          </label>
        </li>
        <li>
          合计:
          <span class="redcolor">{{totalAmount}}</span>
        </li>
        <li>
          <button
            type="button"
            class="btn btn-primary resetbtn"
            @clink="submitPrice(totalPrice)"
          >
            结算(<span>{{ goodCount }}</span
            >)
          </button>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
export default {
  props: ["totalAmount","goodCount"],
  data() {
    return {
      check: false,
    };
  },
  emits:["updataAlllist"],
  methods: {
    submitPrice(totalPrice) {
      console.log(totalPrice);
    },
      //全选功能实现
      //修改父元素中的list值
      allCheck(){
        console.log(this.check)
        this.$emit("updataAlllist",this.check)
      }
  },
};
</script>
<style lang="less" scoped>
.foot_container {
  background-color: #fff;
  z-index: 999;
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 50px;
  text-align: center;
  line-height: 50px;
  border-top: 1px solid #ccc;
}
.box {
  margin: 0 auto;
  width: 95%;
  font-size: 13px;
  ul {
    padding: 0;
    margin: 0;
    display: flex;
    justify-content: space-between;
    li {
      float: left;
      height: 50px;
    }
  }
}

.group_allcheck {
  display: flex;
  justify-content: space-between;
  align-items: center;
  .inputcheck {
    margin: 0px;
    margin-right: 4px;
  }
}
.redcolor {
  color: rgb(241, 123, 12);
  font-weight: 700;
}
.resetbtn {
  background: orange;
  border: none;
  height: 35px;
  border-radius: 19px;
  font-size: 12px;
}
</style>

总额

  totalprice() {
      var total = 0;
      this.listDate.forEach((v) => {
        if (v.check) {
          total = v.price * v.count + total;
        }
      });
      return total;
    },

商品选择数量

   goodNum() {
    var total = 0;
    this.listDate.forEach((v) => {
      if (v.check) {
        total = total + 1;
      }
    });
    this.goodNum= total;
    return total;
  },

eventBus更改count值

  created() {
    bus.on("countChange", (map) => {
       console.log(map.goodId)
      this.listDate.forEach((v) => {
        if(v.id==map.goodId){
          this.listDate[map.goodId-1].count= map.goodCount
          // map.goodCount
        }
      });
    });
  },

在这里插入图片描述
商品管理
ui绘制
header

<template>
  <div
    :style="{ backgroundColor: bgcolor, color: color, fontSize: fsize + 'px' }"
    class="header_container"
  >
    <span>{{ title }}</span
    ><button v-if="openManage" type="button" class="btn btn-warning manage" @click="manageBtnFn" >
     完成
    </button>
    <button v-else type="button" class="btn btn-warning manage" @click="manageBtnFn" >
    管理
    </button>
  </div>
</template>
<script>
import bus from "../EventBus/event";
export default {
  data(){
    return {
      openManage:false
    };
  },
  methods: {
    manageBtnFn() {
      this.openManage = !this.openManage;
      bus.emit("changeManageBtn", this.openManage);
    },
  },
  props: {
    title: {
      type: String,
      default: "购物车",
    },
    color: {
      type: String,
      default: "white",
    },
    bgcolor: {
      type: String,
      default: "#f1bb55",
    },
    fsize: {
      type: Number,
      default: "12",
    },
  },
};
</script>
<style lang="less" scoped>
.header_container {
  position: fixed;
  z-index: 999;
  top: 0;
  left: 0;
  width: 100%;
  height: 40px;
  text-align: center;
  line-height: 40px;
  .manage {
    position: absolute;
    margin-right: 7px;
    margin-top: 7px;
    right: 0;
    height: 26px;
    border: none;
    border-radius: 20px;
    font-size: 12px;
    background-color: #f1bb55;
    &:hover {
      background-color: #ec971f;
    }
  }
}
</style>

footer

<template>
  <div class="foot_container">
    <div class="box">
      <ul>
        <li>
          <label class="radio-inline group_allcheck">
            <input
              type="checkbox"
              value="全选"
              v-model="check"
              class="inputcheck"
              @click="allCheck"
            />
            全选
          </label>
        </li>
        <li v-show="!controlDel">
          合计:
          <span class="redcolor">{{ totalAmount }}</span>
        </li>
        <li v-show="controlDel">
          <button type="button" class="btn btn-warning delBtn">删除</button>
        </li>
        <li v-show="!controlDel">
          <button
            type="button"
            class="btn btn-primary resetbtn"
            @click="submitPrice"
          >
            结算(<span>{{ goodCount }}</span
            >)
          </button>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
import bus from "../EventBus/event";
export default {
  props: ["totalAmount", "goodCount"],
  data() {
    return {
      check: false,
      controlDel: false,
    };
  },
  created() {
    bus.on("changeManageBtn", (v) => {
      this.controlDel = v;
    });
  },
  emits: ["updataAlllist"],
  methods: {
    submitPrice() {
      console.log(this.totalAmount);
      if (this.totalAmount == 0 || this.totalAmount == "") {
        alert("请选择商品");
      } else {
        alert("当前支付金额为" + this.totalAmount);
      }
    },
    //全选功能实现
    //修改父元素中的list值
    allCheck() {
      console.log(this.check);
      this.$emit("updataAlllist", this.check);
    },
  },
};
</script>
<style lang="less" scoped>
.foot_container {
  background-color: #fff;
  z-index: 999;
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 50px;
  text-align: center;
  line-height: 50px;
  border-top: 1px solid #ccc;
}
.box {
  margin: 0 auto;
  width: 95%;
  font-size: 13px;
  ul {
    padding: 0;
    margin: 0;
    display: flex;
    justify-content: space-between;
    li {
      float: left;
      height: 50px;
    }
    .delBtn {
      width: 100px;
    }
  }
}

.group_allcheck {
  display: flex;
  justify-content: space-between;
  align-items: center;
  .inputcheck {
    margin: 0px;
    margin-right: 4px;
  }
}
.redcolor {
  color: rgb(241, 123, 12);
  font-weight: 700;
}
.resetbtn {
  background: #f0ad4e;
  border: none;
  height: 35px;
  border-radius: 19px;
  font-size: 12px;
}
</style>

在这里插入图片描述进阶版
使用在线api以及more,filter优化

<template>
  <div>
    <Head></Head>
    <BodyShop :listDate="list"></BodyShop>
    <FootShop
      @delGoodlist="delList"
      @updataAlllist="updataAlllist"
      :totalAmount="totalprice"
      :goodCount="goodNum"
      :isFull="fullState"
    ></FootShop>
  </div>
</template>

<script>
import axios from "axios";
import Head from "./components/header.vue";
import BodyShop from "./components/body.vue";
import FootShop from "./components/footer.vue";
import bus from "./EventBus/event";
export default {
  name: "App",
  components: {
    Head,
    BodyShop,
    FootShop,
  },
  data() {
    return {
      totalprice: 0,
      goodNum: 0,
      list: [],
      fullState: false,
    };
  },
  created() {
    this.initCartList();
    bus.on("countChange", (map) => {
      this.list.some((v) => {
        if (v.id == map.goodId) {
          this.list[v.id - 1].goods_count = map.goodCount;
        }
      });
      //   this.list.forEach((v) => {
      //     if (v.id == map.goodId) {
      //       this.list[v.id - 1].goods_count = map.goodCount;
      //     }
      //   });
    });
  },
  methods: {
    async initCartList() {
      const { data: res } = await axios.get("https://www.escook.cn/api/cart");
      if (res.status === 200) {
        this.list = res.list;
      }
    },
    //全选功能
    updataAlllist(check) {
      this.list.forEach((v) => (v.goods_state = !check));
    },
    delList() {},
  },
  computed: {
    totalprice() {
      return this.list
        .filter((item) => item.goods_state)
        .reduce((t, item) => (t += item.goods_price * item.goods_count), 0);
      //   var total = 0;
      //   this.list.forEach((v) => {
      //     if (v.goods_state) {
      //       total = v.goods_price * v.goods_count + total;
      //     }
      //   });
      //   return total;
    },
    //是否是全选状态
    fullState() {
      return this.list.every((item) => item.goods_state === true);
    },
    goodNum() {
      return this.list
        .filter((item) => item.goods_state)
        .reduce((t, item) => (t += item.goods_count), 0);
    },
  },
};
</script>
<style lang="less" scoped></style>

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可可鸭~

想吃糖~我会甜

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值