說明:本總結來源於慕課網 @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. 概述
在商品頁面中點擊某一個商品,可以跳到商品詳情頁查看詳情,包括:商品圖片、名稱價格、商品評價、加入購物車。該組件爲全屏彈層組件,如果單純作爲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>
後續章節傳送: