目录
1 效果
2 接口数据
3 App.vue
4 HEADER.vue
5 COUNT.vue
6 GOODS.vue
7 FOOTER.vue
1 效果
由四种子组件和一个App.vue构成
2 接口数据
返回结果如下
{
"status": 200,
"message": "获取购物车列表数据成功!",
"list": [
{
"id": 1,
"goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
"goods_img": "https://www.escook.cn/vuebase/pics/1.png",
"goods_price": 108,
"goods_count": 1,
"goods_state": true
},
{
"id": 2,
"goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
"goods_img": "https://www.escook.cn/vuebase/pics/2.png",
"goods_price": 129,
"goods_count": 1,
"goods_state": true
},
{
"id": 3,
"goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
"goods_img": "https://www.escook.cn/vuebase/pics/3.png",
"goods_price": 198,
"goods_count": 1,
"goods_state": false
},
{
"id": 4,
"goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
"goods_img": "https://www.escook.cn/vuebase/pics/4.png",
"goods_price": 99,
"goods_count": 1,
"goods_state": false
},
{
"id": 5,
"goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
"goods_img": "https://www.escook.cn/vuebase/pics/5.png",
"goods_price": 156,
"goods_count": 1,
"goods_state": true
},
{
"id": 6,
"goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
"goods_img": "https://www.escook.cn/vuebase/pics/6.png",
"goods_price": 142.8,
"goods_count": 1,
"goods_state": true
},
{
"id": 7,
"goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
"goods_img": "https://www.escook.cn/vuebase/pics/7.png",
"goods_price": 219,
"goods_count": 2,
"goods_state": true
},
{
"id": 8,
"goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
"goods_img": "https://www.escook.cn/vuebase/pics/8.png",
"goods_price": 178,
"goods_count": 1,
"goods_state": true
},
{
"id": 9,
"goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
"goods_img": "https://www.escook.cn/vuebase/pics/9.png",
"goods_price": 128,
"goods_count": 1,
"goods_state": false
},
{
"id": 10,
"goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
"goods_img": "https://www.escook.cn/vuebase/pics/10.png",
"goods_price": 153,
"goods_count": 1,
"goods_state": false
}
]
}
3 App.vue
<template>
<HEADER title="购物车案例"></HEADER>
<div class="good_list">
<GOODS
v-for="item in good_list"
:key="item.id"
:goods_state_from_other="item.goods_state"
:goods_img="item.goods_img"
:goods_name="item.goods_name"
:goods_price="item.goods_price"
:goods_count="item.goods_count"
:goods_id=item.id
@get_son_state="change_one_check"
></GOODS>
</div>
<FOOTER
:all_checked_from_other=all_checked
:total_price=total_price
:goods_total_num=goods_total_num
@all_check="all_check"
></FOOTER>
</template>
<script>
import FOOTER from '@/components/FOOTER.vue'
import GOODS from '@/components/GOODS.vue'
import HEADER from '@/components/HEADER.vue'
import axios from 'axios'
import bus from '@/components/eventBus.js'
export default {
name: 'App',
components: {
FOOTER,
GOODS,
HEADER
},
data() {
return {
good_list: [],
all_checked:false,
total_price:0,
goods_total_num:0
}
},
methods: {
get_goods_list() {
axios({
method: 'GET',
url: 'https://www.escook.cn/api/cart'
}).then((res) => {
if (res.data.status === 200) {
this.good_list = res.data.list
this.get_footer_infomation()
}
else {
alert(res.data.message)
}
})
},
get_footer_infomation() {
this.all_checked = this.good_list.every((item) => item.goods_state == true)
this.goods_total_num = 0
this.total_price = this.good_list.reduce((sum,item)=>{
if (item.goods_state == true) {
this.goods_total_num = this.goods_total_num + item.goods_count
sum = sum + item.goods_price * item.goods_count
}
return sum
},0)
},
all_check(val) {
for (let i in this.good_list) {
this.good_list[i].goods_state = val
}
},
change_one_check(val) {
console.log(val)
for (let i in this.good_list) {
if (this.good_list[i].id == val.goods_id) {
this.good_list[i].goods_state = val.goods_state
}
}
},
change_one_count(val) {
for (let i in this.good_list) {
if (this.good_list[i].id == val.goods_id) {
this.good_list[i].goods_count = val.goods_count
}
}
}
},
created() {
this.get_goods_list()
},
beforeUpdate() {
this.get_footer_infomation()
bus.$on('counter_send_to_App_goods_count_event',val => {
this.change_one_count(val)
})
}
}
</script>
<style scoped>
.good_list {
margin-top:45px;
margin-bottom:50px;
}
</style>
传数据的时候最好不要将一个整个对象传过去,最好在传的时候就把对象拆散,这样你子组件的复用性会提高
4 HEADER.vue
<template>
<div class="header-container">{{ title }}</div>
</template>
<script>
export default {
props: {
title:{
type:String,
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;
left:0;
z-index: 999;
}
</style>
5 COUNT.vue
COUNT.vue通过eventbus直接与App.uve通信
<template>
<div class="number-container d-flex justify-content-center align-items-center">
<!-- 减 1 的按钮 -->
<button type="button" class="btn btn-light btn-sm" @click="change_num(-1)">-</button>
<!-- 购买的数量 -->
<span class="number-box">{{ goods_count }}</span>
<!-- 加 1 的按钮 -->
<button type="button" class="btn btn-light btn-sm" @click="change_num(1)">+</button>
</div>
</template>
<script>
import bus from './eventBus.js'
export default{
props:{
goods_count_from_other: {
type:Number,
default:1
},
goods_id: {
type:Number,
required:true
}
},
data() {
return {
goods_count:1
}
},
created() {
this.goods_count = this.goods_count_from_other
},
methods: {
change_num(n) {
if (n<0) {
if (this.goods_count > 1) {
this.goods_count = this.goods_count + n
}
}
else {
this.goods_count = this.goods_count + n
}
bus.$emit('counter_send_to_App_goods_count_event',{
goods_count:this.goods_count,
goods_id:this.goods_id
})
},
},
}
</script>
<style lang="less" scoped>
.number-box {
min-width: 30px;
text-align: center;
margin: 0 5px;
font-size: 12px;
}
.btn-sm {
width: 30px;
}
</style>
6 GOODS.vue
<template>
<div class="goods-container">
<!-- 左侧图片 -->
<div class="thumb">
<div class="custom-control custom-checkbox">
<!-- 复选框 -->
<input type="checkbox" class="custom-control-input" :id="'goods' + goods_id" v-model=goods_state />
<label class="custom-control-label" :for="'goods' + goods_id">
<!-- 商品的缩略图 -->
<img :src=goods_img alt=''/>
</label>
</div>
</div>
<!-- 右侧信息区域 -->
<div class="goods-info">
<!-- 商品标题 -->
<h6 class="goods-title">{{ goods_name }}</h6>
<div class="goods-info-bottom">
<!-- 商品价格 -->
<span class="goods-price">¥{{ goods_price }}</span>
<!-- 商品的数量 -->
<COUNTER :goods_count_from_other=goods_count :goods_id=goods_id></COUNTER>
</div>
</div>
</div>
</template>
<script>
import COUNTER from '@/components/COUNTER.vue'
export default {
components: {
COUNTER,
},
props:{
goods_state_from_other:{
type:Boolean,
default:true
},
goods_img:{
type:String,
default:'@/assets/logo.png'
},
goods_name:{
type:String,
required:true
},
goods_price:{
type:Number,
required:true
},
goods_count:{
type:Number,
default:1
},
goods_id:{
type:Number,
required:true
}
},
data() {
return {
goods_state:this.goods_state_from_other
}
},
watch:{
goods_state() {
this.send_father_state()
},
goods_state_from_other() {
this.goods_state = this.goods_state_from_other
}
},
methods:{
send_father_state() {
this.$emit('get_son_state',{goods_id:this.goods_id,goods_state:this.goods_state})
},
}
}
</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>
7 FOOTER.vue
<template>
<div class="footer-container">
<!-- 左侧的全选 -->
<div class="custom-control custom-checkbox">
<!-- <input type="checkbox" class="custom-control-input" id="cbFull" v-model=all_checked /> -->
<input type="checkbox" class="custom-control-input" id="cbFull" @click=all_check :checked="all_checked"/>
<!-- v-model=all_checked -->
<label class="custom-control-label" for="cbFull">全选</label>
</div>
<!-- 中间的合计 -->
<div>
<span>合计:</span>
<span class="total-price">¥{{ total_price.toFixed(2) }}</span>
</div>
<!-- 结算按钮 -->
<button type="button" class="btn btn-primary btn-settle">结算({{ goods_total_num }})</button>
</div>
</template>
<script>
export default {
props:{
all_checked_from_other:{
type:Boolean,
required:true
},
total_price:{
type:Number,
required:true
},
goods_total_num:{
type:Number,
required:true
}
},
data() {
return {
all_checked:this.all_checked_from_other
}
},
methods:{
all_check() {
if (this.all_checked == true) {
this.all_checked = false
}
else {
this.all_checked = true
}
this.$emit('all_check',this.all_checked)
}
},
watch:{
// all_checked() {
// this.all_check()
// },
all_checked_from_other() {
this.all_checked = this.all_checked_from_other
}
}
}
</script>
<style lang="less" scoped>
.footer-container {
font-size: 12px;
height: 50px;
width: 95%;
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>
全选的时候不要使用v-model,当你全选的时候,取消其中一个就会取消全选按钮,取消全选按钮就会取消所有按钮
所以在对于全选按钮,我们不使用 数据驱动视图,仅使用 视图驱动数据,令其仅在点击的时候产生变化