項目總結:vue.js2.5餓了麼APP(5)主要組件實現 - 商品詳情頁部分

說明:本總結來源於慕課網 @ustbhuangyi老師的課程《Vue.js2.5+cube-ui重構餓了麼App》課程,本博客做了項目總結梳理便於回顧,需要學習的夥伴可以移步學習。與君共勉!

之前章節傳送: 

項目總結:vue.js2.5餓了麼APP(1)概述+項目準備 

項目總結:vue.js2.5餓了麼APP(2)主要組件實現 - 頭部相關組件

項目總結:vue.js2.5餓了麼APP(3)主要組件實現 - 購物車相關組件(上)

項目總結:vue.js2.5餓了麼APP(4)主要組件實現 - 購物車相關組件(下)


速看

在商品頁面中點擊某一個商品,可以跳到商品詳情頁查看詳情,包括:商品圖片、名稱價格、商品評價、加入購物車。該組件爲全屏彈層組件,如果單純作爲goods的子組件調用會出現不能全屏顯示的問題。因此將其註冊爲create-API模塊,讓good組件動態掛載到body下。(這樣就可以使用fixed全屏佈局以及層級的z-index來控制是否蓋住下面的header部分。)(原因:food是fixed佈局,外部有一個transform,fixed佈局會受到transform影響,因此對於全屏類彈層組件,組件的樣式很容易受到父元素影響,(fixed佈局是全屏類的))同樣的,該頁面也需要控制shop-cart-sticky購物車組件顯隱。

評論部分有兩個維度實現過濾:首先可以查看評價類型(全部、推薦、吐槽)還有“只看有內容的評價”,將其該部分設置爲rating-select組件,接收props(ratings是個數組(通過ratings可以計算各部分的評價個數);selectType是選擇類型(有三種類型,定義三種常量POSITIVE、NEGATIVE、ALL)使用常量增加可讀性;onlycontent是否有內容;desc描述(對象默認描述:all:"全部" positive:“滿意” negative:“不滿意”) )外部可以傳入標籤,從而實現複用。在food中添加計算屬性computedRatings,定義數組並遍歷將符合的ratings放入(當選中所有時push(rating)或者當前選擇類型和這條評價類型相同,也push(rating))從而按情況顯示ratings


目錄

一、food組件-商品部分

1. 概述

2. 布居

3. 實現

(1)添加頭圖

(2)使用聲明式調用food(未採用)

(3)註冊api組件

(4)實現food 組件內部滾動

(5)使用sticky組件

(6)在商品詳情頁進行購物

二、food組件-評價部分

1. 佈局

2. 實現

(1)顯示整個評論列表

(2)時間部分

(3)評價過濾功能

三、food組件-rating-select組件

1. 佈局

2. 實現

(1)rating-select組件接收數據

(2)rating-select組件使用

(3)添加點擊事件

(4)控制列表顯示變化


一、food組件-商品部分

1. 概述

在商品頁面中點擊某一個商品,可以跳到商品詳情頁查看詳情,包括:商品圖片、名稱價格、商品評價、加入購物車。該組件爲全屏彈層組件,如果單純作爲goods的子組件調用會出現不能全屏顯示的問題。因此將其註冊爲create-API模塊,讓good組件動態掛載到body下。(這樣就可以使用fixed全屏佈局以及層級的z-index來控制是否蓋住下面的header部分。)(原因:food是fixed佈局,外部有一個transform,fixed佈局會受到transform影響,因此對於全屏類彈層組件,組件的樣式很容易受到父元素影響,(fixed佈局是全屏類的))同樣的,該頁面也需要控制shop-cart-sticky購物車組件顯隱。

評論部分有兩個維度實現過濾:首先可以查看評價類型(全部、推薦、吐槽)還有“只看有內容的評價”,將其該部分設置爲rating-select組件,接收props(ratings是個數組(通過ratings可以計算各部分的評價個數);selectType是選擇類型(有三種類型,定義三種常量POSITIVE、NEGATIVE、ALL)使用常量增加可讀性;onlycontent是否有內容;desc描述(對象默認描述:all:"全部" positive:“滿意” negative:“不滿意”) )外部可以傳入標籤,從而實現複用。在food中添加計算屬性computedRatings,定義數組並遍歷將符合的ratings放入(當選中所有時push(rating)或者當前選擇類型和這條評價類型相同,也push(rating))從而按情況顯示ratings.

(在rating-select中的每個選項添加點擊事件select,toggle 。由於selectType是傳遞過來的props不能直接修改,因此把這個props的值派發到父組件food,讓後讓父組件做修改,響應式更新數據,通過props再影響子組件。)

2. 布居

當點擊某一商品時點擊可以查看詳情,主要分爲:頭部圖片+商品購物+商品信息+評價

商品評價可以有選項分類,有全部、推薦、吐槽,並且有一個按鈕可以只看有內容的評價

二期:goods組件需要基於API實現

    <transition name="move" @after-leave="afterLeave">
        <div class="food" v-show="visible">
            <cube-scroll :data="computedRatings" ref="scroll">
                <div class="food-content">
                    <div class="image-header"></div>
                    <div class="content">
                        <div class="cart-control-wrapper">
                            <cart-control @add="addFood" :food="food"></cart-control>
                        </div>
                        <transition name="fade"><div class="buy">加入購物車</div>     </transition>
                    </div>
                    <split v-show="food.info"></split>
                    <div class="info" v-show="food.info"></div>
                    <split></split>
                    <div class="rating">
                        <rating-select></rating-select>
                        <div class="rating-wrapper"></div>
                    </div>
                </div>
            </cube-scroll>
        </div>
    </transition>

需要接收food這個props,需要根據不同的food渲染不同內容,開始是隱藏的,通過通過使用visible控制顯隱,其中的<transition>對應左滑的過渡動畫,內部使用cube-scroll組件實現滾動.

3. 實現

(1)添加頭圖

使用height:0 padding-top:100%的方式,讓圖片可以達到等比效果

(2)使用聲明式調用food(未採用)

即在goods組件中引入food組件。使用food組加需要傳入:food="selectedFood",在goods組件中,當選中列表的一項時,添加點擊事件selectFood(food),並實現方法(將food賦值給selectedFood)。在data中聲明一個當前選中的商品selectedFood.並且給添加一個ref="food"

selectFood(food){
	this.selectedFood = food
	this.$refs.food.show()
}

發現效果是在下面區域顯示整個food組件而沒有遮蓋頭部部分。

原因:food是fixed佈局,外部有一個transform,fixed佈局會受到transform影響,因此對於全屏類彈層組件,組件的樣式很容易受到父元素影響,(fixed佈局是全屏類的)

解決方法:使用create-API模塊,讓good組件動態掛載到body下。這樣就可以使用fixed全屏佈局以及層級的z-index來控制是否蓋住下面的header部分。

(3)註冊api組件

定義私有方法_showFood(),當選擇的food變化了,selectedFood響應式變化

selectFood(food) {
            this.selectedFood = food
            this._showFood()
            this._showShopCartSticky()
        },
_showFood() {
            this.foodComp = this.foodComp || this.$createFood({
                $props: {
                    food: 'selectedFood'
                },
                $events: {
                    leave: () => {
                        this._hideShopCartList()
                    },
                    add: (el) => {
                        this.shopCartStickyComp.drop(el)
                    }
                }
            })
            this.foodComp.show()
        },

(4)實現food 組件內部滾動

scroll每次需要初始化,在food組件中添加created

const EVENT_SHOW = 'show'
created() {
        this.$on(EVENT_SHOW, () => {
            this.$nextTick(() => {
                this.$refs.scroll.refresh()
            })
        })
    },

(5)使用sticky組件

在goods組件中,打開food組件時,同時要將shop-cart-sticky組件顯示出來。同時定義showSopCartSticky()方法

selectFood(food) {
            this.selectedFood = food
            this._showFood()
            this._showShopCartSticky()
        },
_showShopCartSticky() {
            this.shopCartStickyComp = this.shopCartStickyComp || this.$createShopCartSticky({
                $props: {
                    selectFoods: 'selectFoods',
                    deliveryPrice: this.seller.deliveryPrice,
                    minPrice: this.seller.minPrice,
                    fold: true
                }
            })
            this.shopCartStickyComp.show()
        },
_hideShopCartSticky() {
            this.shopCartStickyComp.hide()
        }

當動畫結束時隱藏sticky組件,在food組件添加過渡鉤子函數after-leave=afterLeave

afterLeave() {
            this.$emit(EVENT_LEAVE)  //leave
        },

並且在goods組件中的——showFood中添加event(hideShopCartSticky)

_showFood() {
            this.foodComp = this.foodComp || this.$createFood({
                $props: {
                    food: 'selectedFood'
                },
                $events: {
                    leave: () => {
                        this._hideShopCartSticky()
                    },
                    add: (el) => {
                        this.shopCartStickyComp.drop(el)
                    }
                }
            })
            this.foodComp.show()
        },
        

(6)在商品詳情頁進行購物

比之前的cart-control多一步,在沒有購買時會顯示“加入購物車”,

實現:使用兩層,一層是使用cat-control組件,另一層是一個按鈕,並且使用<tansition>組件包裹,驅動動畫的變化,通過food.count控制顯隱,當count是0或者undefined,就是顯示的,否則相反。都是用絕對定位,讓“加入購物車”部分蓋住cart-control

<div class="cart-control-wrapper">
          <cart-control @add="addFood" :food="food"></cart-control>
</div>
<transition name="fade">
          <div @click="addFirst" class="buy" v-show="!food.count">加入購物車</div>
</transition>

並且在“加入購物車”添加addFirst方法,(沒有count時設置爲1)點擊按鈕驅動外層的shop-cart-sticky組件,因此需要派發EVENT_ADD;在cart-control添加addFood方法(事件向父組件傳遞,food組件的父組件是goods,裏面含有shopCartStickyComp,它的drop()方法纔是真正調用小球動畫的,因此事件需要一層層派發)

addFirst(event) {
            this.$set(this.food, 'count', 1)
            this.$emit(EVENT_ADD, event.target)
        },
addFood(target) {
            this.$emit(EVENT_ADD, target)
        },

這是在goods中監聽add方法完成動畫

_showFood() {
            this.foodComp = this.foodComp || this.$createFood({
                $props: {
                    food: 'selectedFood'
                },
                $events: {
                    leave: () => {
                        this._hideShopCartSticky()
                    },
                    add: (el) => {
                        this.shopCartStickyComp.drop(el)
                    }
                }
            })
            this.foodComp.show()
        },

二、food組件-評價部分

1. 佈局

除了有對評價的顯示,還有對評價的過濾,可以對評價列表進行切換。

<div class="rating">
       <h1 class="title">商品評價</h1>
       <rating-select></rating-select>
       <div class="rating-wrapper">
              <ul>
                    <li class="rating-item border-bottom-1px">
                        <div class="user">
                            <span class="name">{{rating.username}}</span>
                            <img class="avatar" :src="rating.avatar">
                        </div>
                        <div class="time">{{format(rating.rateTime)}}</div>
                       <p class="text"></p>
                   </li>
             </ul>
            <div class="no-rating" v-show="!computedRatings || !computedRatings.length">暫無評價</div>
      </div>
 </div>

2. 實現

(1)顯示整個評論列表

ratings使用計算屬性(簡化書寫)

<div class="rating-wrapper">
        <ul v-show="computedRatings && computedRatings.length">
             <li
                 v-for="(rating, index) in computedRatings"
                 class="rating-item border-bottom-1px"
                 :key="index">
                 <div class="user"><div>
                 <div class="time">{{format(rating.rateTime)}}</div>
                 <p class="text"> </p>
            </li>
       </ul>
</div>

computed: {
        ratings() {
            return this.food.ratings
        }
    },

遍歷整個列表,渲染出所有的ratings,包含時間,評價,用戶,手指圖標

(2)時間部分

通常日期的顯示服務端不會顯示時間,而是時間戳。而前端可以按照自己的要求改變爲字符串。這裏使用了moment的開源組件(Moment.js)可以添加format()方法,使用moment的方法即可

format(time) {
            return moment(time).format('YYYY-MM-DD hh:mm')
        }

至此完成了評價列表

(3)評價過濾功能

有兩個維度實現過濾:首先可以查看評價類型(全部、推薦、吐槽)過濾評論的同時模塊激活狀態顏色改變;還有“只看有內容的評價”,將其設置爲rating-select組件,在rating-select中的每個選項添加點擊事件select,toggle,由於不能直接修改selectType,(傳遞過來的props)因此把這個props的值派發到父組件food,讓後讓父組件做修改,響應式更新數據再影響子組件。

因此在food組件中可以監聽這兩個方法onselect() ontoggle()控制改變。

三、food組件-rating-select組件

1. 佈局

主要是(全部、推薦、吐糟)+只看有內容的評論

<div class="rating-select">
        <div class="rating-type border-bottom-1px">
            <span @click="select(2)" class="block positive" :class="{'active': selectType===2}">{{desc.all}}
                <span class="count">{{ratings.length}}</span>
            </span>
            <span @click="select(0)" class="block positive" :class="{'active': selectType===0}">{{desc.positive}}
                <span class="count">{{positives.length}}</span>
            </span>
            <span @click="select(1)" class="block positive" :class="{'active': selectType===1}">{{desc.negative}}
                <span class="count">{{negatives.length}}</span>
            </span>
        </div>
        <div @click="toggle" class="switch" :class="{'on':onlyContent}">
            <span class="icon-check_circle"></span>
            <span class="text">只看有內容的評價</span>
        </div>
    </div>

2. 實現

(1)rating-select組件接收數據

設置props,外部可以傳入的變量。

接收ratings是個數組(通過ratings可以計算各部分的評價個數);selectType是選擇類型(有三種類型,定義三種常量POSITIVE、NEGATIVE、ALL)使用常量增加可讀性;onlycontent是否有內容;desc描述(對象默認描述:all:"全部" positive:“滿意” negative:“不滿意”)

const POSITIVE = 0
const NEGATIVE = 1
const ALL = 2

props: {
        ratings: {
            type: Array,
            default() {
                return []
            }
        },
        selectType: {
            type: Number,
            default: ALL
        },
        onlyContent: {
            type: Boolean,
            default: false
        },
        desc: {
            type: Object,
            default() {
                return {
                    all: '全部',
                    positive: '滿意',
                    negative: '不滿意'
                }
            }
        }
    },

添加計算屬性positive negative,根據服務端約定數據得到,計算每個部分的數量

computed: {
        positives() {
            return this.ratings.filter((rating) => {
                return rating.rateType === POSITIVE
            })
        },
        negatives() {
            return this.ratings.filter((rating) => {
                return rating.rateType === NEGATIVE
            })
        }
    },

(2)rating-select組件使用

在food組件寫入date,包括onlyContent,selectType,desc(“全部、推薦、吐槽”)

data() {
        return {
            onlyContent: true,
            selectType: ALL,
            desc: {
                all: '全部',
                positive: '推薦',
                negative: '吐槽'
            }
        }
    },

然後在rating-select中綁定數據

<rating-select
                            :ratings="ratings"
                            :onlyContent="onlyContent"
                            :selectType="selectType"
                            :desc="desc"
                            @select="onSelect"
                            @toggle="onToggle">
 </rating-select>

(3)添加點擊事件

在rating-select中的每個選項添加點擊事件(如上代碼),並寫方法select,toggle

const EVENT_SELECT = 'select'
const EVENT_TOGGLE = 'toggle'
methods: {
        select(type) {
            this.$emit(EVENT_SELECT, type)
        },
        toggle() {
            this.$emit(EVENT_TOGGLE)
        }
    }

思路:這時不能直接修改selectType,不能修改傳遞過來的props,因此把這個props的值派發到父組件food,讓後讓父組件做修改,響應式更新數據,這個數據通過props再影響子組件。

因此在food組件中可以監聽這兩個方法onselect() ontoggle()控制改變。

methods: {
        onSelect(type) {
            this.selectType = type
        },
        onToggle() {
            this.onlyContent = !this.onlyContent
        }
    }

(在這個地方使用v-model不是很合適)

至此只是完成了交互效果,所有交互的效果應該影響列表,根據onlycontent 和 select計算而來

(4)控制列表顯示變化

在food中添加計算屬性computedRatings,定義數組遍歷,

解釋:當選中所有時push(rating)或者當前選擇類型和這條評價類型相同,也push(rating)最後返回數組

computed: {
        computedRatings() {
            const ret = []
            this.ratings.forEach((rating) => {
                if (this.onlyContent && !rating.text) {
                    return
                }
                if (this.selectType === ALL || this.selectType === rating.rateType) {
                    ret.push(rating)
                }
            })
            return ret
        }
    },

使用這個計算屬性, 在每一個li中遍歷computedRatings,並且當沒有評價時顯示爲“暫無評價”

<ul v-show="computedRatings && computedRatings.length">
          <li
            v-for="(rating, index) in computedRatings"
             class="rating-item border-bottom-1px"
            :key="index">
         </li>
</ul>

後續章節傳送:

項目總結:vue.js2.5餓了麼APP(6)主要組件實現 - 評價頁+商家頁部分

項目總結:vue.js2.5餓了麼APP(7)項目部署與總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章