天天看點

Vue ( 五 ) food 元件開發

一、父元件調用子元件的方法

思路:使用者點選商品時商品詳情頁出現,是以需要 父元件中點選商品時調用子元件一個方法來控制 商品詳情頁的 顯示 隐藏。

food元件
<template>
	<div class="food" v-show="foodtoShow">		
	</div>
</template>

<script>
	export default{
		props: {
	      selcurfood: { //  使用者選擇的商品
	        type: Object
	      }
	   },
	   data(){
	   	return {
	   		foodtoShow :false //  定義商品詳情頁的初始狀态   開始 隐藏
	   	 }
	   },
	   methods:{
	   	  foodinforshow(){   //  待父元件 觸發了點選事件,調用 foodinforshow 方法,顯示商品詳情頁
	   	  	this.foodtoShow = true
	   	  }
	   }
		
	}
</script>           
父元件goods元件
此處省略 引入、注冊...

1.綁定事件

<li @click='selectFood(food,$event)' v-for="food in item.foods" class="food-item">...           

2.應用元件

<food :selcurfood='selectedfoods' ref="food"></food>           

selectedfoods 代表使用者目前點選的商品

3.建立一個接受使用者選擇商品的對象

data (){
 	return {
 		goods: [],//  goods json  數組
        listHeight: [],// 存放 foods 内部的每一塊的高度
        scrollY:0,
        selectedfoods:{} // 接收使用者點選的商品
 	}
 },           

4.點選事件

selectFood(food,event){
    	if (!event._constructed) {// 阻止浏覽器的原生 click 事件
	        return;
	      }
    	this.selectedfoods=food// 傳入使用者選擇的商品,存在selectedfoods 對象裡
    	this.$refs.food.foodinforshow() //利用 ref 屬性 來調用 子元件的方法
    }
           
到這裡 就和商品詳情頁相關聯了!繼續...

二、實作詳情頁内容的滾動

1.實作滾動需要better-scroll,是以需要dom綁定,異步加載bscroll
<template>
  <transition name="move">
  <!-- v-show 控制顯示詳情頁 ref 實作dom 綁定-->
  <div class="food" v-show="foodtoShow" ref='foodDetail'>
  </div>
  </transition>
</template>
  import BScroll from 'better-scroll';
  export default {
    props: {
	      selcurfood: { //  使用者選擇的商品
	        type: Object
	      }
	   },
    data(){
      return {
        foodtoShow :false, //  定義商品詳情頁的初始狀态   開始 隐藏
      }
    },
    methods: {
      show(){
        this.showFlag = true; 
        this.$nextTick( () => { //  接口   保證dom 渲染完畢    異步添加滾動綁定
   	  		if(!this.scroll){
   	  			this.scroll=new BScroll(this.$refs.foodDetail,{
   	  				click:true
   	  			})
   	  		}else{
   	  			this.scroll.refresh()
   	  		}
   	  	})
      },
      foodtoHide(){ // 點選傳回 圖示   商品詳情消失
   	  	this.foodtoShow = false
   	  },
  }           

注意:better-scroll 作用在 ref 下的 所有内容。是以要有foodcontent 來 包裹内容,否則better-scroll 不起作用,附上部分代碼。

<div class="food" v-show="foodtoShow" ref='foodDetail'>
	<div class="foodcontent">
		<div class="image-header">
		    <img :src="selcurfood.image" alt="" />
			<div class="back" @click="foodtoHide">
				<i class="icon-arrow_lift"></i>
			</div>
	    </div>	
		<div class="content">
			<h1 class="title">{{selcurfood.name}}</h1>
			<div class="detail">
			  <span class="sell-count">月售{{selcurfood.sellCount}}</span>	
			  <span class="rating">好評率{{selcurfood.rating}}</span>
			</div>
			<div class="price">
			    <span class="newPrice">¥{{selcurfood.price}}</span>
			    <span  class="oldPrice"v-show='selcurfood.oldPrice'>¥{{selcurfood.oldPrice}}</span>
			</div>
			<!--引入cartcontrol元件,并且用一個div包裹他-->
			<div class="cartcontrol-wrapper">
        	   <cart-control :foodsele='selcurfood' @add="addFood"></cart-control>
            </div>
            <transition name='fade'>
            	<!--使用.stop.prevent阻止冒泡和預設事件,避免穿透-->
            	<div class="buy" v-show="!selcurfood.count || selcurfood.count===0" @click.stop.prevent="ballshow">
            	加入購物車
               </div>
            </transition>
		</div>	
		<split-line v-show="selcurfood.info"></split-line>
	  	<div class="info" v-show="selcurfood.info">
			<h1 class="title">商品資訊</h1>
			<p class="text">{{selcurfood.info}}</p>
		</div>
		<split-line ></split-line>
		<div class="rating">
			<h1 class="title">商品評價</h1>
			<ratingselect 
				:selectType="selectType"
				:onlyContent="onlyContent" :desc="desc"
                :ratings="selcurfood.ratings" >
				
			</ratingselect>
		</div>
	</div>
</div>           

三、購物車按鈕問題

問題:點選加入購物車,出現小球,做抛物線動畫的位置從螢幕上方運動,而不是從點選購物車處開始做運動
分析:當點選加入購物車的按鈕時候(ballshow),food.count被添加了資料Vue.set(this.selcurfood, 'count', 1),是以加入購物車按鈕會被隐藏(v-show的display:none),但同時會執行this.$emit('add', event.target);,隻不過這個是異步執行的,并且這個被異步執行的方法add是抛物線小球動畫計算初始目标高度的地方,是以當傳入的購物車按鈕被設定為display:none的時候,動畫的目标初始高度無法計算,就會去使用父層div的高度,進而影響了抛物線小球動畫效果.
解決辦法: 給點選購物車這個按鈕消失時加一個動畫,進而vue有足夠的時間将資料傳遞到異步執行的方法,這樣就不會影響抛物線小球的動畫初始目标計算了。
<transition name='fade'>
	<!--使用.stop.prevent阻止冒泡和預設事件,避免穿透-->
	<div class="buy" v-show="!selcurfood.count || selcurfood.count===0" @click.stop.prevent="ballshow">
	加入購物車
   </div>
</transition>           
點選出現小球
<!--引入cartcontrol元件,并且用一個div包裹他-->
<div class="cartcontrol-wrapper">
   <cart-control :foodsele='selcurfood' @add="addFood"></cart-control>
</div>
ballshow(event){ //  點選加入購物車按鈕,傳入事件,小球出現    
     	 if (!event._constructed) {// pc 點選    因為購物車按鈕在bscroll裡面,是以需要處理掉bscroll的事件類型
          return;
        }
     	  // 抛物線小球動畫
        this.$emit('add', event.target); //觸發目前執行個體food上的事件add(在goods元件上綁定在food元件的add方法)
        Vue.set(this.selcurfood, 'count', 1);
     },
     addFood(target) { //跟add關聯的addFood方法
        this.$emit('add', event.target); // 觸發目前執行個體food上的事件add(在goods元件上綁定在food元件的add方法)
    }           
引用解釋:兩次觸發目前執行個體的事件add是因為兩個操作都是同一個動作,這個動作是綁定在food元件上的add方法,而food元件會在goods元件中被導<food :selcurfood='selectedfoods' @add="addFood" ref="food"></food>,而在goods元件裡面,addFood方法就會指向目前goods元件的方法_drop,繼而使用shopcart的小球抛物線動畫this.$refs.shopcart.drop(target);,這樣就是實作了使用跨元件調用方法的效果.

四、商品評價

(1) 評價類型選擇

<ratingselect  
    :selectType="usrseleType"
    :onlyContent="isonlyContent" 
    :curdesc="foodDesc"
    :ratings="selcurfood.ratings"
    @usrselect='usrseleRating'
    @toggleSwitch='toggleContent' >						
</ratingselect>

data(){
   return {
   	foodtoShow :false,   //  定義商品詳情頁的初始狀态   開始 隐藏
   	usrseleType: ALL,    //  預設類型
    isonlyContent: true, //  是否隻看有内容的評價 預設 不看
    foodDesc: {          //  類型  對象
      all: '全部',
      positive: '推薦',
      negative: '吐槽'
    }
  }
}
 usrseleRating (type){ // 子元件 傳過來的事件
      this.usrseleType = type
      this.$nextTick(() => { // 每更改一次類型,dom 異步重新整理一次
      this.scroll.refresh();
     });
    },
  toggleContent(){ //  切換顯示是否有内容的評價
      this.isonlyContent=!this.isonlyContent
      this.$nextTick(() => { //切換的時候需要重新重新整理bscroll
      this.scroll.refresh();
        });
    },
  needShow(type,txt) { 
    if (this.isonlyContent && !txt) { // //隻顯示有内容的 并且 沒有内容就傳回false
      return false;
    }
    if (this.usrseleType === ALL) { //顯示全部類型的評價
      return true;
    } else { // 顯示對應的類型的評價
      return type === this.usrseleType;
    }
  }           
usrseleRating和toggleContent使用異步$nextTick是因為vue是異步更新dom的,當改變了vue屬性時候,目前的dom不是立即更新的(會導緻頁面的高度變化了,但是bscroll來不及更新,影響滾動體驗),而是會放進去異步更新隊列裡面等候更新,即使這個隊列的等待時間不長,但是也來不及馬上更新dom,是以使用$nextTick強制重新整理這個隊列
在food.vue元件使用usrseleRating和toggleContent來更新food.vue元件的屬性,而不能在子元件ratingSelect裡面更新,因為vue限制了子元件不能更改父元件的屬性,是以通過使用類似this.$emit('select', type);來調用父元件的方法來更改

(2) 評價時間轉換

使用了vue 的過濾器
<div class="time">{{rating.time | formatDate}}</div>
filters: {
  formatDate(time) { 
    let date = new Date(time); 
    //調用curTime子產品的formatDate函數來解析時間
    return formatDate(date, 'yyyy-MM-dd hh:mm');
  }
}
//在es6下,export 函數function的導入需要這樣寫
import { formatDate } from '../../common/js/date'; //導入自定義的date子產品
           
formatDate.js是一個自定義的js元件,不是vue元件,目錄位于:src/common/js,這種寫法是為了練習js的子產品化程式設計
将單獨的一個函數寫成一個子產品
通過export導出函數
通過import導入函數
export function formatDate(date, fmt) { //在es6下導出一個函數
//對一個或多個y進行比對,比對到就進行年的替換(年有四位,是以需要特殊處理)
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
  }
  let o = {
    'M+': date.getMonth() + 1, //js的月是從0開始算,是以要加1
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds()
  };
  //對月,日,時,分,秒進行比對替換(這些都是兩位,可以一起處理)
  for (let k in o) {
    if (new RegExp(`(${k})`).test(fmt)) { //比對到key例如MM
      let str = o[k] + ''; //然後o['MM'] 就是date.getMonth() + 1
      
      //如果比對到的時間是1位數,例如是M,那麼就直接使用date.getMonth() + 1的值,
      //如果是兩位數,那麼就在前面補0,使用padLeftZero函數
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str)); 
    }
  }
  return fmt;
};

//先加兩個0,然後再根據長度截取(因為最長也就2個0的長度)
function padLeftZero(str) {
  return ('00' + str).substr(str.length);
}           

food元件到此差不多了。

原文釋出時間為:2018年02月12日

原文作者:

前端喵

本文來源:

開源中國

如需轉載請聯系原作者

繼續閱讀