文章目录
- 04-头部购物车-商品列表-本地
- 05-头部购物车-删除操作-本地
- 06-购物车页面-基础布局
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
// 获取购物车列表
findCartList (ctx) {
return new Promise((resolve, reject) => {
if (ctx.rootState.user.profile.token) {
// 登录 TODO
} else {
// 本地
// Promise.all() 可以并列发送多个请求,等所有请求成功,调用then
// Promise.race() 可以并列发送多个请求,等最快的请求成功,调用then
// 传参事promise数组
const promiseArr = ctx.state.list.map(item => {
// 返回接口函数的调用
return getNewCartGoods(item.skuId)
})
Promise.all(promiseArr).then(dataArr => {
dataArr.forEach((data, i) => {
ctx.commit('updateCart', { skuId: ctx.state.list[i].skuId, ...data.result })
})
resolve()
}).catch(e => {
reject(e)
})
}
})
},
- 再头部购物车组件初始化的时候更新列表信息
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: {
// ... 省略
// 删除购物车商品
deleteCart (ctx, skuId) {
return new Promise((resolve, reject) => {
if (ctx.rootState.user.profile.token) {
// 登录 TODO
} else {
// 本地
ctx.commit('deleteCart', skuId)
resolve()
}
})
},
- 头部组件实现删除逻辑
src/components/app-header-cart.vue
+ 购物车无商品不显示弹出层,并且不是在购物车页面
+<div class="layer" v-if="$store.getters['cart/validTotal']&&$route.path!=='/cart'">
+ 绑定点击事件传入skuId
+<i @click="deleteCart(item.skuId)" class="iconfont icon-close-new"></i>
setup () {
// 删除
const deleteCart = (skuId) => {
store.dispatch('cart/deleteCart', skuId).then(() => {
Message({ type: 'success', text: '删除成功' })
}).catch(e => {
Message({ type: 'error', text: '删除失败' })
})
}
return { deleteCart }
}
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>
<!-- 选择规格组件 -->
</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>
<!-- 无效商品 -->
<tbody>
<tr><td colspan="6"><h3 class="tit">失效商品</h3></td></tr>
<tr v-for="i in 3" :key="i">
<td><XtxCheckbox style="color:#eee;" /></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">颜色:粉色 尺寸:14cm 产地:中国</p>
</div>
</div>
</td>
<td class="tc"><p>¥200.00</p></td>
<td class="tc">1</td>
<td class="tc"><p>¥200.00</p></td>
<td class="tc">
<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>
<a href="javascript:;">移入收藏夹</a>
<a href="javascript:;">清空失效商品</a>
</div>
<div class="total">
共 7 件商品,已选择 2 件,商品合计:
<span class="red">¥400</span>
<XtxButton type="primary">下单结算</XtxButton>
</div>
</div>
<!-- 猜你喜欢 -->
<GoodRelevant />
</div>
</div>
</template>
<script>
import GoodRelevant from '@/views/goods/components/goods-relevant'
export default {
name: 'XtxCartPage',
components: { GoodRelevant }
}
</script>
<style scoped lang="less">
.tc {
text-align: center;
.xtx-numbox {
margin: 0 auto;
width: 120px;
}
}
.red {
color: @priceColor;
}
.green {
color: @xtxColor
}
.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>