zoukankan      html  css  js  c++  java
  • Vue(小案例_vue+axios仿手机app)_Vuex优化购物车功能

    一、前言                                                                   

            1、用vuex实现加入购物车操作

            2、购物车详情页面

             3、点击删除按钮,删除购物详情页面里的对应商品

    二、主要内容                                                            

     

    1、用vuex加入购物车

      (1)在src目录下创建store.js,

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    export default new Vuex.Store({
           //vuex五大将
        state:{
            num:1//小球的数量默认为1
        },
    
        getters:{
            getShopNum(state){
                return state.num;
            }
        },
    
        mutations:{
            addShopNum(state,num){//增加小球数量
                state.num +=num;
            },
    
            changeShopNum(state,num){//改变小球数量
                state.num = num;
            }
        },
    
        actions:{
            addShopNumByAction({commit},num){
                commit('addShopNum',num);
            },
    
            changeShopNum({commit}, num){
                commit('changeShopNum',num)
            }
        }
    
    })

      (2)在main.js入口文件中挂载,并且导入

    import store from './store.js'
    
    /* eslint-disableo-new */
    new Vue({
      el: '#app',
      router,
      store,//一定要导入
      components: { App },
      template: '<App/>'
    })

      (3)在app.vue(底部导航组件)中用computed监听这个pickNum

    computed:{
        pickNum(){
          return this.$store.getters.getShopNum
        }
      }

      

      (4)在点击“加入购物车”那个组件, 

     afterEnter(){
                    this.isExist=false; //显示出来之后执行这个,又将小球隐藏
    
                   /* 不用这个$bus
                   this.$bus.$emit('sendPickNum',this.pickNum);*/
                    
                    //用vuex, 触发action,
                    this.$store.dispatch('addShopNumByAction',this.pickNum);
                    //当触发了上面的事件之后,
                    GoodsTool.add({
                        id:this.goodsInfo.id,
                        num:this.$store.getters.getShopNum
                    })
                },

    2、购物车详情页面(上面点击+++,下面也要变化)

      (1)在购物车详情页面,每次点加,减的时候让他去触发action

    methods:{
                addNum(shop){//每次点击都接受到当前的对象
                    shop.num++; //这里的值虽然加上了,但是,数据并没有响应上去,是因为created是一开始就加载的,后来点击修改了num的值,但是没有 响应视图
                    this.$store.dispatch('addShopNumByAction',1)//触发action
                    console.log(shop)
                },
                substract(shop){
                    if(shop.num==1){
                        return;
                    }
    
                    shop.num--;
                    this.$store.dispatch('addShopNumByAction',-1)//触发action
    
                }
    }

      (2)要让底部导航栏里面的数量随着点击而发生变化

    created(){
        //当你的组件一创建好了后就挂载这个bus公交车,$on这个事件监听
       /* this.$bus.$on(`sendPickNum`, (data)=>{
          this.pickNum=this.pickNum + data;
        }),
    
        this.pickNum=GoodsTool.getTotalCount();*/
        //触发action里面的changShop方法,并且将当前的总数量传给他
    this.$store.dispatch('changeShopNum',GoodsTool.getTotalCount())
    
      }

    3、点击删除按钮,删除购物详情页面里的对应商品

    del(shop,index){//将当前的对象,和index传进来
                    this.shopCart.splice(index,1)//数组中的当前对象
                    delete GoodsTool[shop.id]
                    GoodsTool.removeGoods(shop.id)
    
                    let num = shop.um;
                    this.$store.dispatch('addShopNumByAction',-num)
    
                }

    4.通信的组件如下

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    export default new Vuex.Store({
        state:{
            num:1//小球的数量默认为1
        },
    
        getters:{
            getShopNum(state){
                return state.num;
            }
        },
    
        mutations:{
            addShopNum(state,num){
                state +=num;
            },
    
            changeShopNum(state,num){
                state.num = num;
            }
        },
    
        actions:{
            addShopNumByAction({commit},num){
                commit('addShopNum',num);
            },
    
            changeShopNum({commit}, num){
                commit('changeShopNum',num)
            }
        }
    
    })
    store.js
    let obj={};
    
    //这里需要存储数据
    //{商品的id, 商品的数量}
    
    //保存商品
    obj.saveGoods = function(goodsList){
        window.localStorage.setItem('goodsList',JSON.stringify(goodsList))
    }
    
    
    //获取商品的值,没有值传一个空对象
    
    obj.getGoodsList = function(){
        return JSON.parse(window.localStorage.getItem('goodsList'|| '{}'))
    }
    
    //增加商品
    obj.add = function(goods){
        let goodsList = this.getGoodsList()//获取到storage里面的对象
        if(goodsList[goods.id]){
    
            //goods.id是商品的数量,对应有值的话就追加
            goodsList[goods.id] = goodsList[goods.id] + goods.num;
        }else{
            goodsList[goods.id]=goods.num;
        }
    
        //传进来之后还需要保存
        this.saveGoods(goodsList);
    } 
    
    //获取购物车的总数量
    obj.getTotalCount = function(){
        let goodsList = this.getGoodsList();
        let values = Object.values(goodsList);//Object.values返回的是一个数组,里面对应着每一个key的value
        let sum = 0;
        values.forEach(val => sum = sum +val);
        return sum;
    
    }
    
    //删除
    obj.removeGoods=function(id){
        let goodsList = this.getGoodsList();
        delete goodsList[id];
        return this.saveGoods(goodsList)
    }
    export default obj;
    GoodsTool.js
    <template>
        <div>
            <go-back-header title="商品详情"></go-back-header>
            <div class="outer-swiper">
                <div class="swiper">
                    我真的是轮播图
                </div>
            </div>
            <div class="product-desc">
                <ul>
                    <li><span class="product-desc-span">
                        商品标题
                    </span></li>
                    <li class="price-li">市场价:
                        <s>¥{{goodsInfo.market_price}}</s> 销售价:<span>¥{{goodsInfo.sell_price}}</span></li>
                    <li class="number-li">购买数量:<span @click='substract'>-</span><span>{{pickNum}}</span><span @click='add'>+</span></li>
                    <li>
                        <mt-button type="primary">立即购买</mt-button>
                        <mt-button type="danger" size='small' @click='ballHandler'>加入购物车</mt-button>
                    </li>
                </ul>
            </div>
                <transition name='ball' @after-enter='afterEnter'>
                    <div class="ball" v-if="isExist"></div>
                </transition>
                <!--<div class="ball" v-if='isExist'></div>-->
            <div class="product-props">
                <ul>
                    <li>商品参数</li>
                    <li>商品货号:{{goodsInfo.goods_no}}</li>
                    <li>库存情况:{{goodsInfo.stock_quantity}}件</li>
                    <li>上架时间:{{goodsInfo.add_time}}</li>
                </ul>
            </div>
            <div class="product-info">
                <ul>
                    <li>
                        <mt-button type="primary" size="large" plain @click.native='showShopInfo()'>图文介绍</mt-button>
                    </li>
                    <li>
                        <mt-button type="danger" size="large" plain @click.native=''>商品评论</mt-button>
                    </li>
                </ul>
            </div>
        </div>
    </template>
    <script>
        import GoodsTool from './GoodsTool.js'
        export default{
            name:'GoodsDetail',
            data(){
                return{
                    url:`getthumImages/${this.$route.params.id}`,
                    goodsInfo:{},//当前购物车的信息,里面有id
                    pickNum:1 ,
                    isExist:false //让小球默认是隐藏的状态, 
                }
            },
    
            methods:{
    
                afterEnter(){
                    this.isExist=false; //显示出来之后执行这个,又将小球隐藏
    
                   /* 不用这个$bus
                   this.$bus.$emit('sendPickNum',this.pickNum);*/
                    
                    //用vuex, 触发action,
                    this.$store.dispatch('addShopNumByAction',this.pickNum);
                    //当触发了上面的事件之后,
                    GoodsTool.add({
                        id:this.goodsInfo.id,
                        num:this.$store.getters.getShopNum
                    })
                },
    
    
                //点击加入购物车执行这个方法,然后让小球显示出来
                ballHandler(){
    
                    this.isExist=true;
                  //  this.$bus.$emit('sendPickNum',this.pickNum); //将当前的pickNum传过去,但是这个不能加在这里,否者一点击“加入购物车就传
                },
                
                add(){
                    //如果当前的数小于库存数,就让他做加
                    if(this.pickNum < this.goodsInfo.stock_quantity){
                        this.pickNum++;
                    }
    
                    
                },
    
                substract(){
                    if(this.pickNum ===1){ //减法的时候最少为1,当此时值为1的时候不做操作
                        return;
                    }
    
                    this.pickNum--;
                },
    
    
    
                showShopInfo(){
    
                    //通过动态路由进行路由跳转
                    this.$router.push({
                        name:"photo.info",
                        query:{
                            id:this.$route.params.id
                        }
                    })
                },
    
                
    
                shopComment(){
                    this.$router.push({
                        name:'good.comment',
                        query:{
                            id:this.$route.params.id
                        }
                    })
                }
    
            },
    
            created(){
               //每个商品都只有一个对应的id,
                this.$axios.get(`goods/getinfo${this.$route.params.id}`)
                .then(res=>{
                    this.goodsInfo = res.data.message[0]
                })
                .catch(err=>{
                    console.log('商品详情异常',err)
                });
    
                this.pickNum = GoodsTool.getTotalCount();//获取购物的所有总数
    
            }
        }
    </script>
    <style scoped>
    .ball-enter-active {
        /*给1s的时间让小球进入动画效果*/
        animation: bounce-in 1s;
    }
    
    .bass-leave{
        /*元素进入以后,透明度0,整个动画都是0*/
        /*元素离开默认是1,所以会闪一下,设置为0*/
        opacity: 0;
    
    }
    
    @keyframes bounce-in {
        0% {
            transform: translate3d(0, 0, 0);
        }
        50% {
            transform: translate3d(140px, -50px, 0);
        }
        75% {
            transform: translate3d(160px, 0px, 0);
        }
        100% {
            transform: translate3d(140px, 300px, 0);
        }
    }
    
    .swiper {
        border: 1px solid rgba(0, 0, 0, 0.2);
        margin: 8px;
         95%;
        border-radius: 15px;
        overflow: hidden;
    }
    
    .outer-swiper,
    .product-desc,
    .product-props,
    .product-info {
        border-radius: 5px;
        border: 1px solid rgba(0, 0, 0, 0.2);
        margin: 3px;
    }
    
    
    /*请ulpadding*/
    
    .outer-swiper ul,
    .product-desc ul,
    .product-props ul,
    .product-info ul {
        padding: 0;
    }
    
    .product-desc ul li,
    .product-props ul li,
    .product-info ul li {
        list-style: none;
        font-size: 15px;
        color: rgba(0, 0, 0, 0.5);
        margin-top: 8px;
    }
    
    .product-desc ul >:nth-child(1) span {
        color: blue;
        font-size: 22px;
        font-weight: bold;
    }
    
    .product-desc ul >:nth-child(1) {
        border-bottom: 1px solid rgba(0, 0, 0, 0.5);
    }
    
    .product-desc ul,
    .product-info ul,
    .product-props ul {
        padding-left: 10px;
    }
    
    .price-li span {
        color: red;
        font-size: 25px;
    }
    
    .price-li s {
        margin-right: 16px;
    }
    
    
    /*加减*/
    
    .number-li span {
        display: inline-block;
        border: 2px solid rgba(0, 0, 0, 0.1);
         25px;
    }
    
    
    /*商品参数*/
    
    .product-props ul >:nth-child(1) {
        text-align: left;
    }
    
    .product-props ul:not(:nth-child(1)) {
        margin-left: 40px;
    }
    
    .product-info ul {
        margin-bottom: 70px;
        padding: 0 5;
    }
    
    .number-li span {
        text-align: center;
    }
    
    .number-li >:nth-child(2) {
         40px;
    }
    
    .ball {
        border-radius: 50%;
        background-color: red;
         24px;
        height: 24px;
        position: absolute;
        top: 440px;
        left: 120px;
        display: inline-block;
        z-index: 9999;
    }
    </style>
    GoodsDetail.vue
    <template>
      <div id='app'>
        <!--顶部-->
         <mt-header title="信息管理系统" fixed>
         <router-link to="/" slot="left">
           <mt-button icon="back">返回</mt-button>
           </router-link>
           <mt-button icon="more" slot="right"></mt-button>
         </mt-header>
    
    
       
    
    
    
        <!--底部-->
        <div class="tabBar">
          <ul>
            <li v-for="(tab, index) in tabs">
              <router-link :to="tab.routerName">
                <img :src="tab.imgSrc">
                <!--小球-->
                <mt-badge size='small' color="#FC0107" v-if='index===2'>{{pickNum}}</mt-badge>
                <p>{{tab.title}}</p>
              </router-link>
            </li>
          </ul>
          
        </div>
       <router-view></router-view>
    
      </div>
        
    </template>
    
    <script>
      import index from './assets/index.png'
      import vip from './assets/vip.png'
      import shopcart from './assets/shopcart.png'
      import search from './assets/search.png'
      
      let tabs = [
        {id:1, title:"首页", imgSrc:index, routerName:{name:'home'}},
        {id:2, title:"会员", imgSrc:vip, routerName:{name:'vip'}},
        {id:3, title:"购物车", imgSrc:shopcart, routerName:{name:'cart'}},
        {id:4, title:"查找", imgSrc:search, routerName:{name:'search'}}
    
    
      ]
    
      import GoodsTool from './GoodsTool.js'
    export default {
    
      name: 'App',
      data(){
        return {
         
          tabs:tabs,
          //pickNum:0,//底部栏小球
    
        }
      },
    
     watch:{
      selected:function(newV,oldV){
        
        console.log(newV);
        console.log(oldV);
        console.log(this.selected);//id绑定的id
        this.$router.push({name:this.selected});
    
      },
    
      computed:{
        pickNum(){
          return this.$store.getters.getShopNum
        }
      }
    
      created(){
        //当你的组件一创建好了后就挂载这个bus公交车,$on这个事件监听
       /* this.$bus.$on(`sendPickNum`, (data)=>{
          this.pickNum=this.pickNum + data;
        }),
    
        this.pickNum=GoodsTool.getTotalCount();*/
        this.$store.dispatch('changeShopNum',GoodsTool.getTotalCount())
    
      }
     }
    }
    </script>
    
    <style scoped>
    .tabBar{
       100%;
      height: 55px;
      background-color: #ccc;
      position: absolute;
      bottom: 0;
      left: 0;
      background-image: linear-gradient(180deg, #d9d9d9, #d9d9d9 50%, transparent 50%);
       background-size: 100% 1px;
      background-repeat: no-repeat;/*做一像素渐变线*/
      background-position: top left;
      background-color: #fafafa;
    }
    
    .tabBar ul{
       100%;
      overflow: hidden;
    }
    
    .tabBar ul li{
      float: left;
       25%;
      height: 55px;
      text-align: center;
    }
    
    .tabBar ul li a{
      display: inline-block;
       100%;
      height: 100%;
     padding-top: 10px;
    
    }
    .tabBar ul li a.link-active{
      background-color: pink;
      position: relative;
    }
    .tabBar ul li a img{
       25px;
      height: 25px;
    }
    .tabBar ul li a p{
      font-size: 12px;
    }
    
    /*重写一下小球的颜色*/
    .mint-bage.is-size-small{
      position: absolute;
      top: 0;
      right: 10px;
    }
    </style>
    App.vue
    <template>
        <div>
            <div class="pay-detail">
                <ul>
                    <li class="p-list" v-for="(shop, index) in shopCart">
                        <mt-switch></mt-switch>
                        <img src="">
                        <div class="pay-calc">
                            <p>{{shop.title}}</p>
                            <div class="calc">
                                <span>¥777</span>
                                <span @click="substract(shop)">-</span>
                                <span>{{shop.num}}</span>
                                <span @click="addNum(shop)">+</span>
                                <a href="javascript:;">删除</a>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
            <div class="show-price">
                <div class="show-1">
                    <p>总计(不含运费):</p>
                    <span>已经选择商品1件,总价¥888元</span>
                </div>
                <div class="show-2">
                    <mt-button type="danger" size="large">去结算</mt-button>
                </div>
            </div>
        </div>
    </template>
    <script>
        import GoodTool from '@/GoodsTool.js'
        export default {
            name:'Cart',
            data(){
                return{
                    shopCart:[]
    
                }
            },
            methods:{
                addNum(shop){//每次点击都接受到当前的对象
                    shop.num++; //这里的值虽然加上了,但是,数据并没有响应上去,是因为created是一开始就加载的,后来点击修改了num的值,但是没有 响应视图
                    this.$store.dispatch('addShopNumByAction',1)
                    console.log(shop)
                },
                substract(shop){
                    if(shop.num==1){
                        return;
                    }
    
                    shop.num--;
                    this.$store.dispatch('addShopNumByAction',-1)
    
                },
    
                del(shop,index){//将当前的对象,和index传进来
                    this.shopCart.splice(index,1)//数组中的当前对象
                    delete GoodsTool[shop.id]
                    GoodsTool.removeGoods(shop.id)
    
                    let num = shop.um;
                    this.$store.dispatch('addShopNumByAction',-num)
    
                }
    
            },
            computed:{
                payment(){
                    let total = 0;//定义总金额
                    let count =0;//定义总数量
    
                    this.shopCart.forEach((shop)=>{
                        if(shop.isSelected){
                            count = count+shop.num;
                            total = total + shop.num * shop.sell_price;
                        }
                    })
    
                    return {
                        total, count
                    }
                }
            },
            
        
    
            created(){
                let goodsList = GoodsTool.getGoodsList();//{"88":5,"99":4} 第一个数商品id 第二个是商品数量
                let ids = Object.key(goodsList).join(',');//Object.key()获取key值 [88,99]
                if(ids){
                    this.$axios.get(`goods/getshopcarlist${ids}`)
                    .then(res=>{
    
                        this.shopCart=res.data.message;//返回的是一个数组
                      //给数组元素添加属性
                      for(var i=0; i<this.shopCart.length;i++){
                          let shop=this.shopCart[i];//获取到当前的对象
                          let num = goodsList[shop.id];//根据当前对象的id查找到对应的购物车数量
    
                          if(num){
                              shop.num = num;
    
                              this.$set(shop, 'num', num);//自己给这个数据进行双向数据绑定
                              this.$set(shop,'isSelected',true);
    
                          }
    
                      }
                    })
                }
            }
        }
    </script>
    <style scoped>
    .pay-detail ul li {
        list-style: none;
        border-bottom: 1px solid rgba(0, 0, 0, 0.2);
        margin-bottom: 3px;
    }
    
    .pay-detail ul {
        padding-left: 5px;
        margin-top: 5px;
    }
    
    .pay-detail ul li img {
         80px;
        height: 80px;
        display: inline-block;
        vertical-align: top;
        margin-top: 10px;
    }
    
    .pay-detail ul li >:nth-child(1),
    .pay-detail ul li >:nth-child(3) {
        display: inline-block;
    }
    
    .pay-calc p {
        display: inline-block;
         250px;
        overflow: hidden;
        color: blue;
        font-size: 15px;
        margin-bottom: 10px;
    }
    
    .pay-detail ul li >:nth-child(1) {
        line-height: 80px;
    }
    
    .calc:nth-child(1) {
        color: red;
        font-size: 20px;
    }
    
    .calc span:not(:nth-child(1)) {
        border: 1px solid rgba(0, 0, 0, 0.3);
        display: inline-block;
         20px;
        text-align: center;
    }
    
    .calc a {
        margin-left: 20px;
    }
    
    .show-1,
    .show-2 {
        display: inline-block;
    }
    
    .show-1,
    .show-2 {
        margin-left: 30px;
    }
    
    .show-price {
        background-color: rgba(0, 0, 0, 0.2);
    }
    </style>
    Cart.vue

    三、总结                                                                   

    虽然现在走得很慢,但不会一直这么慢
  • 相关阅读:
    BeanPostProcessor后置处理器原理以及ApplicationListener原理
    SpringCloud之服务注册与发现Eureka+客户端Feign
    AJPFX关于TreeSet集合的介绍
    AJPFX:关于面向对象的封装
    AJPFX总结Java 类与对象的初始化
    AJPFX关于增强for的概述和使用(foreach)
    AJPFX总结泛型概念和使用
    AJPFX简述可变参数概述和使用
    AJPFX关于JAVA StringBuffer的用法总结
    AJPFX关于Collection 集合的表述
  • 原文地址:https://www.cnblogs.com/xxm980617/p/10725439.html
Copyright © 2011-2022 走看看