【Vue】《从0开始写一个WebApp》其五

其五:购物车部分开发

布局

在这里插入图片描述

使用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
    		},
    	},
    

在这里插入图片描述

这样,购物车数据同步就实现了。价格也可以用相同的方法实现,就不重复写了。

然后 暂时先坑着,我想写别的了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值