Vue商城项目05(完)

实现购物车效果

用vuex并设计购物车数据存储方式

  1. 运行 cnpm i vuex -S
  2. import Vuex from ‘vuex’
  3. Vue.use(Vuex)
  4. var store = new Vuex.Store({})
  5. 在vm实例中挂载 store 状态管理对象 store

点击加入购物车功能

    addToShopCar() {
      // 添加到购物车
      this.ballFlag = !this.ballFlag;
      // { id:商品的id, count: 要购买的数量, price: 商品的单价,selected: false  }
      // 拼接出一个,要保存到 store 中 car 数组里的 商品信息对象
      var goodsinfo = {
        id: this.id,
        count: this.selectedCount,
        price: this.goodsinfo.sell_price,
        selected: true
      };
      // 调用 store 中的 mutations 来将商品加入购物车
      this.$store.commit("addToCar", goodsinfo);
    }

购物车处徽标数值的自动更新

				<span class="mui-badge" id="badge">{{ $store.getters.getAllCount }}</span>
				<span class="mui-tab-label">购物车</span>
  getters: { // this.$store.getters.***
    // 相当于 计算属性,也相当于 filters
    getAllCount(state) {
      var c = 0;
      state.car.forEach(item => {
        c += item.count
      })
      return c
    }
  }

实现购物车商品的本地持久存储

每次刚进入 网站,肯定会 调用 main.js 在刚调用的时候,先从本地存储中,把 购物车的数据读出来,放到 store 中
var car = JSON.parse(localStorage.getItem('car') || '[]')

当 更新 car 之后,把 car 数组,存储到 本地的 localStorage 中
localStorage.setItem('car', JSON.stringify(state.car))

绘制购物车页面中商品列表的布局

借助于mui-master,examples,card.html。
复制一个基础的组件shopcar_numbox.vue,有选择数量按钮。可以直接加样式style="height:25px;"改变高度

获取购物车中所有商品列表并加载显示

    getGoodsList() {
      // 1. 获取到 store 中所有的商品的Id,然后拼接出一个 用逗号分隔的 字符串
      var idArr = [];
      this.$store.state.car.forEach(item => idArr.push(item.id));
      // 如果 购物车中没有商品,则直接返回,不需要请求数据接口,否则会报错
      if (idArr.length <= 0) {
        return;
      }
      // 获取购物车商品列表。join(",")根据接口文档需要传入的办法,用逗号分隔开传过去
      this.$http
        .get("api/goods/getshopcarlist/" + idArr.join(","))
        .then(result => {
          if (result.body.status === 0) {
            this.goodslist = result.body.message;
          }
        });
    }

在循环购物车列表时候初始化数量值,购物车商品数量改变同步到store中

main.js里

getters:
    getGoodsCount(state) {
      var o = {}
      state.car.forEach(item => {
        o[item.id] = item.count
      })
      return o
    }

mutations:
    updateGoodsInfo(state, goodsinfo) {
      // 修改购物车中商品的数量值
      // 分析: 
      state.car.some(item => {
        if (item.id == goodsinfo.id) {
          item.count = parseInt(goodsinfo.count)
          return true
        }
      })
      // 当修改完商品的数量,把最新的购物车数据,保存到 本地存储中
      localStorage.setItem('car', JSON.stringify(state.car))
    }

父组件:

               <numbox :initcount="$store.getters.getGoodsCount[item.id]" :goodsid="item.id"></numbox>
                <!-- 问题:如何从购物车中获取商品的数量呢 -->
                <!-- 1. 我们可以先创建一个 空对象,然后循环购物车中所有商品的数据, 把 当前循环这条商品的 Id, 作为 对象 的 属性名,count值作为 对象的 属性值,这样,当把所有的商品循环一遍,就会得到一个对象: { 88: 2, 89: 1, 90: 4 } -->

子组件:

<input  :value="initcount" />
 methods: {
    countChanged() {
      // 数量改变了
      // console.log(this.$refs.numbox.value);
      // 每当数量值改变,则立即把最新的数量同步到 购物车的  store 中,覆盖之前的数量值
      this.$store.commit("updateGoodsInfo", {
        id: this.goodsid,
        count: this.$refs.numbox.value
      });
    }
  },
  props: ["initcount", "goodsid"]

实现购物车中商品的删除

    <a href="#" @click.prevent="remove(item.id, i)">删除</a>
    <!--  prevent阻止默认事件  -->

      remove(id, index) {
      // 点击删除,把商品从 store 中根据 传递的 Id 删除,同时,把 当前组件中的 goodslist 中,对应要删除的那个商品,使用 index 来删除
      this.goodslist.splice(index, 1); //第一个参数是删除项目的位置,第二个是要删除的项目数量。
      this.$store.commit("removeFormCar", id);
    }

main.js里的store里的mutations:
      removeFormCar(state, id) {
      // 根据Id,从store 中的购物车中删除对应的那条商品数据
      state.car.some((item, i) => {
        if (item.id == id) {
          state.car.splice(i, 1)
          return true;
        }
      })
      // 将删除完毕后的,最新的购物车数据,同步到 本地存储中
      localStorage.setItem('car', JSON.stringify(state.car))
    }

