综合案例:购物车
数据渲染
构建cart购物车模块
准备后端接口服务环境
请求数据存入vuex
cart.js
// 新建购物车模块
import axios from 'axios'
export default {namespaced: true,state () {return {// 购物车数据 [{},{}]list: []}},mutations: {updateList (state, newList) {state.list = newList}},actions: {// 请求方式:get// 请求地址:http://localhost:3000/cartasync getList (context) {const res = await axios.get('http://localhost:3000/cart')context.commit('updateList', res.data)}},getters: {}
}
App.vue
<template><div class="app-container"><!-- Header 区域 --><cart-header></cart-header><!-- 商品 Item 项组件 --><cart-item v-for="item in list" :key="item.id" :item="item"></cart-item><!-- Foote 区域 --><cart-footer></cart-footer></div>
</template><script>
import CartHeader from '@/components/cart-header.vue'
import CartFooter from '@/components/cart-footer.vue'
import CartItem from '@/components/cart-item.vue'import { mapState } from 'vuex'
export default {name: 'App',created () {this.$store.dispatch('cart/getList')},computed: {...mapState('cart', ['list'])},components: {CartHeader,CartFooter,CartItem}
}
</script><style lang="less" scoped>
.app-container {padding: 50px 0;font-size: 14px;
}
</style>
cart-item.vue
<template><div class="goods-container"><!-- 左侧图片区域 --><div class="left"><img :src="item.thumb" class="avatar" alt=""></div><!-- 右侧商品区域 --><div class="right"><!-- 标题 --><div class="title">{{ item.name }}</div><div class="info"><!-- 单价 --><span class="price">{{ item.price }}</span><div class="btns"><!-- 按钮区域 --><button class="btn btn-light">-</button><span class="count">{{ item.count }}</span><button class="btn btn-light">+</button></div></div></div></div>
</template>
<script>
export default {name: 'CartItem',methods: {},props: {item: {type: Object,require: true}}
}
</script><style lang="less" scoped>
.goods-container {display: flex;padding: 10px;+ .goods-container {border-top: 1px solid #f8f8f8;}.left {.avatar {width: 100px;height: 100px;}margin-right: 10px;}.right {display: flex;flex-direction: column;justify-content: space-between;flex: 1;.title {font-weight: bold;}.info {display: flex;justify-content: space-between;align-items: center;.price {color: red;font-weight: bold;}.btns {.count {display: inline-block;width: 30px;text-align: center;}}}}
}.custom-control-label::before,
.custom-control-label::after {top: 3.6rem;
}
</style>
修改数量功能的实现
cart.js
// 新建购物车模块
import axios from 'axios'
export default {namespaced: true,state () {return {// 购物车数据 [{},{}]list: []}},mutations: {updateList (state, newList) {state.list = newList},// obj: {id:xxx, newCount:xxx}updateCount (state, obj) {// 根据 id 找到对应的对象,更新count属性即可const goods = state.list.find(item => item.id === obj.id)goods.count = obj.newCount}},actions: {// 请求方式:get// 请求地址:http://localhost:3000/cartasync getList (context) {const res = await axios.get('http://localhost:3000/cart')context.commit('updateList', res.data)},// 请求方式: patch// 请求地址: http://localhost:3000/cart/:id值 表示修改的是哪个对象// 请求参数:// {// name:'新值',【可选】// price:'新值',【可选】// count:'新值',【可选】// thumb:'新值',【可选】// }async updateCountAsync (context, obj) {// 将修改更新同步到后台服务器await axios.patch(`http://localhost:3000/cart/${obj.id}`, {count: obj.newCount})// 将修改更新同步到 vuexcontext.commit('updateCount', {id: obj.id,newCount: obj.newCount})}},getters: {}
}
cart-item.vue
<template><div class="goods-container"><!-- 左侧图片区域 --><div class="left"><img :src="item.thumb" class="avatar" alt=""></div><!-- 右侧商品区域 --><div class="right"><!-- 标题 --><div class="title">{{ item.name }}</div><div class="info"><!-- 单价 --><span class="price">{{ item.price }}</span><div class="btns"><!-- 按钮区域 --><button class="btn btn-light" @click="btnClick(-1)">-</button><span class="count">{{ item.count }}</span><button class="btn btn-light" @click="btnClick(1)">+</button></div></div></div></div>
</template>
<script>
export default {name: 'CartItem',methods: {btnClick (step) {const newCount = this.item.count + stepconst id = this.item.idif (newCount < 1) returnthis.$store.dispatch('cart/updateCountAsync', {id,newCount})}},props: {item: {type: Object,require: true}}
}
</script><style lang="less" scoped>
.goods-container {display: flex;padding: 10px;+ .goods-container {border-top: 1px solid #f8f8f8;}.left {.avatar {width: 100px;height: 100px;}margin-right: 10px;}.right {display: flex;flex-direction: column;justify-content: space-between;flex: 1;.title {font-weight: bold;}.info {display: flex;justify-content: space-between;align-items: center;.price {color: red;font-weight: bold;}.btns {.count {display: inline-block;width: 30px;text-align: center;}}}}
}.custom-control-label::before,
.custom-control-label::after {top: 3.6rem;
}
</style>
底部 getters 统计
cart.js
// 新建购物车模块
import axios from 'axios'
export default {namespaced: true,state () {return {// 购物车数据 [{},{}]list: []}},mutations: {updateList (state, newList) {state.list = newList},// obj: {id:xxx, newCount:xxx}updateCount (state, obj) {// 根据 id 找到对应的对象,更新count属性即可const goods = state.list.find(item => item.id === obj.id)goods.count = obj.newCount}},actions: {// 请求方式:get// 请求地址:http://localhost:3000/cartasync getList (context) {const res = await axios.get('http://localhost:3000/cart')context.commit('updateList', res.data)},// 请求方式: patch// 请求地址: http://localhost:3000/cart/:id值 表示修改的是哪个对象// 请求参数:// {// name:'新值',【可选】// price:'新值',【可选】// count:'新值',【可选】// thumb:'新值',【可选】// }async updateCountAsync (context, obj) {// 将修改更新同步到后台服务器await axios.patch(`http://localhost:3000/cart/${obj.id}`, {count: obj.newCount})// 将修改更新同步到 vuexcontext.commit('updateCount', {id: obj.id,newCount: obj.newCount})}},getters: {// 商品总数 sum + item.counttotal (state) {return state.list.reduce((sum, item) => sum + item.count, 0)},// 商品总结个 sum + item.count * item.pricetotalPrice (state) {return state.list.reduce((sum, item) => sum + item.count * item.price, 0)}}
}
cart-footer.vue
<template><div class="footer-container"><!-- 中间的合计 --><div><span>共 {{ total }} 件商品,合计:</span><span class="price">¥{{ totalPrice }}</span></div><!-- 右侧结算按钮 --><button class="btn btn-success btn-settle">结算</button></div>
</template><script>
import { mapGetters } from 'vuex'
export default {name: 'CartFooter',computed: {...mapGetters('cart', ['total', 'totalPrice'])}
}</script><style lang="less" scoped>
.footer-container {background-color: white;height: 50px;border-top: 1px solid #f8f8f8;display: flex;justify-content: flex-end;align-items: center;padding: 0 10px;position: fixed;bottom: 0;left: 0;width: 100%;z-index: 999;
}.price {color: red;font-size: 13px;font-weight: bold;margin-right: 10px;
}.btn-settle {height: 30px;min-width: 80px;margin-right: 20px;border-radius: 20px;background: #42b983;border: none;color: white;
}
</style>