新建vue项目

vue vuex 购物车案例_javascript

 此项目需要用到json-server,模拟后台服务器接口功能,

npm i json-server -g

安装此包,然后再vue项目的全局根目录中新建db文件夹,其中新建index.json文件。

{
  "cart": [
    {
      "id": 1001,
      "name": "低帮城市休闲户外鞋天然牛皮COOLMAX纤维",
      "price": 128,
      "count": 3,
      "thumb": "https://yanxuan-item.nosdn.127.net/3a56a913e687dc2279473e325ea770a9.jpg"
    },
    {
      "id": 1002,
      "name": "网易未央黑猪猪肘330g*1袋",
      "price": 39,
      "count": 3,
      "thumb": "https://yanxuan-item.nosdn.127.net/3a56a913e687dc2279473e325ea770a9.jpg"
    },
    {
      "id": 1003,
      "name": "KENROLL男女简洁多彩一片室外拖",
      "price": 88,
      "count": 3,
      "thumb": "https://yanxuan-item.nosdn.127.net/3a56a913e687dc2279473e325ea770a9.jpg"
    },
    {
      "id": 1004,
      "name": "低帮城市休闲户外鞋天然牛皮COOLMAX纤维",
      "price": 99,
      "count": 6,
      "thumb": "https://yanxuan-item.nosdn.127.net/3a56a913e687dc2279473e325ea770a9.jpg"
    }
  ],
  "friends": [
    {
      "id": 1,
      "name": "hang",
      "age": 18
    },
    {
      "id": 2,
      "name": "wang",
      "age": 29
    }
  ]
}

模拟数据库,在db文件夹下,调用系统cmd命令,启动模拟服务器

json-server --watch index.json

vue vuex 购物车案例_javascript_02

 

vue vuex 购物车案例_json_03

 main.js

import Vue from 'vue'
import App from './App.vue'
import store from "@/store";

Vue.config.productionTip = false;

new Vue({
  render: h => h(App),
  store
}).$mount('#app');

app.vue

<template>
  <div id="app">
    <!-- Header 区域   -->
    <cart-header></cart-header>

    <!-- Item 区域   -->
    <cart-item v-for="item in list" :key="item.id" :item="item"></cart-item>

    <!-- Footer 区域   -->
    <cart-footer></cart-footer>
  </div>
</template>

<script>
import CartHeader from "@/components/CartHeader";
import CartItem from "@/components/CartItem";
import CartFooter from "@/components/CartFooter";
import { mapState } from 'vuex'

export default {
  name: 'App',
  components: {
    CartHeader,
    CartItem,
    CartFooter
  },
  computed:{
    ...mapState('cart',['list'])
  },
  created() {
    this.$store.dispatch('cart/getList');
  }
}
</script>

<style>
  *{
    padding: 0;
    margin: 0;
  }
</style>

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import cart from "@/store/modules/cart";

Vue.use(Vuex);

const store = new Vuex.Store({
    modules:{
        cart
    }
});

export default store;

cart.js

import axios from "axios";
export default {
    //开启命名空间
    namespaced:true,
    state(){
        return {
            list:[]
        }
    },
    getters:{
        //总数
        total(state){
            return state.list.reduce( (sum,item) => sum += item.count,0);
        },
        //总金额
        totalPrice(state){
            return state.list.reduce( (sum,item) => sum + item.count * item.price ,0);
        }
    },
    mutations:{
        updateList(state,newList){
            state.list = newList;
        },
        //
        updateCount(state,obj){
            //根据id找到对应的对象,更新count属性
            const goods = state.list.find( item => item.id === obj.id);
            goods.count = obj.newCount;
        }
    },
    actions:{
        //请求方式 get
        //请求地址:http://localhost:3000/cart
        async getList(context){
            const res = await axios.get('http://localhost:3000/cart');
            //console.log(res);
            context.commit('updateList',res.data);
        },
        //请求方式 patch
        //请求地址:http://localhost:3000/cart/:id值
        //请求参数 { name:'新值', [可选]}
        //
        async updateCountAsync(context,obj){
            //将修改更新同步到后台服务器
            const res = await axios.patch(`http://localhost:3000/cart/${obj.id}`,{
                count: obj.newCount
            });
            console.log(res);
            //调用mutation
            context.commit('updateCount',{
                id: obj.id,
                newCount: obj.newCount
            });
        }
    }
}

CartHeader.vue

<template>
    <div class="cart-header">
        <div class="title">购物车案例</div>
    </div>
</template>

<script>
    export default {
        name: "CartHeader"
    }
</script>

<style scoped>
    .cart-header{
        width: 100%;
        height: 70px;
        background-color: mediumseagreen;
        margin-bottom: 10px;
    }

    .cart-header .title{
        color: white;
        font-size: 34px;
        width: 200px;
        height: 100%;
        line-height: 70px;
        text-align: center;
        margin: 0 auto;
    }
</style>

CartItem.vue

<template>
    <div class="cart-item">
        <!-- 左侧图片区域       -->
        <div class="left">
            <img :src="item.thumb" alt="" class="avatar">
        </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",
        props:{
            item: {
                type: Object,
                required:true
            }
        },
        methods:{
            btnClick(step){
                const newCount = this.item.count + step;
                const id = this.item.id;
                //console.log(id,newCount);
                if(newCount < 1) return;
                this.$store.dispatch('cart/updateCountAsync',{
                    id,
                    newCount,
                });
            }
        }
    }
</script>

<style scoped>


    .cart-item{
        width: 98%;
        height: 200px;
        margin-left: 2%;
    }

    .cart-item .left{
        width: 180px;
        height: 180px;
        float: left;
    }

    .left img{
        width: 100%;
        height: 100%;
    }

    .cart-item .right{
        width: 30%;
        height: 100%;
        float: left;
    }

    .right .title{
        text-indent: 40px;
        font-size: 24px;
        height: 75%;
        font-family: Arial,"Arial Black",Georgia;
    }

    .right .info{
        width: 100%;
        height: 25%;
        line-height: 30px;

    }

    .right .price{
        padding: 3px;
        margin-left: 40px;
        color: orange;
        font-size: 24px;
    }

    .right .btns{
        float: right;
    }

    .btns .btn{
        width: 30px;
        height: 33px;
        line-height: 30px;
        font-size: 20px;
        margin: 0 15px;
    }

    .btns .count{
        display: inline-block;
        width: 25px;
        text-align: center;
        font-size: 20px;
        font-weight: bold;
    }

    .btns .btn-light{
        background-color: #cccccc;
    }
</style>

CartFooter.vue

<template>
    <div class="cart-footer">
        <!--  中间合计      -->
        <div>
            <span>共 {{ total }} 件商品,合计:</span>
            <span class="price">¥ {{ totalPrice }}</span>
        </div>
        <!--  右侧结算按钮      -->
        <button class="btn">结算</button>
    </div>
</template>

<script>
    import { mapGetters} from 'vuex'
    export default {
        name: "CartFooter",
        computed:{
            ...mapGetters('cart',['total','totalPrice'])
        }
    }
</script>

<style scoped>
    .cart-footer{
        width: 30%;
        height: 50px;
        font-size: 20px;
        float: right;
    }

    .cart-footer div{
        display: inline;
        width: 400px;
        margin-left: 8%;
    }
    .cart-footer .price{
        font-size: 30px;
        font-weight: bold;
        color: red;
    }

    .cart-footer .btn{
        width: 150px;
        height: 50px;
        background-color: mediumseagreen;
        border-radius: 30px;
        float: right;
        margin-right: 40px;
    }
</style>

vue vuex 购物车案例_vue.js_04