商家頁面seller.vue

主體結構

html代碼

<div class="seller" ref="seller">
    <div class="seller-content">
      <!--overview部分-->
      <div class="overview">

      </div>
      <split></split>
      <!--bulltin部分-->
      <div class="bulletin">
        
      </div>
      <split></split>
      <!--pics部分-->
      <div class="pics">
        
      </div>
      <split></split>
      <!--info部分-->
      <div class="info">
      
      </div>
    </div>
  </div>
  • seller-contentdiv是可以擴大高度的,可以被內容增長而撐高,而sellerdiv是固定的高度的,從而造成滾動css

js代碼

import BScroll from 'better-scroll';
  import split from '../split/split';

  export default {
    props: {
      seller: { //app.vue裏面router-view會傳入一個seller
        type: Object
      }
    },
    watch: { //當seller變更的時候會觸發
      'seller'() {
        this.$nextTick(() => {
          this._initScroll();
          this._initPics();
        });
      }
    },
    mounted() { //在dom被渲染以後觸發
      this.$nextTick(() => {
        this._initScroll();
        this._initPics();
      });
    },
    methods: {
      _initScroll() {
        if (!this.scroll) {
          this.scroll = new BScroll(this.$refs.seller, {
            click: true
          });
        } else {
          this.scroll.refresh();
        }
      },
      _initPics() {
        //詳細代碼在下面,這裏暫時先忽略
      }
    },
    components: {
      split
    }
  };
  • 使用watch方法是爲了監聽數據seller的變化,由於當打開頁面的時候,seller是異步獲取的,並不必定是可以立刻獲取,沒有seller的數據,相關dom就沒法被渲染,而且bscroll是基於dom執行的,因此須要監聽seller的變化而後來從新執行相關的初始化函數html

  • 使用mounted方法是爲了保證相關dom渲染完成,由於bscroll是基於dom執行的,可是當切換頁面的時候,dom會從新渲染,但未必可以立刻完成,因此須要在mounted方法裏面從新執行相關的初始化函數vue

  • 關於watch和mounted的使用要基於vue的生命週期來理解vue生命週期html5

css代碼

.seller
    position: absolute
    top: 174px //給header留空間
    bottom: 0
    left: 0
    width: 100%
    overflow: hidden //隱藏多餘部分,由於須要滾動

overview部分

html部分

<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>
        <!--列表處理remark內容-->
        <ul class="remark">
        <!--使用一個block塊包裹內容-->
          <li class="block">
          <!--標題用h2標籤-->
            <h2>起送價</h2>
            <!--用content塊包裹內容-->
            <div class="content">
            <!--用span表明特殊字體-->
              <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">
          <span class="icon-favorite" :class="{'active':favorite}"></span>
          <span class="text">{{favoriteText}}</span>
        </div>
      </div>

js部分

import BScroll from 'better-scroll';
  //引入一個利用html5的localstorage的存儲模塊
  import { saveToLocal, loadFromLocal } from 'common/js/store';

  export default {
    props: {
      seller: {
        type: Object
      }
    },
    data() {
      return {
        favorite: (() => { 
        //利用localstorage讀取這個屬性
          return loadFromLocal(this.seller.id, 'favorite', false);
        })()//vue須要返回的data必須是函數,因此加上()
      };
    },
    computed: {
      favoriteText() { //經過favorite的值來計算favoriteText的值
        return this.favorite ? '已收藏' : '收藏';
      }
    },
    methods: {
      toggleFavorite(event) {
        if (!event._constructed) {
          return;
        }
        //經過取反來設置切換
        this.favorite = !this.favorite;
        //利用localstorage存儲這個屬性
        saveToLocal(this.seller.id, 'favorite', this.favorite);
      }
    }
  };

css部分

.overview
      position: relative //設置相對佈局參考位置
      padding: 18px
      .title
        margin-bottom: 8px
        line-height: 14px
        color: rgb(7, 17, 27)
        font-size: 14px
      .desc
        padding-bottom: 18px
        border-1px(rgba(7, 17, 27, 0.1))
        font-size: 0
        .star
          display: inline-block
          margin-right: 8px
          vertical-align: top //行內對齊
        .text
          display: inline-block
          margin-right: 12px
          line-height: 18px
          vertical-align: top //行內對齊
          font-size: 10px
          color: rgb(77, 85, 93)
      .remark
        display: flex //flex佈局
        padding-top: 18px
        .block
          flex: 1 //flex佈局等分區域
          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, 159)
          .content //設置該塊的公共屬性
            line-height: 24px
            font-size: 10px
            color: rgb(7, 17, 27)
            .stress //特殊大小再插入class覆蓋
              font-size: 24px
      .favorite
        position: absolute
        width: 50px //設置一個固定寬度,被text-align使用
        right: 11px
        top: 18px
        text-align: center
        .icon-favorite
          display: block
          margin-bottom: 4px
          line-height: 24px
          font-size: 24px
          color: #d4d6d9
          &.active
            color: rgb(240, 20, 20)
        .text
          line-height: 10px
          font-size: 10px
          color: rgb(77, 85, 93)

bulletin部分

html代碼

<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">
          <!--使用classMap作對應-->
            <span class="icon" :class="classMap[seller.supports[index].type]"></span>
            <span class="text">{{seller.supports[index].description}}</span>
          </li>
        </ul>
      </div>