绘制结算区域样式

    display: flex; //让里面竖着的的盒子一行排放
    justify-content: space-between;//两端对齐
    align-items: center;//垂直居中

把store中选中的状态同步到页面上,同步商品的勾选状态到store中保存

计算属性是基于响应式依赖进行缓存的,只有数据发生变化时,才会重新计算,否则直接调用缓存

    // $store.getters.getGoodsSelected的值为{"88":true}(id为键,值为selected状态)            
    //v-model=true时为打开开关
            <mt-switch 
              v-model="$store.getters.getGoodsSelected[item.id]"
              @change="selectedChanged(item.id, $store.getters.getGoodsSelected[item.id])">
            </mt-switch>

  组件里的方法
    selectedChanged(id, val) {
      // 每当点击开关,把最新的 快关状态,同步到 store 中
      // console.log(id + " --- " + val);
      this.$store.commit("updateGoodsSelected", { id, selected: val });
    }          


main.js里 
 getters:
    getGoodsSelected(state) {
      var o = {}
      state.car.forEach(item => {
        o[item.id] = item.selected
      })
      return o
    }

  mutations:
      updateGoodsSelected(state, info) {
      state.car.some(item => {
        if (item.id == info.id) {
          item.selected = info.selected
        }
      })
      // 把最新的 所有购物车商品的状态保存到 store 中去
      localStorage.setItem('car', JSON.stringify(state.car))
    }

实现勾选数量和总价的自动计算

main.js中  getters: 
    getGoodsCountAndAmount(state) {
      var o = {
        count: 0, // 勾选的数量
        amount: 0 // 勾选的总价
      }
      state.car.forEach(item => {
        if (item.selected) {
          o.count += item.count
          o.amount += item.price * item.count
        }
      })
      return o
    }

    组件里:
              <p>已勾选商品 <span class="red">{{ $store.getters.getGoodsCountAndAmount.count }}</span> 件, 总价 <span class="red">¥{{ $store.getters.getGoodsCountAndAmount.amount }}</span></p>

实现返回按钮的功能

在Mint-ui官网里找,简单修改一下app.vue

    <mt-header fixed title="黑马程序员·Vue项目">
      <span slot="left" @click="goBack" v-show="flag">
        <mt-button icon="back">返回</mt-button>
      </span>
    </mt-header>

<script>
export default {
  data() {
    return {
      flag: false
    };
  },
  created() {
    this.flag = this.$route.path === "/home" ? false : true;
  },
  methods: {
    goBack() {
      // 点击后退
      this.$router.go(-1);
    }
  },
  watch: { //监听路由的变化
    "$route.path": function(newVal) {
      if (newVal === "/home") {
        this.flag = false;
      } else {
        this.flag = true;
      }
    }
  }
};
</script>

tomcat虚拟目录

server.xml文件里的<Connector port="80"></Connector>是打开的端口号80

在Tomcat安装目录下的conf文件夹找到server.xml文件,在结尾处的</Host>前面加上
<Context path="" reloadable="true" docBase="D:\Demo" />
path为浏览器访问目录,docBase项目的虚拟根目录
开启Tomcat服务器,在浏览器访问localhost:80即可看到该目录

项目外网测试

将项目托管到Apache并启用:
先把dist删除,然后webpack打包会放到dist中,把dist中的index.html,bundle.js拷贝到docBase项目的虚拟根目录
注意:如果没有设置虚拟根目录,放在默认的根目录中
输入172.0.0.1:80或者localhost:80即可看到本地

扫描二维码关注公众号,回复: 9908470 查看本文章

开启Apache的gzip压缩

要让apache支持gzip功能,要用到deflate_Module和headers_Module。打开apache的配置文件httpd.conf,大约在105行左右,找到以下两行内容:(这两行不是连续在一起的)

#LoadModule deflate_module modules/mod_deflate.so
#LoadModule headers_module modules/mod_headers.so

然后将其前面的“#”注释删掉,表示开启gzip压缩功能。开启以后还需要进行相关配置。在httpd.conf文件的最后添加以下内容即可:

<IfModule deflate_module>
    #必须的,就像一个开关一样,告诉apache对传输到浏览器的内容进行压缩
    SetOutputFilter DEFLATE
    DeflateCompressionLevel 9
</IfModule>

最少需要加上以上内容,才可以生gzip功能生效。由于没有做其它的额外配置,所以其它相关的配置均使用Apache的默认设置。这里说一下参数“DeflateCompressionLevel”,它表示压缩级别,值从1到9,值越大表示压缩的越厉害。

使用ngrok将本机映射为一个外网的Web服务器

打开ngrok.exe,输入ngrok http 80
注意 80 是项目运行的端口号
Session Status online时说明已经上线。其中Forwarding后面的就是地址,直接输入即可。

注意:由于默认使用的美国的服务器进行中间转接,所以访问速度炒鸡慢,访问时可启用f墙软件,提高网页打开速度。

发布了59 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41160739/article/details/104808130