vue购物车组件设计结构

 

购物车原型图

 

 

从功能上拆分层次
尽量让组件原子化
容器组件(只管理数据)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>
        &nbsp;
        <span>{{item.price}}元</span>
        &nbsp;
        <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>
        &nbsp;
        <span>(数量 {{item.quantity}})</span>
        &nbsp;
        <a href="#" @click="addClickHandler(item.id, $event)">增加</a>
        &nbsp;
        <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>

 

posted @ 2021-03-08 09:42  全情海洋  阅读(678)  评论(0编辑  收藏  举报