classMap的原理(classMap使用在以前的header組件使用過Header.vue)segmentfault

  • seller.supports的結構是這樣的數組

"supports": [
      {
        "type": 0,
        "description": "在線支付滿28減5"
      },
      {
        "type": 1,
        "description": "VC無限橙果汁全場8折"
      },
      {
        "type": 2,
        "description": "單人精彩套餐"
      },
      {
        "type": 3,
        "description": "該商家支持發票,請下單寫好發票擡頭"
      },
      {
        "type": 4,
        "description": "已加入「外賣保」計劃,食品安全保障"
      }
    ],
  • classMap是['decrease', 'discount', 'special', 'invoice', 'guarantee']這樣的,這個順序是按照設計須要和數據結構排的,例如type爲0對應在線支付滿28減5安全

  • 若是要獲取對應的class,就能夠classMap[seller.supports[index].type],例如seller.supports[0].type就是0,那麼就是classMap[0]就是decrease這個class數據結構

  • 若是要獲取對應的description就能夠seller.supports[index].description,例如seller.supports[0].description就直接知道了supports數組的第一個元素的description屬性的值了app

js代碼

import split from '../split/split';

  export default {
    props: {
      seller: {
        type: Object
      }
    },
    created() { //使用自建的一個classMap數組來對應不一樣的類別
      this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee'];
    },
    components: {
      split
    }
  };
  • 使用created方法是爲了在加載的時候第一時間完成,由於vue的渲染須要this.classMap屬性(created是vue的生命週期的第一個)

css代碼

@import "../../common/stylus/mixin.styl"
    .bulletin
      padding: 18px 18px 0 18px
      .title
        margin-bottom: 8px
        line-height: 14px
        color: rgb(7, 17, 27)
        font-size: 14px
      .content-wrapper
        padding: 0 12px 16px 12px
        border-1px(rgba(7, 17, 27, 0.1))
        .content
          line-height: 24px
          font-size: 12px
          color: rgb(240, 20, 20)
      .supports
        .support-item
          padding: 16px 12px
          border-1px(rgba(7, 17, 27, 0.1))
          font-size: 0
          &:last-child
            border-none() //須要引入一個mixin,處理沒邊框的問題
        .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') //使用mixin處理這些圖標
          &.discount
            bg-image('discount_4')
          &.guarantee
            bg-image('guarantee_4')
          &.invoice
            bg-image('invoice_4')
          &.special
            bg-image('special_4')
        .text
          line-height: 16px
          font-size: 12px
          color: rgb(7, 17, 27)

pics部分

html代碼

<div class="pics">
        <h1 class="title">商家實景</h1>
        <!--兩個ref綁定picWrapper,picList-->
        <div class="pic-wrapper" ref="picWrapper">
        <!--列表循環輸出圖片-->
          <ul class="pic-list" ref="picList">
            <li class="pic-item" v-for="pic in seller.pics">
            <!--固定圖片尺寸-->
              <img :src="pic" width="120" height="90">
            </li>
          </ul>
        </div>
</div>
  • ref獲取picWrapper是爲了滾動圖片須要,父DOM是picWrapper,子DOM是picList

  • ref獲取picList是爲了計算圖片列表的總長度,用來設置圖片列表的橫向滾動,由於滾動的觸發條件是子DOM比父DOM要"大"的時候纔會觸發,因爲圖片的數量是未知的,因此須要去計算,而後寫入到picList

js代碼

_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';
          //異步綁定滾動
          this.$nextTick(() => {
            if (!this.picScroll) {
              this.picScroll = new BScroll(this.$refs.picWrapper, {
                scrollX: true, //bscroll的橫向滾動屬性
                eventPassthrough: 'vertical' //bscroll的過濾垂直滾動
              });
            } else {
              this.picScroll.refresh();
            }
          });
        }
  • 通常來講,當某頁面自己是垂直滾動的時候,中間的某個DOM須要作橫向滾動的話,須要處理滾動座標的變化,例如手勢滾動的時候有x,y座標,是向上,下,左仍是右會區分

  • 圖片列表的總長度是單個圖片的寬度+單個圖片的外邊距的和乘以圖片的數量,再減去最後一個圖片的外邊距

  • BScroll插件須要增長一個屬性scrollX: true來傳入橫向的滾動座標,也須要增長一個屬性eventPassthrough: 'vertical'過濾垂直的滾動,前者已經解釋過,後者是由於須要橫向滾動的頁面是內嵌在垂直滾動的頁面的裏面的,因此會形成外面的垂直滾動事件也會傳入到橫向滾動的頁面,爲了不影響橫向滾動,因此須要屏蔽垂直滾動的事件

  • 咱們平時在作這種滾動內嵌滾動的時候也能夠參考這個處理方法.

  • 另外異步綁定滾動的常規作法,先判斷是否已經有BScroll實例了,而後再肯定是否須要刷新

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

info部分

html代碼

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

css代碼

.info
      padding: 18px 18px 0 18px
      color: rgb(7, 17, 27)
      .title
        padding-bottom: 12px
        line-height: 14px
        border-1px(rgba(7, 17, 27, 0.1))
        font-size: 14px
      .info-item
        padding: 16px 12px
        line-height: 16px
        border-1px(rgba(7, 17, 27, 0.1)) //1像素的border 的mixin
        font-size: 12px
        &:last-child
          border-none() //沒有border的mixin
相關文章
相關標籤/搜索