欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue2.0 饿了么学习笔记(17)seller组件开发

程序员文章站 2023-12-31 15:09:40
...

vue2.0 饿了么学习笔记(17)seller组件开发

vue2.0 饿了么学习笔记(17)seller组件开发

BScroll回顾: 外层有一个容器,定义了视口的高度,内层div可以被内容自动撑高,当内层容器高度超过视口高度时,就会出现滚动

1)接收传递进来的seller数据,并编写界面和样式

 props: {
    //APP.vue的routerview中已经将seller传进来了,这里只需要接收就好
    seller: {
      type: Object
    }
  },
      <div class="overview">
        <h1 class="title">{{seller.name}}</h1>
        <div class="desc border-1px">
          <star :size="36" :score="seller.score"></star>
          <span class="text">({{seller.ratingCount}})</span>
          <span class="text">月售{{seller.sellCount}}单</span>
        </div>
        <ul class="remark">
          <li class="block">
            <h2>起送价</h2>
            <div class="content">
              <span class="stress">{{seller.minPrice}}</span>元
            </div>
          </li>
          <li class="block">
            <h2>商家配送</h2>
            <div class="content">
              <span class="stress">{{seller.deliveryPrice}}</span>元
            </div>
          </li>
          <li class="block">
            <h2>平均配送时间</h2>
            <div class="content">
              <span class="stress">{{seller.deliveryTime}}</span>元
            </div>
          </li>
        </ul>
        <div class="favorite"  @click="toggleFavorite($event)">
          <i class="icon-favorite" :class="{'active':favorite}"></i> <!-- 对应是否收藏两种样式-->
          <span>{{favoriteText}}</span> <!-- 有没有选中对应不同的文本,所以这里要绑定一个变量,放到data中 -->
        </div>
      </div>

添加响应的css

  .seller
      position absolute 
      top 174px
      bottom 0
      left 0
      width 100%
      overflow hidden
      .overview
        padding 18px
        position relative
        .title
          margin-bottom 8px
          line-height 14px
          color rgb(7, 17, 27)
          font-size 14px
        .desc
          padding-bottom 18px
          font-size 0
          border-1px(rgba(7, 17, 27, 0.1))
          .star
            display inline-block
            vertical-align top
            margin-right 8px
          .text
            display inline-block
            vertical-align top
            margin-right 12px
            line-height 18px // 不能为父元素设置line-heigth,要不然组件会被撑高
            font-size 10px
            color rgb(77, 85, 93)
        .remark
          display flex
          padding-top 18px
          .block
            flex 1
            text-align center
            border-right 1px solid rgba(7, 17, 27, 0.1)
            &:last-child
              border none
            h2
              margin-bottom 4px
              line-height 10px
              font-size 10px
              color rgb(147, 153, 149)
            .content
              line-height 24px
              font-size 10px
              color rgb(7, 17, 27)
              .stress
                font-size 24px
2)添加公告与活动部分,先添加一个split组件,在添加内容,不要忘了把图片拷贝过来

 

<div class="bulletin">
        <h1 class="title">公告与活动</h1>
        <div class="content-wrapper border-1px">
          <p class="content">{{seller.bulletin}}</p>
        </div>
        <ul v-if="seller.supports" class="supports">
            <li class="support-item border-1px" v-for="(item,index) in seller.supports" :key="(item.id,index.id)">
              <span class="icon" :class="classMap[seller.supports[index].type]"></span>
              <span class="text">{{seller.supports[index].description}}</span>
            </li>
          </ul>
      </div>
      <split></split>
 created() {
    this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee'];
  }

编写相应的样式:

        .supports
          .support-item
            padding 16px 12px
            border-1px(rgba(7, 17, 27, 0.1))
            font-size 0
            &:last-child
              border-none()
            .icon
              display inline-block
              width 16px
              height 16px
              vertical-align top
              margin-right 6px
              background-size 16px 16px
              background-repeat no-repeat
              &.decrease
                bg-image('decrease_4')
              &.discount
                bg-image('discount_4')
              &.guarantee
                bg-image('guarantee_4')
              &.invoice
                bg-image('invoice_4')
              &.special
                bg-image('special_4')
            .text
              display inline-block
              font-size 12px
              line-height 16px
              color rgb(7, 17, 27)

3)页面过长,将BScroll引入进来

 

3.1 .初始化BScroll语句放在created(),按照DOM的生命周期来说,执行到DOM的时候不能保证DOM已经被渲染了,在goods.vue中把BScroll初始化语句写在created()中,是因为前半部分先执行了获取api数据的语句,在seller中,数据是通过props传进来的,不能保证数据是否已经被渲染了

