Vue商城项目04

绘制 商品列表 页面基本结构并美化

<template>
  <div class="goods-list">
    
    <div class="goods-item">
      <img src="https://img14.360buyimg.com/n0/jfs/t1/68656/34/7819/87669/5d5e0becE1808db2a/4f29ef986f103e36.jpg" alt="">
      <h1 class="title">小米(Mi)小米Note 16G双网通版</h1>
      <div class="info">
        <p class="price">
          <span class="now">¥899</span>
          <span class="old">¥999</span>
        </p>
        <p class="sell">
          <span>热卖中</span>
          <span>剩60件</span>
        </p>
      </div>
    </div>

    <div class="goods-item">
      <img src="https://img14.360buyimg.com/n0/jfs/t1/68656/34/7819/87669/5d5e0becE1808db2a/4f29ef986f103e36.jpg" alt="">
      <h1 class="title">尼康(Nikon)D3300套机(18-55mm f/3.5-5.6G VRII)(黑色)</h1>
      <div class="info">
        <p class="price">
          <span class="now">¥899</span>
          <span class="old">¥999</span>
        </p>
        <p class="sell">
          <span>热卖中</span>
          <span>剩60件</span>
        </p>
      </div>
    </div>

    <div class="goods-item">
      <img src="https://img14.360buyimg.com/n0/jfs/t1/68656/34/7819/87669/5d5e0becE1808db2a/4f29ef986f103e36.jpg" alt="">
      <h1 class="title">小米(Mi)小米Note 16G双网通版</h1>
      <div class="info">
        <p class="price">
          <span class="now">¥899</span>
          <span class="old">¥999</span>
        </p>
        <p class="sell">
          <span>热卖中</span>
          <span>剩60件</span>
        </p>
      </div>
    </div>

  </div>
</template>

<script>
</script>

<style lang="scss" scoped>
.goods-list{
  display: flex;
  flex-wrap: wrap;//默认情况下,flex项目都会尝试适合一行。 wrap规定灵活的项目在必要的时候拆行或拆列。
  padding: 7px;
  justify-content: space-between; //让里面的两个盒子靠边对齐

  .goods-item{
    width: 49%;  //中间有2%的空隙
    border: 1px solid #ccc;
    box-shadow: 0 0 8px #ccc;
    margin: 4px 0;
    padding: 2px;
    display: flex;
    flex-direction: column;//改变主轴row沿x轴的方向成沿y轴
    justify-content: space-between;//沿y轴上两个盒子靠边对齐,不然底部可能有很大空隙
    min-height: 293px;//找盒子的最小高度,在还没加载出来有一个最小高度
    img{
      width: 100%;
    }
    .title{
      font-size: 14px;
    }

    .info{
      background-color: #eee;
      p{
        margin: 0;//p标签有默认Margin
        padding: 5px;
      }
      .price{
        .now{
          color: red;
          font-weight: bold;
          font-size: 16px;
        }
        .old{
          text-decoration: line-through; //删除线
          font-size: 12px;
          margin-left: 10px;
        }
      }
      .sell{
        display: flex;
        justify-content: space-between;
        font-size: 13px;
      }
    }
  }
}
</style>

尝试在手机上 去进行项目的预览和测试

vue项目如何通过电脑连接手机热点在手机上访问:(wifi同理 无线局域网适配器 本地连接的IPV4)

  1. 首先当然得让电脑连上手机热点,使他们处于同一个局域网中;
  2. 去命令行工具输入 ipconfig获取电脑的ip,如下图所示IPv4地址就是本机电脑的IP
    无线局域网适配器 WLAN: IPv4 地址
  3. 去vue项目中修改配置项, package.json文件
    “dev”: “webpack-dev-server --open --port 3000 --hot --host IPv4 地址”
  4. 在电脑上打开之后,把地址输入到手机上即可。

在手机运行的时候,有的页面显示不该显示的东西。把click换成tap。
mui提供了tap事件替换了html5的click事件
@tap=“getPhotoListByCateId(item.id)”

加载商品列表中的数据

vue-router中编程式导航

    <!-- 在网页中,有两种跳转方式: -->
    <!-- 方式1: 使用 a 标签 的形式叫做 标签跳转  -->
    <!-- 方式2: 使用 window.location.href 的形式,叫做 编程式导航 -->
     <div class="goods-item" v-for="item in goodslist" :key="item.id" @click="goDetail(item.id)"></div>

     goDetail(id) {
      // 使用JS的形式进行路由导航
      // 注意: 一定要区分 this.$route 和 this.$router 这两个对象,
      // 其中: this.$route 是路由【参数对象】,所有路由中的参数, params, query 都属于它
      // 其中: this.$router 是一个路由【导航对象】,用它 可以方便的 使用 JS 代码,实现路由的 前进、后退、 跳转到新的 URL 地址

      // 1. 最简单的传字符串
      // this.$router.push("/home/goodsinfo/" + id);
      // 2. 传递对象
      // this.$router.push({ path: "/home/goodsinfo/" + id });
      // 3. 传递命名的路由
      this.$router.push({ name: "goodsinfo", params: { id } });
    }

绘制商品详情页面的卡片视图

在hello-mui,examples,card.html中 找对应的代码拷贝。
父级元素内部有子元素,如果给子元素添加margin-top样式,那么父级元素也会跟着下来,造成外边距塌陷:
overflow: hidden;解决外边距塌陷
id: this.$route.params.id, // 将路由参数对象中的 id 挂载到 data , 方便后期调用

抽离轮播图组件 轮播图宽度的设置

<template>
  <div>
    <mt-swipe :auto="4000">
      <!-- 将来,谁使用此 轮播图组件,谁为我们传递 lunbotuList -->
      <!-- 此时,lunbotuList 应该是 父组件向子组件传值来设置 -->
      <mt-swipe-item v-for="item in lunbotuList" :key="item.url">
        <img :src="item.img" alt="" :class="{'full': isfull}">  
         <!--如果isfull为true,则应用full类宽度为100%。,否则宽度自适应-->
      </mt-swipe-item>
    </mt-swipe>
  </div>

</template>
<script>
export default {
  props: ["lunbotuList", "isfull"]
};
</script>
<style>
.full {
  width: 100%;
}
</style>

其他页面引入该组件
import swiper from "../subcomponents/swiper.vue";
components: {swiper}
<swiper :lunbotuList="lunbotuList" :isfull="false"></swiper>
方法自己写

    getLunbotu() {
     this.$http.get("api/getthumimages/" + this.id).then(result => {
       if (result.body.status === 0) {
         // 先循环轮播图数组的每一项,为 item 添加 img 属性,因为 轮播图 组件中,只认识 item.img, 不认识 item.src
         result.body.message.forEach(item => {
           item.img = item.src;
         });
         this.lunbotu = result.body.message;
       }
     });
   },

一点样式:
<del>¥oldprice</del>删除线
display: flex;的时候 br 不换行,若不想一行排列,可以让display: block;

购物车小球动画效果

页面中id="badge"购物车的位置:

			<router-link class="mui-tab-item-llb" to="/shopcar">
				<span class="mui-icon mui-icon-extra mui-icon-extra-cart">
					<span class="mui-badge" id="badge">0</span>
				</span>
				<span class="mui-tab-label">购物车</span>
			</router-link>

一些关键代码:

    <!-- transition的过渡-->
    <transition
      @before-enter="beforeEnter"
      @enter="enter"
      @after-enter="afterEnter">
    <!-- 只要想要在Vue中直接操作DOM元素,就必须用ref属性进行注册
    ref在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;this.$refs.ball获取当前元素-->
      <div class="ball" v-show="ballFlag" ref="ball"></div>
    </transition>

    <mt-button type="danger" size="small" @click="addToShopCar">加入购物车</mt-button>

  data() {
    return {
      ballFlag: false, // 控制小球的隐藏和显示的标识符

    };

        addToShopCar() {
      // 添加到购物车
      this.ballFlag = !this.ballFlag;
    },
    beforeEnter(el) {
      el.style.transform = "translate(0, 0)";
      //translate(0, 0)位置(0, 0)
    },
    enter(el, done) {
      el.offsetWidth;
      //offsetWidth = width + padding +border

      // 小球动画优化思路:
      // 1. 先分析导致 动画 不准确的 本质原因: 我们把 小球 最终 位移到的 位置,已经局限在了某一分辨率下的 滚动条未滚动的情况下;
      // 2. 只要分辨率和 测试的时候不一样,或者 滚动条有一定的滚动距离之后, 问题就出现了;
      // 3. 因此,我们经过分析,得到结论: 不能把 位置的 横纵坐标 直接写死了,而是应该 根据不同情况,动态计算这个坐标值;
      // 4. 经过分析,得出解题思路: 先得到 徽标的 横纵 坐标,再得到 小球的 横纵坐标,然后 让 y 值 求差, x 值也求 差,得到 的结果,就是横纵坐标要位移的距离
      // 5. 如何 获取 徽标和小球的 位置   domObject.getBoundingClientRect()

      // 获取小球的 在页面中的位置
      const ballPosition = this.$refs.ball.getBoundingClientRect();
      // 获取 徽标 在页面中的位置,只要页面中有,就可以获取到
      const badgePosition = document
        .getElementById("badge")
        .getBoundingClientRect();

      const xDist = badgePosition.left - ballPosition.left;
      const yDist = badgePosition.top - ballPosition.top;

      // ${xDist}模版字符串,等价于' + xDist + '
      el.style.transform = `translate(${xDist}px, ${yDist}px)`;
      el.style.transition = "all 0.5s cubic-bezier(.4,-0.3,1,.68)";
      done();
    },
    afterEnter(el) {
      this.ballFlag = !this.ballFlag;
    }

  },

    .ball {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background-color: red;
    position: absolute;
    z-index: 99;  //让其在最上面显示
    top: 390px; //找位置调试的时候在f12里鼠标滑动值
    left: 146px;
  }


