购物车原型图

从功能上拆分层次
尽量让组件原子化
容器组件(只管理数据)vuex
组件拆分
该功能拆分成两个组件,顶部是商品列表,底部是购物车商品
功能上
1.点击加入购物车,底部购物车新增商品(或者是已有商品,数量加1即可)
2.点击增加按钮,数量加一,点击减少,数量减1
数据结构上
1.有两种数据,一种是商品列表数据,有商品id,商品名称,商品价格。另一种是购物车数据,有商品id,商品名称,数量
逻辑分析
1.点击添加到购物车商品,将商品id传递过去,然后在数据组件(总组件中)对数据处理,通过id来给cartlist数组添加一条新数据(没有相同id,新增,有相同id,数量加1)
2.cartlist和productionlist传递给cart组件,通过id来计算一个新数据list(因为cartlist没有商品名称),总价也可以计算出来
3.点击添加按钮,也通过id来判断数组中的数量加1, 点击减少,来判断数组中数量减1
总结;通过商品id来处理数组中的数据,利用数组中的各种方法,用到了事件总线,子向父传递数据
总组件index.vue
<template>
<div>
<ProductionList :list="productionList"/>
<hr>
<CartList
:productionList="productionList"
:cartList="cartList"
/>
</div>
</template>
<script>
import ProductionList from './ProductionList/index'
import CartList from './CartList/index'
import event from './event'
export default {
components: {
ProductionList,
CartList
},
data() {
return {
productionList: [
{
id: 1,
title: '商品A',
price: 10
},
{
id: 2,
title: '商品B',
price: 15
},
{
id: 3,
title: '商品C',
price: 20
}
],
cartList: [
{
id: 1,
quantity: 1 // 购物数量
}
]
}
},
methods: {
// 加入购物车
addToCart(id) {
// 先看购物车中是否有该商品
const prd = this.cartList.find(item => item.id === id)
if (prd) {
// 数量加一
prd.quantity++
return
}
// 购物车没有该商品
this.cartList.push({
id,
quantity: 1 // 默认购物数量 1
})
},
// 从购物车删除一个(即购物数量减一)
delFromCart(id) {
// 从购物车中找出该商品
const prd = this.cartList.find(item => item.id === id)
if (prd == null) {
return
}
// 数量减一
prd.quantity--
// 如果数量减少到了 0
if (prd.quantity <= 0) {
this.cartList = this.cartList.filter(
item => item.id !== id
)
}
}
},
mounted() {
this.$on('addToCart', this.addToCart)
this.$on('delFromCart', this.delFromCart)
}
}
</script>
顶部商品列表组件production
<template>
<div>
<ProductionItem
v-for="item in list"
:key="item.id"
:item="item"
/>
</div>
</template>
<script>
import ProductionItem from './ProductionItem'
export default {
components: {
ProductionItem,
},
props: {
list: {
type: Array,
default() {
return [
// {
// id: 1,
// title: '商品A',
// price: 10
// }
]
}
}
}
}
</script>
<template>
<div>
<span>{{item.title}}</span>
<span>{{item.price}}元</span>
<a href="#" @click="clickHandler(item.id, $event)">加入购物车</a>
</div>
</template>
<script>
import event from '../event'
export default {
props: {
item: {
type: Object,
default() {
return {
// id: 1,
// title: '商品A',
// price: 10
}
}
}
},
methods: {
clickHandler(id, e) {
e.preventDefault()
this.$emit('addToCart', id)
}
},
}
</script>
底部购物车组件
<template>
<div>
<CartItem
v-for="item in list"
:key="item.id"
:item="item"
/>
<p>总价 {{totalPrice}}</p>
</div>
</template>
<script>
import CartItem from './CartItem'
export default {
components: {
CartItem,
},
props: {
productionList: {
type: Array,
default() {
return [
// {
// id: 1,
// title: '商品A',
// price: 10
// }
]
}
},
cartList: {
type: Array,
default() {
return [
// {
// id: 1,
// quantity: 1
// }
]
}
}
},
computed: {
// 购物车商品列表
list() {
return this.cartList.map(cartListItem => {
// 找到对应的 productionItem
const productionItem = this.productionList.find(
prdItem => prdItem.id === cartListItem.id
)
// 返回商品信息,外加购物数量
return {
...productionItem,
quantity: cartListItem.quantity
}
// 如:
// {
// id: 1,
// title: '商品A',
// price: 10,
// quantity: 1 // 购物数量
// }
})
},
// 总价
totalPrice() {
return this.list.reduce(
(total, curItem) => total + (curItem.quantity * curItem.price),
0
)
}
}
}
</script>
<template>
<div>
<span>{{item.title}}</span>
<span>(数量 {{item.quantity}})</span>
<a href="#" @click="addClickHandler(item.id, $event)">增加</a>
<a href="#" @click="delClickHandler(item.id, $event)">减少</a>
</div>
</template>
<script>
import event from '../event'
export default {
props: {
item: {
type: Object,
default() {
return {
// id: 1,
// title: '商品A',
// price: 10,
// quantity: 1 // 购物数量
}
}
}
},
methods: {
addClickHandler(id, e) {
e.preventDefault()
this.$emit('addToCart', id)
},
delClickHandler(id, e) {
e.preventDefault()
this.$emit('delFromCart', id)
}
}
}
</script>
vuex版本
vuex模块拆分,模块拆分,如何获取数据;参考;https://www.cnblogs.com/fsg6/p/14416502.html

