zoukankan      html  css  js  c++  java
  • 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>
  • 相关阅读:
    vim命令大全
    docer中运行crontab
    基于预加载的热区域数据的简单设计
    解析Health端点数据获取异常数据
    微服务链路调用耗时示例图
    Spring Cloud health节点通过注册中心扫描状态的简单实现
    转载:Service Mesh:重塑微服务市场--敖小剑
    Springboot统一参数验证方式
    Spirng boot 启动的时候进行监控检查不通过停止服务与自定义健康监控节点
    准备 Python3 和 Python 虚拟环境
  • 原文地址:https://www.cnblogs.com/fsg6/p/14498037.html
Copyright © 2011-2022 走看看