api层
import request from '@/utils/request'
export const addCart = (goodsId, goodsNum, goodsSkuId) => {return request.post('/cart/add', {goodsId,goodsNum,goodsSkuId})
}
export const getCartList = () => {return request.get('/cart/list')
}
export const changeCount = (goodsId, goodsNum, goodsSkuId) => {return request.post('/cart/update', {goodsId,goodsNum,goodsSkuId})
}
export const delSelect = (cartIds) => {return request.post('/cart/clear', {cartIds})
}
store层
import { getCartList, changeCount, delSelect } from '@/api/cart'
import { Toast } from 'vant'export default {namespaced: true,state() {return {cartList: []}},mutations: {setCartList(state, newList) {state.cartList = newList},toggleCheck(state, goodsId) {const goods = state.cartList.find((item) => item.goods_id === goodsId)goods.isChecked = !goods.isChecked},toggleAllCheck(state, flag) {state.cartList.forEach((item) => {item.isChecked = flag})},changeCount(state, { goodsId, goodsNum }) {const goods = state.cartList.find((item) => item.goods_id === goodsId)goods.goods_num = goodsNum}},actions: {async getCartAction({ commit }) {const { data } = await getCartList()data.list.forEach((item) => {item.isChecked = true})commit('setCartList', data.list)},async changeCountAction({ commit }, { goodsId, goodsNum, goodsSkuId }) {commit('changeCount', { goodsId, goodsNum })await changeCount(goodsId, goodsNum, goodsSkuId)},async delSelect({ commit, getters, dispatch }) {const selCartList = getters.selCartListconst cartIds = selCartList.map((item) => item.id)await delSelect(cartIds)Toast('删除成功')dispatch('getCartAction')}},getters: {cartTotal(state) {return state.cartList.reduce((sum, item, index) => sum + item.goods_num, 0)},selCartList(state) {return state.cartList.filter((item) => item.isChecked)},selCount(state, getters) {return getters.selCartList.reduce((sum, item, index) => sum + item.goods_num, 0)},selPrice(state, getters) {return getters.selCartList.reduce((pre, item) => {return pre + item.goods.goods_price_min * item.goods_num}, 0).toFixed(2)},isAllChecked(state) {return state.cartList.every((item) => item.isChecked)}}
}
视图层
<template><div class="cart"><van-nav-bar title="购物车" fixed /><div class="cart-box" v-if="isLogin && cartList.length > 0"><!-- 购物车开头 --><div class="cart-title"><span class="all">共<i>{{ cartTotal || 0 }}</i>件商品</span><span class="edit"><van-icon name="edit" @click="isEdit = !isEdit" />编辑</span></div><!-- 购物车列表 --><div class="cart-list"><div class="cart-item" v-for="item in cartList" :key="item.goods_id"><van-checkbox@click="toggleCheck(item.goods_id)"icon-size="18":value="item.isChecked"></van-checkbox><div class="show" @click="$router.push(`/prodetail/${item.goods_id}`)"><img :src="item.goods.goods_image" alt="" /></div><div class="info"><span class="tit text-ellipsis-2">{{ item.goods.goods_name }}</span><span class="bottom"><div class="price">¥ <span>{{ item.goods.goods_price_min }}</span></div><CountBox@input="(value) => changeCount(value, item.goods_id, item.goods_sku_id)"v-model="item.goods_num"/></span></div></div></div><div class="footer-fixed"><div class="all-check" @click="toggleAllCheck"><van-checkbox icon-size="18" :value="isAllChecked"></van-checkbox>全选</div><div class="all-total"><div class="price"><span>合计:</span><span>¥ <i class="totalPrice">{{ selPrice }}</i></span></div><div v-if="!isEdit" class="goPay" :class="{ disabled: selCount === 0 }" @click="goToPay">结算({{ selCount }})</div><div v-else @click="handleDel" class="delete" :class="{ disabled: selCount === 0 }">删除</div></div></div></div><div class="empty-cart" v-else><img src="@/assets/empty.png" alt="" /><div class="tips">您的购物车是空的, 快去逛逛吧</div><div class="btn" @click="$router.push('/')">去逛逛</div></div></div>
</template><script>
import store from '@/store'
import CountBox from '@/components/CountBox.vue'
import { mapState, mapGetters } from 'vuex'export default {name: 'CartPage',components: {CountBox},computed: {...mapState('cart', ['cartList', 'selPrice']),...mapGetters('cart', ['cartTotal', 'selCount', 'selPrice', 'isAllChecked', 'selCartList']),// 是否登录isLogin() {return store.getters.token}},data() {return {isEdit: false}},created() {// 必须是登录过的用户,才能用户购物车列表if (this.isLogin) {this.$store.dispatch('cart/getCartAction')}},methods: {// 切换选中toggleCheck(goodsId) {this.$store.commit('cart/toggleCheck', goodsId)},// 全选切换toggleAllCheck() {this.$store.commit('cart/toggleAllCheck', !this.isAllChecked)},async changeCount(goodsNum, goodsId, goodsSkuId) {this.$store.dispatch('cart/changeCountAction', {goodsId,goodsSkuId,goodsNum})},// 删除async handleDel() {if (this.selCount === 0) returnawait this.$store.dispatch('cart/delSelect', this.selCount)this.isEdit = false},// 去结算goToPay() {if (this.selCount > 0) {this.$router.push({path: '/pay',query: {mode: 'cart',cartIds: this.selCartList.map((item) => item.id).join(',')}})}}},watch: {isEdit(value) {if (value) {this.$store.commit('cart/toggleAllCheck', false)} else {this.$store.commit('cart/toggleAllCheck', true)}}}
}
</script><style lang="less" scoped>
// 主题 padding
.cart {padding-top: 46px;padding-bottom: 100px;background-color: #f5f5f5;min-height: 100vh;.cart-title {height: 40px;display: flex;justify-content: space-between;align-items: center;padding: 0 10px;font-size: 14px;.all {i {font-style: normal;margin: 0 2px;color: #fa2209;font-size: 16px;}}.edit {.van-icon {font-size: 18px;}}}.cart-item {margin: 0 10px 10px 10px;padding: 10px;display: flex;justify-content: space-between;background-color: #ffffff;border-radius: 5px;.show img {width: 100px;height: 100px;}.info {width: 210px;padding: 10px 5px;font-size: 14px;display: flex;flex-direction: column;justify-content: space-between;.bottom {display: flex;justify-content: space-between;.price {display: flex;align-items: flex-end;color: #fa2209;font-size: 12px;span {font-size: 16px;}}.count-box {display: flex;width: 110px;.add,.minus {width: 30px;height: 30px;outline: none;border: none;}.inp {width: 40px;height: 30px;outline: none;border: none;background-color: #efefef;text-align: center;margin: 0 5px;}}}}}// 购物车为空.empty-cart {display: flex;justify-content: center;flex-direction: column;align-items: center;width: 100%;height: calc(100vh - 100px);img {width: 140px;height: 92px;display: block;margin: 0 auto;}.tips {text-align: center;color: #666;margin: 30px;}.btn {width: 110px;height: 32px;line-height: 32px;text-align: center;background-color: #fa2c20;border-radius: 16px;color: #fff;display: block;margin: 0 auto;}}
}.footer-fixed {position: fixed;left: 0;bottom: 50px;height: 50px;width: 100%;border-bottom: 1px solid #ccc;background-color: #fff;display: flex;justify-content: space-between;align-items: center;padding: 0 10px;.all-check {display: flex;align-items: center;.van-checkbox {margin-right: 5px;}}.all-total {display: flex;line-height: 36px;.price {font-size: 14px;margin-right: 10px;.totalPrice {color: #fa2209;font-size: 18px;font-style: normal;}}.goPay,.delete {min-width: 100px;height: 36px;line-height: 36px;text-align: center;background-color: #fa2f21;color: #fff;border-radius: 18px;&.disabled {background-color: #ff9779;}}}
}
</style>