3.2 初始化BScroll语句放在ready中又不起作用,DOM被渲染完之后,ready是自动被执行的,在ready中写的代码都可以安全的去使用DOM,但是高度计算不对,无法正确滚动,原因:seller是异步获取的,但是我们的内容都是靠seller里的数据撑开的,,所以一开始内容肯定是小于我我们定义的wrapper的,所以没有被撑开

3.3 将其放入watch:{}中可以watch到seller的变化,将初始化语句写成一个方法,在watch中进行调用

watch: {
    'seller'() {
      this._initScroll();
      this._initPics();
    }
  },

下面是初始化的函数:

    _initScroll() {
      if (!this.scroll) {
        this.$nextTick(() => {
          this.scroll = new BScroll(this.$refs.seller, {click: true});
        });
      } else {
        this.scroll.refresh();
      } 
    },
    _initPics() {
      if(this.seller.pics) {
        let picWidth = 120;
        let margin = 6;
        let width = (picWidth + margin) * this.seller.pics.length - margin;
        this.$refs.picList.style.width = width + 'px'; //不要忘记加单位
        if(!this.picScroll){
          this.$nextTick(() => {
            this.picScroll = new BScroll(this.$refs.picWrapper, {
              scrollX: true, //表示横向滚动
              eventPassthrough: 'vertical'  //横向滚动图片的时候就忽略垂直方向的滚动
            });
          });
        } else {
          this.picScroll.refresh();
        } 
 
      }
    }

3.4 .每次切换商家页面时,DOM被重新渲染,又不能滚动了,因为在watch中只能观测到seller的变化,切换区块的时候seller没有变化,因为只有我们刷新界面,seller发生变化时,watch才会执行,所以单纯的切换界面seller中的watch不会被执行,所以BScroll就不会初始化。我们在ready方法中重新加入初始化语句

watch: {
    'seller'() {
      this._initScroll();
      this._initPics();
    }
  },
  ready() {
    this._initScroll();
    this._initPics();
   //设定ul的宽度
  },

之后,我们发现之前的情况是切换之后不能滚动,现在的新问题是一开始(没切换界面之前)就不能滚动了,切换之后就可以滚动了;

一定要为初始化函数_initScroll()和this._initPics()中的nextTick()下的添加if-else语句,因为ready的执行时机要先于watch中的seller,然后我们在执行seller中的initScroll的时候就会发现BScroll已经被初始化了,所以initScroll失效,即使在watch中观察到变化也只能什么都不做,所以我们将一个else选项,对BScroll进行刷新,完成

6.商家实景区块:添加图片,设置样式,横向排列并实现横向滚动,ul是外层的宽度,并不是真实的li撑开的宽度,使用BScroll实现滚动,添加—_initPic()方法,并把它添加到watch和ready中

pic-wrapper是固定宽度的视口的大小,当里面的ul超过视口宽度的时候就会出现滚动

      <div class="pics">
        <h1 class="title">商家实景</h1>
        <div class="pic-wrapper" ref="picWrapper">
          <ul class="pic-list" ref="picList">
            <li class="pic-item" v-for="pic in seller.pics" :key="pic.id">
              <img :src="pic" width="120" height="90">
            </li>
          </ul>
        </div>
      </div>
      <split></split>

_initPics中根据图片的宽度,margin和数量,计算出内层ul的宽度,并设置ul的宽度

    _initPics() {
      if (this.seller.pics) {
        let picWidth = 120;
        let margin = 6;
        let width = (picWidth + margin) * this.seller.pics.length - margin;
        this.$refs.picList.style.width = width + 'px'; //不要忘记加单位
        if (!this.picScroll) {
          this.$nextTick(() => {
            this.picScroll = new BScroll(this.$refs.picWrapper, {
              scrollX: true, //表示横向滚动
              eventPassthrough: 'vertical' //横向滚动图片的时候就忽略垂直方向的滚动
            });
          });
        } else {
          this.picScroll.refresh();
        } 
      }
    }

css样式:

      .pics
        padding 18px
        .title
          margin-bottom 12px
          line-height 14px
          color rgb(7, 17, 27)
          font-size 14px
        .pic-wrapper
          width 100%
          overflow hidden
          white-space nowrap /*不产生折行*/
          .pic-list
            font-size 0
            .pic-item
              display inline-block
              margin-right 6px
              width 120px
              height 90px
              &:last-child
                margin 0

添加商家信息

    <div class="info">
        <div class="title  border-1px">商家信息</div>
        <ul>
          <li class="info-item" v-for="info in seller.infos" :key="info.id">{{info}}</li>
        </ul>
      </div>
    </div>

 