当子组件numbox里面的值发生变化立即传给父组件,同时有最大数量

子组件:

<template>
<!-- 问题: 因为该max值是从父组件调用的方法里取到的,可能在拿到值前面该组件就被渲染完毕。我们不知道什么时候能够拿到 max 值,但是,总归有一刻,会得到一个真正的 max 值 -->
<!-- 我们可以 使用 watch 属性监听,来 监听 父组件传递过来的 max 值,不管 watch 会被触发几次,但是,最后一次,肯定是一个 合法的 max 数值 -->
  <div class="mui-numbox" data-numbox-min='1'>
    <button class="mui-btn mui-btn-numbox-minus" type="button">-</button>
    <!--@change="countChanged"  值改变了就会触发事件countChanged -->
    <input id="test" class="mui-input-numbox" type="number" value="1" @change="countChanged" ref="numbox" />
    <button class="mui-btn mui-btn-numbox-plus" type="button">+</button>
  </div>
</template>

<script>
import mui from "../../lib/mui/js/mui.min.js";

export default {
  mounted() {
    // 初始化数字选择框组件
    mui(".mui-numbox").numbox();
    console.log(this.max);
  },
  methods: {
    countChanged() {
      // 每当 文本框的数据被修改的时候,立即把 最新的数据,通过事件调用,传递给父组件
      //在子组件中需要向父组件传值处使用this.$emit("function",param);  
       
      //其中function为父组件定义函数,param为需要传递参数
      //在父组件中子组件引用处添加函数 @:function="function1";
      ////其中function为子组件中定义函数,function1为父组件定义函数--用于接收子组件传值并进行相应数据处理,可定义为同一名称
      this.$emit("getcount", parseInt(this.$refs.numbox.value));
    }
  },
  props: ["max"],//从父组件取到的值
  watch: {
    // 属性监听
    max: function(newVal, oldVal) {
      // 使用Mui里该组件的 JS API , 设置 numbox 的最大值
      mui(".mui-numbox")
        .numbox()
        .setOption("max", newVal);
    }
  }
};
</script>

<style lang="scss" scoped>

</style>

父组件:

        <!-- 分析: 如何实现加入购物车时候,拿到 选择的数量 -->
        <!-- 1. 经过分析发现: 按钮属于 goodsinfo 页面, 数字 属于 numberbox 组件 -->
        <!-- 2. 由于涉及到了父子组件的嵌套了,所以,无法直接在 goodsinfo 页面zhong 中获取到 选中的商品数量值-->
        <!-- 3. 怎么解决这个问题:涉及到了 子组件向父组件传值了(事件调用机制) -->
        <!-- 4. 事件调用的本质: 父向子传递方法,子调用这个方法, 同时把 数据当作参数 传递给这个方法 -->
    <p>购买数量:<numbox @getcount="getSelectedCount" :max="goodsinfo.stock_quantity"></numbox></p>

    data里      selectedCount: 1 // 保存用户选中的商品数量, 默认,认为用户买1个

        getSelectedCount(count) {
      // 当子组件把 选中的数量传递给父组件的时候,把选中的值保存到 data 上
      this.selectedCount = count;
      console.log("父组件拿到的数量值为: " + this.selectedCount);
    }
发布了59 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41160739/article/details/104776602
今日推荐