1、Count.vue
<template>
<div
class="number-container d-flex justify-content-center align-items-center"
>
<button @click="sub" type="button" class="btn btn-light btn-sm">-</button>
<span class="number-box">{{ num }}</span>
<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() {
const obj = { id: this.id, value: this.num + 1 };
bus.$emit("shareAdd", obj);
},
sub() {
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: {
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";
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");
if (res.status === 200) {
this.lists = res.list;
}
},
getNewState(val) {
this.lists.some((item) => {
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: {
allState() {
console.log(this.lists);
return this.lists.every((item) => item.goods_state);
},
amt() {
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>