7.添加收藏按钮,设置:active样式(红,白)和字体的变化(收藏和未收藏)

        <div class="favorite"  @click="toggleFavorite($event)">
          <i class="icon-favorite" :class="{'active':favorite}"></i> <!-- 对应是否收藏两种样式-->
          <span>{{favoriteText}}</span> <!-- 有没有选中对应不同的文本,所以这里要绑定一个变量,放到data中 -->
        </div>

favorite是一个变量,我们要在data里面观测它

data() {
    return {
     // favorite: false, //默认没有被收藏,从localStorge中取读取,不是一个默认值了
      favorite: (() => {
       return loadFromlLocal(this.seller.id, 'favorite', false);
     })()
    };
  },
computed: {
    favoriteText() {
       return this.favorite ? '已收藏' : '收藏'; 
    }
  },

添加css样式:

        .favorite
          position absolute
          right 11px
          top 18px
          width 50px /* 设定宽度改变样式的时候就不会发生偏移*/
          text-align center
          .icon-favorite
            display block
            margin-bottom 4px
            line-height 24px
            font-size 24px
            width 50px
            color #d4d6d9
            &.active
              color rgb(240,20,20)
          .text
            line-height 10px
            font-size 10px
            color rgb(77,85,93) 

methods中添加逻辑点击事件:

 toggleFavorite(event) {
        if (!event._constructed) {
          return;
        }
        this.favorite = !this.favorite;
        //这样写取法区分商家id,不同商家的状态一样
        //localStorage.favorite = this.favorite;
        saveToLocal(this.seller.id, 'favorite', this.favorite);
      },

每一个商家都有一个唯一的id,这个id存在url中,所以封装一个函数,将url解析成对象的模式

/**
 * 解析url参数
 * Created by yi on 2016-12-28.
 * @return Object {id:12334}
 */
export function urlParse() {
  let url = window.location.search;
  let obj = {};
  let reg = /[?&][^?&]+=[^?&]]+/g;
  let arr = url.match(reg);
  // ['?id=123454','&a=b']

  if (arr) {
    arr.forEach((item) => {
      let tempArr = item.substring(1).split('=');// 先分割取到id=123454,之后用=号分开
      let key = tempArr[0];
      let val = tempArr[1];
      obj[key] = val;
    });
  }
  // return obj;
  return {id: 123123};
};

之后,在App.vue组件中引入urlParse,并在data中获取data

 

 

刷新之后,收藏样式就会消失,将收藏的信息添加到localStorge中,创建store.js实现数据的存取,专门存取不同商家的id

//savaToLocal(this.seller.id, 'favorite', this.favorite);存取
export function saveToLocal(id, key, value) { //存储到localStorge
  let seller = window.localStorage.__seller__;
  if (!seller) { //没有seller的时候,初始化,定义一个seller对象,并给他设定一个id
    seller = {};
    seller[id] = {}; // 每个id下都是一个单独的obj
  } else {
    seller = JSON.parse(seller); // JSON 字符串转换为对象
    if (!seller[id]) { //判断是否有当前这个商家
      seller[id] = {};
    }
  }
  seller[id][key] = value; // 将key和value存到id这个对象的下边
  //将一个JavaScript值(对象或者数组)转换为一个 JSON字符串
  window.localStorage.__seller__ = JSON.stringify(seller);
}
//loadFromlLocal(this.seller.id, 'favorite', false);读取
export function loadFromlLocal(id, key, def) { //读取,读不到的时候传入一个default变量
  let seller = window.localStorage.__seller__;
  if (!seller) {
    return def;
  }
  seller = JSON.parse(seller)[id]; // 取到这个商家下所有的对象
  if (!seller) {
    return def;
  }
  let ret = seller[key];
  return ret || def;
}

引入:

import {saveToLocal, loadFromlLocal} from 'common/js/store.js';

在toggleFavorite中使用这两个方法

 toggleFavorite(event) {
        if (!event._constructed) {
          return;
        }
        this.favorite = !this.favorite;
        //这样写取法区分商家id,不同商家的状态一样
        //localStorage.favorite = this.favorite;
        saveToLocal(this.seller.id, 'favorite', this.favorite);
      },

 

data() {
    return {
     // favorite: false, //默认没有被收藏,从localStorge中取读取,不是一个默认值了
      favorite: (() => {
       return loadFromlLocal(this.seller.id, 'favorite', false);
     })()
    };
  }

 

 

 

 

 

 

 

 

 

相关标签: vue2.0饿了么

上一篇:

下一篇: