<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-content
div是可以擴大高度的,可以被內容增長而撐高,而seller
div是固定的高度的,從而造成滾動css
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
.seller position: absolute top: 174px //給header留空間 bottom: 0 left: 0 width: 100% overflow: hidden //隱藏多餘部分,由於須要滾動
<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>
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); } } };
store.js參考:split,formatDate,store,util組件編程
.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)
<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
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的生命週期的第一個)
@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)
<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
_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實例了,而後再肯定是否須要刷新
.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"> <h1 class="title border-1px">商家信息</h1> <ul> <li class="info-item" v-for="info in seller.infos">{{info}}</li> </ul> </div>
.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