vue3项目-小兔鲜儿笔记-购物车02
// 有效商品列表
validList(state) {
return state.list.filter((goods) => goods.isEffective && goods.stock > 0)
},
// 有效商品件数
validTotal() {
return this.validList.reduce((p, c) => {
return p + c.count
}, 0)
},
// 有效商品总金额
validAmount() {
return this.validList.reduce((p, c) => {
return p + Number((c.nowPrice * c.count).toFixed(2))
}, 0)
},
// 选中商品列表
selectedList() {
return this.validList.filter((goods) => goods.selected)
},
// 选中商品件数
selectedTotal() {
return this.selectedList.reduce((p, c) => {
return p + c.count
}, 0)
},
// 选中商品总金额
selectedAmount() {
return this.selectedList.reduce((p, c) => {
return p + Number((c.nowPrice * c.count).toFixed(2))
}, 0)
},
// 是否全选
isCheckAll() {
return (
this.validList.length === this.selectedList.length &&
this.selectedList.length !== 0
)
}
<!-- 有效商品 -->
<tbody>
<tr v-if="cartStore.validList.length === 0">
<!-- 合并6列的宽度 -->
<td colspan="6">
<cart-none />
</td>
</tr>
<tr v-for="goods in cartStore.validList" :key="goods.skuId">
<td>
<!-- 通过$event拿到change事件返回的默认参数 -->
<xtx-checkbox
@change="checkOne(goods.skuId, $event)"
:modelValue="goods.selected"
/>
</td>
<td>
<div class="goods">
<router-link to="/">
<img v-lazy="goods.picture" alt="" />
</router-link>
<div>
<p class="name ellipsis">{{ goods.name }}</p>
<!-- 选择规格组件 -->
<CartSku
:attrsText="goods.attrsText"
:skuId="goods.skuId"
@change="($event) => updateCartSku(goods.skuId, $event)"
/>
</div>
</div>
</td>
<td class="tc">
<p>¥{{ goods.nowPrice }}</p>
<p>
比加入时降价
<span class="red">
¥{{ goods.nowPrice - goods.price }}
</span>
</p>
</td>
<td class="tc">
<xtx-numberbox
:max="goods.stock"
@change="($event) => changeCount(goods.skuId, $event)"
:modelValue="goods.count"
/>
</td>
<td class="tc">
<p class="f16 red">
¥{{ Number((goods.nowPrice * goods.count).toFixed(2)) }}
</p>
</td>
<td class="tc">
<p><a href="javascript:;">移入收藏夹</a></p>
<p>
<a
@click="deleteCart(goods.skuId)"
href="javascript:;"
class="green"
>
删除
</a>
</p>
<p><a href="javascript:;">找相似</a></p>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 操作栏 -->
<div class="action">
<div class="batch">
<xtx-checkbox @change="checkAll" :modelValue="cartStore.isCheckAll">
全选
</xtx-checkbox>
<ul>
<li>
<a @click="batchDeleteCart" href="javascript:;">删除所选商品</a>
</li>
<li><a href="javascript:;">移入收藏夹</a></li>
</ul>
</div>
<div class="wrapped">
<span class="totalCount">共 {{ cartStore.validTotal }} 件商品,</span>
<span class="validCount">
已选择 {{ cartStore.selectedTotal }} 件,
</span>
<span class="validAmount">
商品合计:
<span class="red">¥{{ cartStore.selectedAmount }}</span>
</span>
<a @click="checkout" href="javascript:;" class="btn">下单结算</a>
</div>
</div>
2. 购物车页面-单选操作-本地
// 更新购物车中的商品信息
updateGoods(newGoods) {
// newGoods中有些字段可能不完整,要先判断
// newGoods中必须要有skuId,这样才能找到对应商品
const oldGoods = this.list.find((goods) => goods.skuId === newGoods.skuId)
for (const key in newGoods) {
if (
newGoods[key] !== null &&
newGoods[key] !== '' &&
newGoods[key] !== undefined
) {
oldGoods[key] = newGoods[key]
}
}
},
// 修改购物车的状态(选中,数量)
asyncUpdateGoods(newGoods) {
// 必须有skuId,可能有:selected 和 count
return new Promise((resolve, reject) => {
const userStore = useUserStore()
if (userStore.profile.token) {
// 已登录
} else {
// 未登录
this.updateGoods(newGoods)
resolve()
}
})
},
<!-- 通过$event拿到change事件返回的默认参数 -->
<xtx-checkbox
@change="checkOne(goods.skuId, $event)"
:modelValue="goods.selected"
/>
// 单选商品
const checkOne = (skuId, selected) => {
cartStore.asyncUpdateGoods({ skuId, selected })
}
3. 购物车页面-全选操作-本地
-
定义actions
// 做有效商品的全选/反选
checkAllCart(selected) {
return new Promise((resolve, reject) => {
const userStore = useUserStore()
if (userStore.profile.token) {
// 已登录
} else {
// 未登录
const validList = this.list.filter(
(goods) => goods.isEffective && goods.stock > 0
)
// 根据传来的selected状态进行全选/反选
validList.forEach((goods) => {
this.updateGoods({ skuId: goods.skuId, selected })
})
}
})
},
<xtx-checkbox
@change="checkAll"
:modelValue="cartStore.isCheckAll"
>
// 全选与反选商品
const checkAll = (selected) => {
cartStore.checkAllCart(selected)
}
4. 购物车页面-删除操作-本地
// 删除购物车中的商品
deleteCart(skuId) {
const index = this.list.findIndex((goods) => goods.skuId === skuId)
this.list.splice(index, 1)
},
//结合登录逻辑和未登录逻辑的删除购物车中的商品
asyncDeleteCart(skuId) {
return new Promise((resolve, reject) => {
const userStore = useUserStore()
if (userStore.profile.token) {
// 已登录
} else {
// 未登录
this.deleteCart(skuId)
resolve()
}
})
}
// 删除商品
const deleteCart = (skuId) => {
Confirm({ title: '温馨提示', text: '您确认从购物车中删除该商品吗?' })
.then(() => {
cartStore.asyncDeleteCart(skuId).then(() => {
Message({ type: 'success', text: '成功删除商品' })
})
})
.catch((e) => {
console.log(e)
})
}
5. 封装确认框组件
-
实现组件基础结构
<template>
<div class="xtx-confirm" :class="{ fade: fade }">
<div class="wrapper" :class="{ fade: fade }">
<div class="header">
<h3>{{ title }}</h3>
<a
@click="cancel"
href="javascript:;"
class="iconfont icon-close-new"
></a>
</div>
<div class="body">
<i class="iconfont icon-warning"></i>
<span>{{ text }}</span>
</div>
<div class="footer">
<xtx-button @click="cancel" size="mini" type="gray">取消</xtx-button>
<xtx-button @click="submit" size="mini" type="primary">确认</xtx-button>
</div>
</div>
</div>
</template>
const props = defineProps({
title: {
type: String,
default: '温馨提示'
},
text: {
type: String,
default: ''
},
cancelCallback: {
type: Function
},
submitCallback: {
type: Function
}
})
逻辑分析:
Confirm.js
import { createVNode, render } from 'vue'
import XtxConfirm from './xtx-confirm.vue'
// 1.导入被创建的组件
// 2.使用createVNode创建组件的虚拟DOM
// 3.准备一个DOM容器装载组件节点
// 4.使用render函数渲染组件的虚拟节点为真实的DOM节点,并挂载到DOM容器上
const div = document.createElement('div')
div.classList.add('xtx-confirm-container')
document.body.appendChild(div)
export default function ({ title, text }) {
// 返回一个promise对象,点取消和确认都要销毁组件
return new Promise((resolve, reject) => {
// 点击取消,触发reject并销毁组件
const cancelCallback = () => {
render(null, div) // 销毁组件
reject(new Error('取消'))
}
// 点击确认,触发resolve并销毁组件
const submitCallback = () => {
render(null, div)
resolve()
}
const vnode = createVNode(XtxConfirm, { title, text, cancelCallback, submitCallback })
render(vnode, div)
})
}
在调用删除商品时使用确认框:
// 删除商品
const deleteCart = (skuId) => {
Confirm({ title: '温馨提示', text: '您确认从购物车中删除该商品吗?' })
.then(() => {
cartStore.asyncDeleteCart(skuId).then(() => {
Message({ type: 'success', text: '成功删除商品' })
})
})
.catch((e) => {
console.log(e)
})
}
6. 购物车页面-批量删除-本地
// 批量删除购物车商品
batchDeleteCart() {
return new Promise((resolve, reject) => {
const userStore = useUserStore()
if (userStore.profile.token) {
// 已登录
} else {
// 未登录
// 遍历选中商品列表,一个个删除
const selectedList = this.list.filter((goods) => goods.selected)
selectedList.forEach((goods) => {
this.deleteCart(goods.skuId)
})
}
})
},
在购物车页面中点击删除所选商品时调用批量删除商品
// 批量删除商品
const batchDeleteCart = () => {
Confirm({ text: '您确认从购物车中删除所选商品吗?' }).then(() => {
// 点击确认按钮,触发resolve(),使用.then获得resolve的回调
cartStore.batchDeleteCart().then(() => {
Message({ type: 'success', text: '成功删除所选商品' })
})
})
}
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决