deliveryPrice:{ // 單價 從json seller 對象數據中獲取 type:Number, default:0 }, minPrice:{ // 最低起送價 從json seller 對象數據中獲取 type:Number, default:20 }
[Vue warn]: Error in render: "TypeError: Cannot read property 'deliveryPrice' of undefined"
<keep-alive> <router-view :sell="sellerObj"></router-view> </keep-alive>
props: { sell: Object // 至關於 形參 },
<shopCart :delivery-price="sell.deliveryPrice" :min-price="sell.minPrice"></shopCart>
props:{ // 經過父組件傳過來的 ( 至關於形參 ) selefoodsArr:{ // 用戶選中的商品存放在一個數組裏 接收的是 data.json數據的 goods(數組) type:Array, // 當父組件傳過來的 類型是對象或者 是數組時, default 就是一個函數 default (){ return [] // 返回數組 存放着選中 商品 對應的 goods下的 foods 數組(由 父組件 的 實參 決定的返回值) } }
computed:{ totalPrice (){ //計算總價,超過起送額度後提示可付款 let total=0 // 定義一個返回值 this.selefoodsArr.forEach((rfoods) =>{ // 遍歷 這個 goods 數組 取到 價格 和 數量 (固然在這裏數據庫沒有count 這個屬性,稍後 咱們會利用 vue.set() 新建一個count 屬性) total += rfoods.price * rfoods.count // 形參 rfoods 實參 是 foods }); return total; }, totalCount (){ // //計算選中的food數量,在購物車圖標處顯示,採用絕對定位,top:0;right:0;顯示在購物車圖標右上角 let count=0 this.selefoodsArr.forEach((rfoods) =>{ // 形參 rfoods 實參 是 foods count += rfoods.count }); return count; }, payDesc (){ //控制底部右邊內容隨food的變化而變化,payDesc()控制顯示內容,enough 添加類調整顯示樣式 let diff = this.minPrice - this.totalPrice if (!this.totalPrice) { return `¥${this.minPrice}起送` } else if (diff > 0) { return `還差¥${diff}元` } else { return '去結算' } } }
<div class="shopCart"> <div class="content"> <div class="content-left"> <div class="logo-wrapper"> <!--徽章 展現選中商品的個數--> <div class="badge" v-show="totalCount"> {{totalCount}} </div> <!--購物車 圖標 選擇商品和未選擇商品 時 動態改變 樣式 條件:只要選擇了商品即總價不爲0 ,樣式變--> <div class="logo" :class="{'active':totalCount}"> <i class="icon-shopping_cart"></i> </div> </div> <!--同理: 總價 不爲0 字體高亮--> <div class="price" :class="{'active':totalPrice}"> ¥{{totalPrice}} </div> <!--配送費 data.json 提供--> <div class="desc"> 另須要配送費¥{{deliveryPrice}}元 </div> </div> <!--根據條件 動態 改變樣式--> <div class="content-right" :class="{'enough':totalPrice>=minPrice}"> {{payDesc}} </div> </div> </div>
&.active color white &.enough background #00b43c color white
在goods 下的 foods 添加一個屬性 count,用來存儲用戶選中的商品個數,計算商品總價 以及 關聯徽章(顯示用戶選擇商品的個數)的變化vue
方法:經過import Vue from 'vue';使用set接口,經過vue.set()添加屬性,當它變化時就能被檢測到,從而父組件能獲取到count值(遍歷選中的商品時使用)web
methods:{ addCart(event){ // 點擊count 加, //console.log(event.target); if (!event._constructed) { // 去掉自帶click事件的點擊 return; } if(!this.foodsele.count){ Vue.set(this.foodsele, 'count', 1) }else{ this.foodsele.count++ } }, decreaseCart (event){ // 點擊減小 if (!event._constructed) { // 去掉自帶click事件的點擊 return; } if(this.foodsele.count){ this.foodsele.count -- } } }
<transition name='move'> <!--平移動畫--> <div class="cart-decrease" v-show="foodsele.count" @click='decreaseCart($event)'> <span class="icon-remove_circle_outline inner"></span><!--旋轉、透明度動畫--> </div> </transition>
.cart-decrease display inline-block padding 6px transition: all .4s linear /*過渡效果的 CSS 屬性的名稱、過渡效果須要多少時間、速度效果的速度曲線*/ .inner line-height 24px font-size 24px color rgb(0,160,220) transition all 0.4s linear &.move-enter-active, &.move-leave-active transform translate3d(0,0,0) /* 這樣能夠開啓硬件加速,動畫更流暢,3D旋轉,X軸位移24px */ .inner display inline-block /* 設置成inline-block纔有高度,纔能有動畫 */ transform rotate(0) &.move-enter, &.move-leave-active opacity: 0 transform translate3d(24px,0,0) .inner transform rotate(180deg)
組件之間傳值-1數據庫
組件之間傳值-2json
addCart(event){ // 點擊count 加, // console.log(event.target); if (!event._constructed) { // 去掉自帶click事件的點擊 return; } if(!this.foodsele.count){ Vue.set(this.foodsele, 'count', 1) }else{ this.foodsele.count++ } // 當點擊 添加數量時 經過 $emit 屬性 提交一個名爲 add 給父組件 // 子組件經過 $emit觸發 add事件 ,將參數傳遞給父組件 this.$emit('add', event.target); }
<cart-control :foodsele='food' @add="addFood"></cart-control>
addFood(target) { this._drop(target); }
<shopCart ref="shopCart" :delivery-price="sell.deliveryPrice" :min-price="sell.minPrice" :selefoods-arr='selectfoods' ></shopCart>
_drop(target) { // 體驗優化,異步執行下落動畫 this.$nextTick(() => { this.$refs.shopCart.balldrop(target);// 將target傳入shopCart子組件中的balldrop方法,因此drop方法能得到用戶點擊按鈕的元素,即能獲取點擊按鈕的位置 }); }
data (){ // 定義一個數組 來 控制小球的狀態 定義多個對象,表示頁面中作多同時運動的小球 return{ // 定義 5 個 小球 balls:[{show:false},{show:false},{show:false},{show:false},{show:false}], dropBalls:[] // 接收下落小球 } }
methods:{ balldrop(ele) { // console.log(el) 取到點擊 對象 for(var i=0;i<this.balls.length;i++){ let ball=this.balls[i] if(!ball.show){ ball.show=true ball.ele=ele this.dropBalls.push(ball) return; } } } }
beforeEnter (el){ //找到因此設爲true的小球 let count=this.balls.length while(count--){ let ball = this.balls[count]; if(ball.show){ let pos=ball.el.getBoundingClientRect() //返回元素相對於視口偏移的位置 let x=pos.left-32 // 點擊的按鈕與小球(fixed)之間x方向的差值 let y=-(window.innerHeight-pos.top-22) el.style.display = ''; //設置初始位置前,手動置空,覆蓋以前的display:none,使其顯示 el.style.webkitTransform = `translate3d(0,${y}px,0)`; //外層元素作縱向的動畫,y是變量 el.style.transform = `translate3d(0,${y}px,0)`; let inner = el.getElementsByClassName('inner_hook')[0];//內層元素作橫向動畫,inner-hook(用於js選擇的樣式名加上-hook,代表只是用 //於js選擇的,沒有真實的樣式含義) inner.style.webkitTransform = `translate3d(${x}px,0,0)`; inner.style.transform = `translate3d(${x}px,0,0)`; } } }, enter(el) { /* eslint-disable no-unused-vars */ let rf = el.offsetHeight; this.$nextTick(() => {//異步執行 el.style.webkitTransform = 'translate3d(0,0,0)'; //重置回來 el.style.transform = 'translate3d(0,0,0)'; let inner = el.getElementsByClassName('inner_hook')[0]; inner.style.webkitTransform = 'translate3d(0,0,0)'; inner.style.transform = 'translate3d(0,0,0)'; }); }, afterEnter(el) { let ball = this.dropBalls.shift(); //取到作完動畫的球,再置爲false,即重置,它還能夠接着被利用 if (ball) { ball.show = false; el.style.display = 'none'; } }
<div class="ball-container"> <div v-for="ball in balls"> <transition name="drop" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"> <div class="ball" v-show="ball.show"> <div class="inner inner_hook"></div> </div> </transition> </div> </div>
&.drop-enter,&.drop-enter-active transition all 0.4s cubic-bezier(0.49,-0.29,0.75,0.41) .inner width 16px height 16px border-radius 50% background rgb(0,160,220) transition all 0.4s linear