vuex数据
总store
import Vue from 'vue' import Vuex from 'vuex' import cart from './modules/cart' import products from './modules/products' import createLogger from '../../../src/plugins/logger' Vue.use(Vuex) const debug = process.env.NODE_ENV !== 'production' export default new Vuex.Store({ modules: { cart, products }, strict: debug, plugins: debug ? [createLogger()] : [] })
productons.js
import shop from '../../api/shop' // initial state const state = { all: [] } // getters const getters = {} // actions —— 异步操作要放在 actions const actions = { // 加载所有商品 getAllProducts ({ commit }) { // 从 shop API 加载所有商品,模拟异步 shop.getProducts(products => { commit('setProducts', products) }) } } // mutations const mutations = { // 设置所有商品 setProducts (state, products) { state.all = products }, // 减少某一个商品的库存(够买一个,库存就相应的减少一个,合理) decrementProductInventory (state, { id }) { const product = state.all.find(product => product.id === id) product.inventory-- } } export default { namespaced: true, state, getters, actions, mutations }
cart.js
import shop from '../../api/shop' // initial state // shape: [{ id, quantity }] const state = { // 已加入购物车的商品,格式如 [{ id, quantity }, { id, quantity }] // 注意,购物车只存储 id 和数量,其他商品信息不存储 items: [], // 结账的状态 - null successful failed checkoutStatus: null } // getters const getters = { // 获取购物车商品 cartProducts: (state, getters, rootState) => { // rootState - 全局 state // 购物车 items 只有 id quantity ,没有其他商品信息。要从这里获取。 return state.items.map(({ id, quantity }) => { // 从商品列表中,根据 id 获取商品信息 const product = rootState.products.all.find(product => product.id === id) return { title: product.title, price: product.price, quantity } }) }, // 所有购物车商品的价格总和 cartTotalPrice: (state, getters) => { // reduce 的经典使用场景,求和 return getters.cartProducts.reduce((total, product) => { return total + product.price * product.quantity }, 0) } } // actions —— 异步操作要放在 actions const actions = { // 结算 checkout ({ commit, state }, products) { // 获取购物车的商品 const savedCartItems = [...state.items] // 设置结账的状态 null commit('setCheckoutStatus', null) // empty cart 清空购物车 commit('setCartItems', { items: [] }) // 请求接口 shop.buyProducts( products, () => commit('setCheckoutStatus', 'successful'), // 设置结账的状态 successful () => { commit('setCheckoutStatus', 'failed') // 设置结账的状态 failed // rollback to the cart saved before sending the request // 失败了,就要重新还原购物车的数据 commit('setCartItems', { items: savedCartItems }) } ) }, // 添加到购物车 // 【注意】这里没有异步,为何要用 actions ???—— 因为要整合多个 mutation // mutation 是原子,其中不可再进行 commit !!! addProductToCart ({ state, commit }, product) { commit('setCheckoutStatus', null) // 设置结账的状态 null // 判断库存是否足够 if (product.inventory > 0) { const cartItem = state.items.find(item => item.id === product.id) if (!cartItem) { // 初次添加到购物车 commit('pushProductToCart', { id: product.id }) } else { // 再次添加购物车,增加数量即可 commit('incrementItemQuantity', cartItem) } // remove 1 item from stock 减少库存 commit('products/decrementProductInventory', { id: product.id }, { root: true }) } } } // mutations const mutations = { // 商品初次添加到购物车 pushProductToCart (state, { id }) { state.items.push({ id, quantity: 1 }) }, // 商品再次被添加到购物车,增加商品数量 incrementItemQuantity (state, { id }) { const cartItem = state.items.find(item => item.id === id) cartItem.quantity++ }, // 设置购物车数据 setCartItems (state, { items }) { state.items = items }, // 设置结算状态 setCheckoutStatus (state, status) { state.checkoutStatus = status } } export default { namespaced: true, state, getters, actions, mutations }
总组件
<template> <div id="app"> <h1>Shopping Cart Example</h1> <hr> <h2>Products</h2> <ProductList/> <hr> <ShoppingCart/> </div> </template> <script> import ProductList from './ProductList.vue' import ShoppingCart from './ShoppingCart.vue' export default { components: { ProductList, ShoppingCart } } </script>
production组件
<template>
<ul>
<li
v-for="product in products"
:key="product.id">
{{ product.title }} - {{ product.price | currency }}
(inventory: {{product.inventory}})<!-- 这里可以自己加一下显示库存 -->
<br>
<button
:disabled="!product.inventory"
@click="addProductToCart(product)">
Add to cart
</button>
</li>
</ul>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: mapState({
// 获取所有商品
products: state => state.products.all
}),
methods: mapActions('cart', [
// 添加商品到购物车
'addProductToCart'
]),
created () {
// 加载所有商品,dispatch到模块的函数
this.$store.dispatch('products/getAllProducts')
}
}
</script>
cart组件
<template> <div class="cart"> <h2>Your Cart</h2> <p v-show="!products.length"><i>Please add some products to cart.</i></p> <ul> <li v-for="product in products" :key="product.id"> {{ product.title }} - {{ product.price | currency }} x {{ product.quantity }} </li> </ul> <p>Total: {{ total | currency }}</p> <p><button :disabled="!products.length" @click="checkout(products)">Checkout</button></p> <p v-show="checkoutStatus">Checkout {{ checkoutStatus }}.</p> </div> </template> <script> import { mapGetters, mapState } from 'vuex' export default { computed: { ...mapState({ // 结账的状态 checkoutStatus: state => state.cart.checkoutStatus }),
//获取到模块store中数据 ...mapGetters('cart', { products: 'cartProducts', // 购物车的商品 total: 'cartTotalPrice' // 购物车商品的总价格 }) }, methods: { // 结账 checkout (products) { this.$store.dispatch('cart/checkout', products) } } } </script>