本地购物车
01-购物车分析
总结:
- 购物车的各种操作都会有两种状态的区分,但是不会在组件中去区分。
- 而是在封装在vuex中的actions中去区分,在组件上只需调用actions即可。
- 在actions中通过user信息去区分登录状态
- 未登录,通过mutations修改vuex中的数据即可,vuex已经实现持久化,会同步保持在本地。
- 已登录,通过api接口去服务端操作,响应成功后通过mutations修改vuex中的数据即可,它也会同步在本地。
- actions函数需要改成 async 函数,返回的是promise对象,才可以在组件判断是否操作成功
注意:
- 登录后,需要合并本地购物车到服务端。
- 退出后,清空vuex数据也会同步清空本地数据。
02-加入购物车
目的:完成商品详情的添加购物车操作,支持未登录状态。
大致步骤:
- 约定本地存储的信息内容
- 编写mutaions添加购物车逻辑
- 编写actions进行添加操作
- 在商品详情页实现添加逻辑
落地代码:
- vuex中的修改数据,获取数据
src/store/modules/cart.js
// 本地:id skuId name picture price nowPrice count attrsText selected stock isEffective
// 线上:比上面多 isCollect 有用 discount 无用 两项项信息
mutations: {
insertCart (state, goods) {
const sameIndex = state.list.findIndex(item => item.skuId === goods.skuId)
// 逻辑:有相同的给goods累加数量,删除相同skuId的商品
if (sameIndex >= 0) {
goods.count = state.list[sameIndex].count + goods.count
state.list.splice(sameIndex, 1)
}
state.list.unshift(goods)
}
},
actions: {
async insertCart (ctx, goods) {
if (ctx.rootState.user.profile.token) {
// 已登录 TODO
} else {
// 未登录
ctx.commit('insertCart', goods)
}
}
}
- 商品详情点击加入购物车
src/views/goods/index.vue
// sku改变时候触发
const changeSku = (sku) => {
if (sku.skuId) {
goods.value.price = sku.price
goods.value.oldPrice = sku.oldPrice
goods.value.inventory = sku.inventory
count.value = 1;
currSku.value = sku
} else {
currSku.value = null
}
}
// 选择的数量
const count = ref(1)
// 加入购物车逻辑
const currSku = ref(null)
const store = useStore()
const insertCart = () => {
if (!currSku.value) {
return Message({text:'请选择商品规格'})
}
if (count.value > goods.inventory) {
return Message({text:'库存不足'})
}
store.dispatch('cart/insertCart', {
id: goods.value.id,
skuId: currSku.value.skuId,
name: goods.value.name,
picture: goods.value.mainPictures[0],
price: currSku.value.price,
nowPrice: currSku.value.price,
count: count.value,
attrsText: currSku.value.specsText,
selected: true,
isEffective: true,
stock: currSku.value.inventory
}).then(() => {
Message({text:'加入购物车成功', type:'success'})
}).catch(() => {
Message({ type: "error", text: "加入购物车失败" });
});
}
return { goods, changeSku, count, insertCart }
03-头部购物车-基础布局
目的:在网站头部购物车图片处,鼠标经过展示购物车列表。
大致步骤:
- 提取头部购物车组件,完成基础布局。
- 通过getters返回,有效商品总数,和有效商品列表,有效商品总金额。
- 渲染组件。
落的代码:
- 新建组件
src/components/app-header-cart.vue
<template>
<div class="cart">
<a class="curr" href="#"> <i class="iconfont icon-cart"></i><em>2</em> </a>
</div>
</template>
<script>
export default {
name: 'AppHeaderCart'
}
</script>
<style scoped lang="less">
.cart {
width: 50px;
.curr {
height: 32px;
line-height: 32px;
text-align: center;
position: relative;
display: block;
.icon-cart {
font-size: 22px;
}
em {
font-style: normal;
position: absolute;
right: 0;
top: 0;
padding: 1px 6px;
line-height: 1;
background: var(--help-color);
color: #fff;
font-size: 12px;
border-radius: 10px;
font-family: Arial;
}
}
}
</style>
- 使用组件,和删除迁移过的代码
src/components/app-header.vue
<div class="search">
<i class="iconfont icon-search"></i>
<input type="text" placeholder="搜一搜">
</div>
+ <AppHeaderCart />
</div>
</header>
</template>
<script>
import AppHeaderNav from './app-header-nav'
+import AppHeaderCart from './app-header-cart'
export default {
name: 'AppHeader',
+ components: { AppHeaderNav, AppHeaderCart }
}
</script>
- 基础布局和弹出效果
src/components/app-header-cart.vue
<template>
<div class="cart">
<a class="curr" href="javascript:;">
<i class="iconfont icon-cart"></i><em>2</em>
</a>
<div class="layer">
<div class="list">
<div class="item" v-for="i in 4" :key="i">
<RouterLink to="">
<img src="https://yanxuan-item.nosdn.127.net/ead73130f3dbdb3cabe1c7b0f4fd3d28.png" alt="">
<div class="center">
<p class="name ellipsis-2">和手足干裂说拜拜 ingrams手足皲裂修复霜</p>
<p class="attr ellipsis">颜色:修复绿瓶 容量:150ml</p>
</div>
<div class="right">
<p class="price">¥45.00</p>
<p class="count">x2</p>
</div>
</RouterLink>
<i class="iconfont icon-close-new"></i>
</div>
</div>
<div class="foot">
<div class="total">
<p>共 3 件商品</p>
<p>¥135.00</p>
</div>
<XtxButton type="plain">去购物车结算</XtxButton>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'AppHeaderCart'
}
</script>
<style scoped lang="less">
.cart {
width: 50px;
position: relative;
z-index: 600;
.curr {
height: 32px;
line-height: 32px;
text-align: center;
position: relative;
display: block;
.icon-cart {
font-size: 22px;
}
em {
font-style: normal;
position: absolute;
right: 0;
top: 0;
padding: 1px 6px;
line-height: 1;
background: var(--help-color);
color: #fff;
font-size: 12px;
border-radius: 10px;
font-family: Arial;
}
}
&:hover {
.layer {
opacity: 1;
transform: none
}
}
.layer {
opacity: 0;
transition: all .4s .2s;
transform: translateY(-200px) scale(1, 0);
width: 400px;
height: 400px;
position: absolute;
top: 50px;
right: 0;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
background: #fff;
border-radius: 4px;
padding-top: 10px;
&::before {
content: "";
position: absolute;
right: 14px;
top: -10px;
width: 20px;
height: 20px;
background: #fff;
transform: scale(0.6,1) rotate(45deg);
box-shadow: -3px -3px 5px rgba(0,0,0,0.1);
}
.foot {
position: absolute;
left: 0;
bottom: 0;
height: 70px;
width: 100%;
padding: 10px;
display: flex;
justify-content: space-between;
background: #f8f8f8;
align-items: center;
.total {
padding-left: 10px;
color: #999;
p {
&:last-child {
font-size: 18px;
color: var(--price-color);
}
}
}
}
}
.list {
height: 310px;
overflow: auto;
padding: 0 10px;
&::-webkit-scrollbar{
width:10px;
height:10px;
}
&::-webkit-scrollbar-track{
background: #f8f8f8;
border-radius: 2px;
}
&::-webkit-scrollbar-thumb{
background: #eee;
border-radius:10px;
}
&::-webkit-scrollbar-thumb:hover{
background: #ccc;
}
.item {
border-bottom: 1px solid #f5f5f5;
padding: 10px 0;
position: relative;
i {
position: absolute;
bottom: 38px;
right: 0;
opacity: 0;
color: #666;
transition: all .5s;
}
&:hover {
i {
opacity: 1;
cursor: pointer;
}
}
a {
display: flex;
align-items: center;
img {
height: 80px;
width: 80px;
}
.center {
padding: 0 10px;
width: 200px;
.name {
font-size: 16px;
}
.attr {
color: #999;
padding-top: 5px;
}
}
.right {
width: 100px;
padding-right: 20px;
text-align: center;
.price {
font-size: 16px;
color: var(--price-color);
}
.count {
color: #999;
margin-top: 5px;
font-size: 16px;
}
}
}
}
}
}
</style>
- 使用getters得到有效商品列表和期种件数
getters: {
// 有效商品列表
validList (state) {
return state.list.filter(item => item.stock > 0 && item.isEffective)
},
// 有效商品件数
validTotal (state, getters) {
return getters.validList.reduce((p, c) => p + c.count, 0)
},
// 有效商品总金额
validAmount (state, getters) {
return getters.validList.reduce((p, c) => p + c.nowPrice * 100 * c.count, 0) / 100
},
},
- 渲染头部购物车信息
<template>
<div class="cart">
<a class="curr" href="javascript:;">
<i class="iconfont icon-cart"></i><em>{{$store.getters['cart/validTotal']}}</em>
</a>
<div class="layer">
<div class="list">
<div class="item" v-for="item in $store.getters['cart/validList']" :key="item.skuId">
<RouterLink to="">
<img :src="item.picture" alt="">
<div class="center">
<p class="name ellipsis-2">{{item.name}}</p>
<p class="attr ellipsis">{{item.attrsText}}</p>
</div>
<div class="right">
<p class="price">¥{{item.nowPrice}}</p>
<p class="count">x{{item.count}}</p>
</div>
</RouterLink>
<i class="iconfont icon-close-new"></i>
</div>
</div>
<div class="foot">
<div class="total">
<p>共 {{$store.getters['cart/validTotal']}} 件商品</p>
<p>¥{{$store.getters['cart/validAmount']}}</p>
</div>
<XtxButton type="plain">去购物车结算</XtxButton>
</div>
</div>
</div>
</template>
04-头部购物车-商品列表
目的:根据本地存储的商品获取最新的库存价格和有效状态。
大致步骤:
- 定义获取最新信息的API
- 定义修改购物车商品信息的mutations
- 定义获取购物车列表信息的actions
- 在头部购物车组件初始化的时候更新列表信息
落的代码:
- 定义获取最新信息的API
src/api/cart.js
import request from '@/utils/request'
/**
* 获取新的商品信息
* @param {String} skuId - 商品SKUID
* @returns Promise
*/
export const getNewCartGoods = (skuId) => {
return request(`/goods/stock/${skuId}`, 'get')
}
- 定义修改购物车商品信息的mutations
src/store/module/cart.js
// 修改购物车商品
updateCart (state, goods) {
// goods中字段有可能不完整,goods有的信息才去修改。
// 1. goods中必需又skuId,才能找到对应的商品信息
const updateGoods = state.list.find(item => item.skuId === goods.skuId)
for (const key in goods) {
if (goods[key] !== null && goods[key] !== undefined && goods[key] !== '') {
updateGoods[key] = goods[key]
}
}
}
- 定义获取购物车列表信息的actions
src/store/module/cart.js
// 获取购物车列表
async findCartList (ctx) {
if (ctx.rootState.user.profile.token) {
// 登录 TODO
} else {
// 本地
// Promise.all() 可以并列发送多个请求,等所有请求成功
const promiseArr = ctx.state.list.map(item => {
// 返回接口函数的调用
return getNewCartGoods(item.skuId)
})
const results = await Promise.all(promiseArr);
results.forEach((item, i) => {
// list promiseArr results 三者顺一样
ctx.commit("updateCart", {
skuId: ctx.state.list[i].skuId,
...item.result,
});
});
}
},
- 再头部购物车组件初始化的时候更新列表信息
src/components/app-header-cart.vue
setup () {
const store = useStore()
store.dispatch('cart/findCartList')
}
05-头部购物车-删除操作
目的:完成头部购物车删除操作,支持未登录状态。
大致步骤:
- 编写mutaions删除购物车商品逻辑
- 编写actions进行删除操作
- 在头部购物车进行删除逻辑
落的代码:
- vuex的mutations和actions代码
src/store/module/cart.js
mutations: {
// ... 省略
// 删除购物车商品
deleteCart (state, skuId) {
const index = state.list.findIndex(item => item.skuId === skuId)
state.list.splice(index, 1)
}
},
actions: {
// ... 省略
// 删除购物车商品
async deleteCart (ctx, skuId) {
if (ctx.rootState.user.profile.token) {
// 登录 TODO
} else {
// 本地
ctx.commit('deleteCart', skuId)
resolve()
}
},
- 头部组件实现删除逻辑
src/components/app-header-cart.vue
+ 绑定点击事件传入skuId
+<i @click="deleteCart(item.skuId)" class="iconfont icon-close-new"></i>
import Message from "erabbit-ui/packages/components/Message";
import { useStore } from "vuex";
export default {
name: "AppHeaderCart",
setup() {
const store = useStore();
const deleteCart = (skuId) => {
store
.dispatch("cart/deleteCart", skuId)
.then(() => {
Message({ type: "success", text: "删除成功" });
})
.catch(() => {
Message({ type: "error", text: "删除失败" });
});
};
return { deleteCart };
},
};
- 购物车不弹出的条件
+ 购物车无商品不显示弹出层,并且不是在购物车页面
+<div class="layer" v-if="$store.getters['cart/validTotal']&&$route.path!=='/cart'">
06-购物车页面-基础布局
目的:完成购物车组件基础布局和路由配置与跳转链接。
大致步骤:
- 完成头部组件,购物车图标,购物车结算按钮,点击跳转购物车路由。商品点击跳转详情的操作。
- 配置购物车路由和组件,完成基础布局。
落的代码:
- 跳转功能
src/components/app-header-cart.vue
<RouterLink to="/cart" class="curr">
<i class="iconfont icon-cart"></i><em>{{$store.getters['cart/validTotal']}}</em>
</RouterLink>
<div class="item" v-for="item in $store.getters['cart/validList']" :key="item.skuId">
+ <RouterLink :to="`/product/${item.id}`">
<img :src="item.picture" alt="">
<XtxButton type="plain" @click="$router.push('/cart')">去购物车结算</XtxButton>
- 组件与路由
src/views/cart/index.vue
<template>
<div class="xtx-cart-page">
<div class="container">
<XtxBread>
<XtxBreadItem to="/">首页</XtxBreadItem>
<XtxBreadItem>购物车</XtxBreadItem>
</XtxBread>
<div class="cart">
<table>
<thead>
<tr>
<th width="120"><XtxCheckbox>全选</XtxCheckbox></th>
<th width="400">商品信息</th>
<th width="220">单价</th>
<th width="180">数量</th>
<th width="180">小计</th>
<th width="140">操作</th>
</tr>
</thead>
<!-- 有效商品 -->
<tbody>
<tr v-for="i in 3" :key="i">
<td><XtxCheckbox /></td>
<td>
<div class="goods">
<RouterLink to="/"><img src="https://yanxuan-item.nosdn.127.net/13ab302f8f2c954d873f03be36f8fb03.png" alt=""></RouterLink>
<div>
<p class="name ellipsis">和手足干裂说拜拜 ingrams手足皲裂修复霜</p>
<p class="attr">颜色:黑色</p>
</div>
</div>
</td>
<td class="tc">
<p>¥200.00</p>
<p>比加入时降价 <span class="red">¥20.00</span></p>
</td>
<td class="tc">
<XtxNumbox />
</td>
<td class="tc"><p class="f16 red">¥200.00</p></td>
<td class="tc">
<p><a href="javascript:;">移入收藏夹</a></p>
<p><a class="green" href="javascript:;">删除</a></p>
<p><a href="javascript:;">找相似</a></p>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 操作栏 -->
<div class="action">
<div class="batch">
<XtxCheckbox>全选</XtxCheckbox>
<a href="javascript:;">删除商品</a>
</div>
<div class="total">
共 7 件商品,已选择 2 件,商品合计:
<span class="red">¥400</span>
<XtxButton type="primary">下单结算</XtxButton>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'XtxCartPage',
}
</script>
<style scoped lang="less">
.tc {
text-align: center;
.xtx-numbox {
margin: 0 auto;
width: 120px;
}
}
.red {
color: var(--price-color);
}
.green {
color: var(--xtx-color);
}
.f16 {
font-size: 16px;
}
.goods {
display: flex;
align-items: center;
img {
width: 100px;
height: 100px;
}
> div {
width: 280px;
font-size: 16px;
padding-left: 10px;
.attr {
font-size: 14px;
color: #999;
}
}
}
.action {
display: flex;
background: #fff;
margin-top: 20px;
height: 80px;
align-items: center;
font-size: 16px;
justify-content: space-between;
padding: 0 30px;
.xtx-checkbox {
color: #999;
}
.batch {
a {
margin-left: 20px;
}
}
.red {
font-size: 18px;
margin-right: 20px;
font-weight: bold;
}
}
.tit {
color: #666;
font-size: 16px;
font-weight: normal;
line-height: 50px;
}
.xtx-cart-page {
.cart {
background: #fff;
color: #666;
table {
border-spacing: 0;
border-collapse: collapse;
line-height: 24px;
th,td{
padding: 10px;
border-bottom: 1px solid #f5f5f5;
&:first-child {
text-align: left;
padding-left: 30px;
color: #999;
}
}
th {
font-size: 16px;
font-weight: normal;
line-height: 50px;
}
}
}
}
</style>
router/index.js
const Cart = () => import("@/views/cart/index");
{ path: "/search", component: Search },
{ path: "/product/:id", component: Goods },
+ { path: "/cart", component: Cart },
07-购物车页面-v-model补充
目的:知道vue3中的v-model语法糖原理
大致步骤:
- 组件支持v-model
- 组件支持多个数据双向绑定 v-model:属性
具体代码:
- vue3组件支持v-model
- Vue2
v-model语法糖原理::value="数据"@input="数据=$event" - Vue3
v-model语法糖原理::modelValue="数据"@update:modelValue="数据=$event"
- Vue2
父组件
<CartTest v-model="count" />
components: { CartTest },
setup() {
const count = ref(0);
return { count };
},
子组件
<template>
<div>
cart-test组件 计数器:{{ modelValue }}
<button @click="updateCount">+1</button>
</div>
</template>
<script>
export default {
name: "CartTest",
props: {
modelValue: {
type: Number,
},
},
emits: ["update:modelValue"],
setup(props, { emit }) {
console.log(props.modelValue);
const updateCount = () => {
// 通知父组件,让父组件去修改
emit("update:modelValue", props.modelValue + 1);
};
return { updateCount };
},
};
</script>
- 组件支持多个数据双向绑定 v-model:属性
- vue2中支持多个数据的双向绑定,使用
v-model还可以使用.sync:abc="数据"父子传值@update:abc="数据=$event"===>:abc.sync="数据"
- Vue3 移除了
.sync全使用v-model:abc="数据"
- vue2中支持多个数据的双向绑定,使用
08-购物车页面-列表展示
目的:实现本地状态下的,购物车商品列表展示功能。
大致步骤:
- 准备有效商品列表数据。已选择商品列表数据。已选择商品件数数据。需要支付的金额数据。全选按钮的状态。
- 渲染模版
落的代码:
- 准备数据
src/store/module/cart.js
// ... getters 代码
// 选中商品列表
selectedList (state, getters) {
return getters.validList.filter(item => item.selected)
},
// 选中商品件数
selectedTotal (state, getters) {
return getters.selectedList.reduce((p, c) => p + c.count, 0)
},
// 选中商品总金额
selectedAmount (state, getters) {
return getters.selectedList.reduce((p, c) => p + (c.nowPrice * 100 * c.count), 0) / 100
},
// 是否全选
isCheckAll (state, getters) {
return getters.validList.length === getters.selectedList.length && getters.selectedList.length !== 0
}
- 渲染列表
<div class="cart">
<table>
<thead>
<tr>
+ <th width="120"><XtxCheckbox :modelValue="$store.getters['cart/isCheckAll']">全选</XtxCheckbox></th>
<th width="400">商品信息</th>
<th width="220">单价</th>
<th width="180">数量</th>
<th width="180">小计</th>
<th width="140">操作</th>
</tr>
</thead>
<!-- 有效商品 -->
<tbody>
+ <tr v-for="item in $store.getters['cart/validList']" :key="item.skuId">
+ <td><XtxCheckbox :modelValue="item.selected" /></td>
<td>
<div class="goods">
+ <RouterLink :to="`/product/${item.id}`">
+ <img :src="item.picture" alt="">
</RouterLink>
<div>
+ <p class="name ellipsis">{{item.name}}</p>
<!-- 选择规格组件 -->
+ <p class="attr">{{item.attrsText}}</p>
</div>
</div>
</td>
<td class="tc">
+ <p>¥{{item.nowPrice}}</p>
+ <p v-if="item.price-item.nowPrice>0">
比加入时降价
+ <span class="red">¥{{item.price-item.nowPrice}}</span>
</p>
</td>
<td class="tc">
+ <XtxNumbox :modelValue="item.count" />
</td>
+ <td class="tc"><p class="f16 red">¥{{item.nowPrice*100*item.count/100}}</p></td>
<td class="tc">
<p><a href="javascript:;">移入收藏夹</a></p>
<p><a class="green" href="javascript:;">删除</a></p>
<p><a href="javascript:;">找相似</a></p>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 操作栏 -->
<div class="action">
<div class="batch">
+ <XtxCheckbox :modelValue="$store.getters['cart/isCheckAll']">全选</XtxCheckbox>
<a href="javascript:;">删除商品</a>
</div>
<div class="total">
+ 共 {{$store.getters['cart/validTotal']}} 件商品,已选择 {{$store.getters['cart/selectedTotal']}} 件,商品合计:
+ <span class="red">¥{{$store.getters['cart/selectedAmount']}}</span>
<XtxButton type="primary">下单结算</XtxButton>
</div>
</div>
09-购物车页面-单选操作
目的:实现本地状态下的,选中商品操作。
大致步骤:
- 使用购物车商品修改信息的mutations(已实现)
- 定义购物车商品选中状态的actions
- 在购物车页面绑定单选的复选框change事件
- 在事件中调用actions的修改函数
落的代码:
- 定义修改购物车商品选中状态的mutations
src/store/module/cart.js
// 修改购物车商品
updateCart (state, goods) {
// goods中字段有可能不完整,goods有的信息才去修改。
// 1. goods中必需又skuId,才能找到对应的商品信息
const updateGoods = state.list.find(item => item.skuId === goods.skuId)
for (const key in goods) {
// 布尔类型 false 值需要使用
+ if (goods[key] !== null && goods[key] !== undefined && goods[key] !== '') {
updateGoods[key] = goods[key]
}
}
},
- 定义修改购物车商品的actions
src/store/module/cart.js
// 修改购物车商品
async updateCart (ctx, goods) {
// goods 中:必须有skuId,其他想修改的属性 selected count
if (ctx.rootState.user.profile.token) {
// 登录 TODO
} else {
// 本地
ctx.commit('updateCart', goods)
}
},
- 在购物车页面绑定单选的复选框change事件并处理选中
src/views/cart/index.vue
<td><XtxCheckbox @change="$event=>checkOne(item.skuId,$event)" :modelValue="item.selected" /></td>
import GoodRelevant from '@/views/goods/components/goods-relevant'
import { useStore } from 'vuex'
export default {
name: 'XtxCartPage',
components: { GoodRelevant },
setup () {
const store = useStore()
// 单选
const checkOne = (skuId, selected) => {
store.dispatch('cart/updateCart', { skuId, selected })
}
return { checkOne }
}
}
10-购物车页面-全选操作
目的:实现本地状态下的,全选商品操作。
大致步骤:
- 修改购物车所有有效商品选中状态的actions
- 在购物车页面修改调用actions的代码
- 在购物车页面绑定全选的复选框change事件
- 在事件中调用actions的修改函数
落的代码
- 修改购物车商品选中状态的actions让其支持全选
src/store/module/cart.js
// 做有效商品的全选&反选
async checkAllCart (ctx, selected) {
if (ctx.rootState.user.profile.token) {
// 登录 TODO
} else {
// 本地
// 1. 获取有效的商品列表,遍历的去调用修改mutations即可
ctx.getters.validList.forEach(item => {
ctx.commit('updateCart', { skuId: item.skuId, selected })
})
}
},
- 在购物车页面修改调用actions的代码
src/views/cart/index.vue
// 全选
const checkAll = (selected) => {
store.dispatch('cart/checkAllCart', selected)
}
return { checkOne, checkAll }
- 在购物车页面绑定全选的复选框change事件并处理选中
src/views/cart/index.vue
<!-- 两处都需要加 -->
<XtxCheckbox @change="checkAll" :modelValue="$store.getters['cart/isCheckAll']">全选</XtxCheckbox>
11-购物车页面-删除操作
目的:实现本地状态下,购物车商品删除
大致步骤:
- 绑定
删除点击事件指定处理函数,调用删除actions - 处理无商品展示界面
落的代码:
- 绑定
删除点击事件指定处理函数,调用删除actionssrc/views/cart/index.vue
<!-- 两处删除都绑定 -->
<p><a @click="deleteCart(item.skuId)" class="green" href="javascript:;">删除</a></p>
// 删除
const deleteCart = (skuId) => {
store.dispatch('cart/deleteCart', skuId)
}
return { checkOne, checkAll, deleteCart }
- 处理无商品展示界面
组件 src/views/cart/components/cart-none.vue
<template>
<div class="cart-none">
<img src="@/assets/none.png" alt="" />
<p>购物车内暂时没有商品</p>
<div class="btn">
<XtxButton type="primary" @click="$router.push('/')">继续逛逛</XtxButton>
</div>
</div>
</template>
<script>
export default {
name: 'CartNone'
}
</script>
<style scoped lang="less">
.cart-none {
text-align: center;
padding: 150px 0;
background: #fff;
img {
width: 180px;
}
p {
color: #999999;
padding: 20px 0;
}
}
</style>
使用 src/views/cart/index.vue
+import XtxConfirm from 'erabbit/packages/components/XtxConfirm'
import { useStore } from 'vuex'
export default {
name: 'XtxCartPage',
+ components: { GoodRelevant, CartNone },
<div class="cart" v-if="$store.getters['cart/validList'].length">
</div>
<!-- 操作栏 -->
<div class="action" v-if="$store.getters['cart/validList'].length">
</div>
<CartNone v-else />
12-购物车页面-批量删除(作业)
目的:实现本地批量删除选中商品功能。
大致的步骤:
- 定义一个批量删除商品的actions支持批量操作
- 遍历选中商品,调用单个删除调用mutations函数即可
- 绑定批量删除点击事件指定处理函数,调用actions进行删除。
落地代码:
- 批量操作商品的actions支持
src/store/module/cart.js
// 批量删除选中商品
async batchDeleteCart (ctx) {
if (ctx.rootState.user.profile.token) {
// 登录 TODO
} else {
// 本地
// 1. 获取选中商品列表,进行遍历调用deleteCart mutataions函数
ctx.getters.selectedList.forEach(item => {
ctx.commit('deleteCart', item.skuId)
})
}
},
- 绑定批量删除点击事件指定处理函数,调用actions进行删除。
src/views/cart/index.vue
<a @click="batchDeleteCart()" href="javascript:;">删除商品</a>
// 批量删除
const batchDeleteCart = () => {
Confirm({ text: '您确定从购物车删除选中的商品吗?' }).then(() => {
store.dispatch('cart/batchDeleteCart')
}).catch(e => {})
}
return { checkOne, checkAll, deleteCart, batchDeleteCart }
13-购物车页面-修改数量(作业)
目的:实现本地版本的修改商品数量。
大致的步骤:
- 绑定xtx-numbox组件的change事件指定处理函数
- 在函数种调用vuex的cart/updateCart函数修改数量
落的代码:
- 绑定xtx-numbox组件的change事件指定处理函数
<XtxNumbox :min="1" :max="item.stock" @change="$event=>changeCount(item.skuId,$event)" :modelValue="item.count" />
- 在函数种调用vuex的cart/updateCart函数修改数量
// 修改数量
const changeCount = (skuId, count) => {
store.dispatch('cart/updateCart', { skuId, count })
}
return { checkOne, checkAll, deleteCart, batchDeleteCart, changeCount }