購物車原型圖

從功能上拆分層次
盡量讓組件原子化
容器組件(只管理數據)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>
