其五:购物车部分开发
布局
使用Vuex存储购物车数据
数据结构
第一层:商户id;第二层:商品id为索引,内容是商品数据。
state: {
cartList: {
/* 1: {
1: {
_id: "1",
name: "番茄 250g / 份",
imgUrl: "http://www.dell-lee.com/imgs/vue3/tomato.png",
sales: 10,
price: 33.6,
oldPrice: 39.6,
count: 5,
},
}, */
},
}
在页面上展示
不知道为啥我写这个
{{cartList?.[shopId]?.[item._id]?.count || 0}}
在页面上会报错,于是我改成了
{{ getCartItemCount(item._id) || 0 }}
//mounted内
//this.cartListByShopId = this.$store.state.cartList[this.router_id]
getCartItemCount(id) {
console.log(id, typeof id)
console.log(this.cartListByShopId[id]?.count)
return this.cartListByShopId[id]?.count
}
但如果进入的页面没有购物车数据,会报
Error in render: "TypeError: Cannot read property '1' of undefined"
没有购物车数据意味着cartListByShopId是Undefined,于是改一改
getCartItemCount(id) {
if (this.cartListByShopId) {
return this.cartListByShopId[id]?.count
} else {
return undefined
}
}
但是这样就无法及时更新数据了,点击添加按钮的时候,不会再调用一次getCartItemCount(item._id)。
我的解决办法是让{{}}这一部分重新渲染,每次点击+按钮的时候就重新渲染。
<span v-if="showNums">{{getCartItemCount(item._id) || 0}}</span>
//+按钮事件里加入
this.showNums = false
this.showNums = true
同样的,当该商品的购物车的数量为空时,-按钮和数量应该不显示
为了避免层级太复杂,使用template把-按钮和数量包起来,初次进入该页面的时候,这两部分是否显示由商品的购物车的数量决定,当按下+按钮时,这两部分应该显示。
<template v-if="getCartItemCount(item._id) ||showTemp[index]">
<span class="muls">-</span>
<span v-if="showNums">{{getCartItemCount(item._id) || 0}}</span>
</template>
其中showTemp数组结构为:
[false,false,false,false]//[0]为false表示index===0的商品不展示
//+按钮事件里加入
this.$set(this.showTemp, index, true)
这样就能获得响应式更新了。
加和减
加购物车这个事件放在addItemCount函数里执行。
- 需要传入shopid、itemid、item,其他参数自定
- 调用$store.commit(“addItemCount”)将这三个参数传入
- 其他操作
在store的mutations下进行真正的添加操作。
addItemCount(state, payload) {
const { shopid, itemid, item } = payload
//查询shopid下有没有购物车,没有则初始化
let shopInfo = state.cartList[shopid]
if (!shopInfo) {
shopInfo = {}
}
//查询itemid下有没有商品,没有,则初始化
let product = shopInfo[itemid]
if (!product) {
product = item
product.count = 0
}
//记得数量+1
product.count += 1
shopInfo[itemid] = product
state.cartList[shopid] = shopInfo
}
//购物车数据-添加
addItemCount(shopid, itemid, item, index) {
this.$store.commit("addItemCount", {
shopid,
itemid,
item,
})
//重新渲染一次选择商品的数量
this.reDrawing()
//重新渲染一次数量区的template
this.$set(this.showTemp, index, true)
}
减这个事件放在subItemCount函数里执行
- 需要传入shopid、itemid,其他参数自定
- 操作前先判断一下,该商品已添加的数量是否为1
- 当减去后数量为0时,应更改showTemp里的数据,让-按钮和数量不显示
- 调用$store.commit(“subItemCount”)将两个参数传入
- 其他操作
在store的mutations下进行真正的减去操作。
subItemCount(state, payload) {
const { shopid, itemid } = payload
//找到shopid下的itemid
let shopInfo = state.cartList[shopid]
let product = shopInfo[itemid]
//确认下数量是不是大于等于1
if (product.count >= 1) {
product.count--
}
shopInfo[itemid] = product
state.cartList[shopid] = shopInfo
}
//购物车数据-减去
subItemCount(shopid, itemid, index) {
this.$store.commit("subItemCount", {
shopid,
itemid,
})
//重新渲染一次选择商品的数量
this.reDrawing()
if (this.getCartItemCount(itemid) === 0) {
this.$set(this.showTemp, index, false)
}
}
啊,我是天才。
将数据同步到cart组件中
期望是这么一种效果:当点击shopContent组件的加减按钮时,cart组件上小篮筐响应式地显示商品种类数量。
使用vuex的getters特性获取购物车长度,以获取商品种类数量
getters: {
total_nums: (state) => (id) => {
let length = 0
if (state.cartList[id]) {
//判断该店铺下有没有购物车
const cart = state.cartList[id]
//注意,state.cartList[id]是一个对象
for (const item in cart) {
//还要排除count=0的情况
if (cart[item].count !== 0) {
length++
}
}
} else {
length = 0
}
return length
},
},
由于获取到的total_nums也不是响应式的,所以采用v-if刷新法来实现响应式。
分析一下:点击事件在shopContent触发,cart组件无法捕捉到该点击事件。采取了兄弟组件传值的方式,以父组件作为媒介。
则事件传播的路径为:
shopContent->shop->cart
其中,shop组件是这两个兄弟组件的父组件。
shopContent中需要添加以下代码。
-
使用props接收父组件的一个事件
-
在加号减号的触发事件中,调用这个事件
props: ["triggerCart"], ... addItemCount&subItemCount: //触发父组件事件,让购物车组件刷新 this.triggerCart()
shop需要添加以下代码。
-
给shop-content传递触发事件
-
给cart绑定ref值
-
监听传递给shop-content的事件,并在该事件触发时,调用cart里的函数
<shop-content :triggerCart="triggerCart" /> <cart ref="cart" />
methods: triggerCart() { this.$refs.cart.refresh() }
cart需要添加以下代码。
-
展示total_nums的值,且用v-if来控制
-
refresh函数,将v-if绑定的值重置
<span class="cart__icon__num" v-if="showNums">{{ getTotal_nums() }}</span>
methods: { getTotal_nums() { //console.log(this.$store.getters.total_nums(this.router_id)) return this.$store.getters.total_nums(this.router_id) }, refresh() { this.showNums = false this.showNums = true }, },
这样,购物车数据同步就实现了。价格也可以用相同的方法实现,就不重复写了。
然后 暂时先坑着,我想写别的了。