vue/vue-ele-project
Posted smart-girl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue/vue-ele-project相关的知识,希望对你有一定的参考价值。
作者大大的地址是:https://github.com/JinwenXie/vue-ele-project
还是老办法,先运行项目看看效果
我不算是外卖爱好者,不过觉得那个添加商品到购物车的动画效果很好看很有趣
最近的框架好多,简直应接不暇,比如上篇的博客,我甚至以为作者大大自己封装一系列组件写项目,
搜索了一下才发现居然是van有赞小程序。
这个demo用到的是cube-ui也是一个框架,好像是滴滴的。
我一下子就惊吓到了。新的技术层出不穷,我会的和用到的都比较少,做观众欣赏别人的代码和产品也很有意思。
我们还是看代码吧
//main.js
import Vue from 'vue'
import App from './App.vue'
// 引入cube-ui
import './cube-ui'
// 引入注册组件文件
import './register'
import 'common/stylus/index.styl'
Vue.config.productionTip = false
new Vue(
render: h => h(App)
).$mount('#app')
主要说的意思就是页面是从app.vue进行渲染的,先看看register中引入了组件
//register.js
// cube-ui的用法
import createAPI from 'cube-ui'
import Vue from 'vue'
import HeaderDetail from 'components/header-detail/header-detail'
import ShopCartList from 'components/shop-cart-list/shop-cart-list'
import ShopCartStikcy from 'components/shop-cart-sticky/shop-cart-sticky'
import Food from 'components/food/food'
createAPI(Vue, HeaderDetail)
createAPI(Vue, ShopCartList)
createAPI(Vue, ShopCartStikcy)
createAPI(Vue, Food)
接下来看app.vue中做了什么
先看最上面的
引入了组件
//v-header.vue
<template>
<div class="header" @click="showDetail">
<div class="content-wrapper">
<div class="avatar">
<img width="64" height="64" :src="seller.avatar">
</div>
<div class="content">
<div class="title">
<span class="brand"></span>
<span class="name">seller.name</span>
</div>
<div class="description">
seller.description/seller.deliveryTime分钟送达
</div>
<div v-if="seller.supports" class="support">
<support-ico :size=1 :type="seller.supports[0].type"></support-ico>
<span class="text">seller.supports[0].description</span>
</div>
</div>
<div v-if="seller.supports" class="support-count">
<span class="count">seller.supports.length个</span>
<i class="icon-keyboard_arrow_right"></i>
</div>
</div>
<div class="bulletin-wrapper">
<span class="bulletin-title"></span><span class="bulletin-text">seller.bulletin</span>
<i class="icon-keyboard_arrow_right"></i>
</div>
<div class="background">
<img :src="seller.avatar" width="100%" height="100%">
</div>
</div>
</template>
<script type="text/ecmascript-6">
// text/ecmascript-6 用来让编辑器识别vue文件中的es6语法
import SupportIco from 'components/support-ico/support-ico'
export default
name: 'v-header',
props:
seller:
type: Object,
default()
return
,
methods:
showDetail()
this.headerDetailComp = this.headerDetailComp || this.$createHeaderDetail(
// 这个里面的是什么意思?
$props:
seller: 'seller'
)
this.headerDetailComp.show()
,
components:
SupportIco
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "~common/stylus/mixin"
@import "~common/stylus/variable"
.header
position: relative
overflow: hidden
color: $color-white
background: $color-background-ss
.content-wrapper
position: relative
display: flex
align-items: center
padding: 24px 12px 18px 24px
.avatar
flex: 0 0 64px
width: 64px
margin-right: 16px
img
border-radius: 2px
.content
flex: 1
.title
display: flex
align-items: center
margin-bottom: 8px
.brand
width: 30px
height: 18px
bg-image('brand')
background-size: 30px 18px
background-repeat: no-repeat
.name
margin-left: 6px
font-size: $fontsize-large
font-weight: bold
.description
margin-bottom: 8px
line-height: 12px
font-size: $fontsize-small
.support
display: flex
align-items: center
.support-ico
margin-right: 4px
.text
line-height: 12px
font-size: $fontsize-small-s
.support-count
position: absolute
right: 12px
bottom: 14px
display: flex
align-items: center
padding: 0 8px
height: 24px
line-height: 24px
text-align: center
border-radius: 14px
background: $color-background-sss
.count
font-size: $fontsize-small-s
.icon-keyboard_arrow_right
margin-left: 2px
line-height: 24px
font-size: $fontsize-small-s
.bulletin-wrapper
position: relative
display: flex
align-items: center
height: 28px
line-height: 28px
padding: 0 8px
background: $color-background-sss
.bulletin-title
flex: 0 0 22px
width: 22px
height: 12px
margin-right: 4px
bg-image('bulletin')
background-size: 22px 12px
background-repeat: no-repeat
.bulletin-text
flex: 1
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
font-size: $fontsize-small-s
.icon-keyboard_arrow_right
flex: 0 0 10px
width: 10px
font-size: $fontsize-small-s
.background
position: absolute
top: 0
left: 0
width: 100%
height: 100%
z-index: -1
filter: blur(10px)
</style>
看看v-header里面的SupportIco
//不能理解里面写的是什么意思
<template>
<span class="support-ico" :class="iconCls"></span>
</template>
<script>
export default
name: 'support-ico',
props:
size:
type: Number
,
type:
type: Number
,
computed:
iconCls()
const classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']
return `icon-$this.size $classMap[this.type]`
</script>
<style lang="stylus" scoped>
@import "~common/stylus/mixin"
.support-ico
display: inline-block
background-repeat: no-repeat
.icon-1
width: 12px
height: 12px
background-size: 12px 12px
&.decrease
bg-image('decrease_1')
&.discount
bg-image('discount_1')
&.guarantee
bg-image('guarantee_1')
&.invoice
bg-image('invoice_1')
&.special
bg-image('special_1')
.icon-2
width: 16px
height: 16px
background-size: 16px 16px
&.decrease
bg-image('decrease_2')
&.discount
bg-image('discount_2')
&.guarantee
bg-image('guarantee_2')
&.invoice
bg-image('invoice_2')
&.special
bg-image('special_2')
.icon-3
width: 12px
height: 12px
background-size: 12px 12px
&.decrease
bg-image('decrease_3')
&.discount
bg-image('discount_3')
&.guarantee
bg-image('guarantee_3')
&.invoice
bg-image('invoice_3')
&.special
bg-image('special_3')
.icon-4
width: 16px
height: 16px
background-size: 16px 16px
&.decrease
bg-image('decrease_4')
&.discount
bg-image('discount_4')
&.guarantee
bg-image('guarantee_4')
&.invoice
bg-image('invoice_4')
&.special
bg-image('special_4')
</style>
vue提供的 @touchmove.prevent 可以用来阻止滑动,但是这个方法会对其内的子div的滑动事件也禁止掉了,这样会导致中间文字无法滑动。
如果没有中间滑动需求,用 @touchmove.prevent 实现是一个很好的方法。
我们接下来看seller组件
他的页面是
呃呃,其实想不明白,为啥要在header中传入sell组件
//seller组件
<template>
<cube-scroll class="seller" :options="sellerScrollOptions">
<div class="seller-content">
<div class="overview">
<h1 class="title">seller.name</h1>
<div class="desc border-bottom-1px">
<star :size="36" :score="seller.score"></star>
<span class="text">(seller.ratingCount)</span>
<span class="text">月售seller.sellCount单</span>
</div>
<ul class="remark">
<li class="block">
<h2>起送价</h2>
<div class="content">
<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>
<split></split>
<div class="bulletin">
<h1 class="title">公告与活动</h1>
<div class="content-wrapper border-bottom-1px">
<p class="content">seller.bulletin</p>
</div>
<ul v-if="seller.supports" class="supports">
<li
class="support-item border-bottom-1px"
v-for="(item,index) in seller.supports"
:key="index"
>
<support-ico :size=4 :type="seller.supports[index].type"></support-ico>
<span class="text">seller.supports[index].description</span>
</li>
</ul>
</div>
<split></split>
<div class="pics">
<h1 class="title">商家实景</h1>
<cube-scroll class="pic-wrapper" :options="picScrollOptions">
<ul class="pic-list">
<li class="pic-item"
v-for="(pic,index) in seller.pics"
:key="index"
>
<img :src="pic" width="120" height="90">
</li>
</ul>
</cube-scroll>
</div>
<split></split>
<div class="info">
<h1 class="title border-bottom-1px">商家信息</h1>
<ul>
<li
class="info-item border-bottom-1px"
v-for="(info,index) in seller.infos"
:key="index"
>
info
</li>
</ul>
</div>
</div>
</cube-scroll>
</template>
<script>
import saveToLocal, loadFromLocal from 'common/js/storage'
import Star from 'components/star/star'
import Split from 'components/split/split'
import SupportIco from 'components/support-ico/support-ico'
export default
props:
data:
type: Object,
default()
return
,
data()
return
favorite: false,
sellerScrollOptions:
directionLockThreshold: 0,
click: false
,
picScrollOptions:
scrollX: true,
scrollY: false,
stopPropagation: true,
directionLockThreshold: 0
,
computed:
seller()
return this.data.seller ||
,
favoriteText()
return this.favorite ? '已收藏' : '收藏'
,
created()
this.favorite = loadFromLocal(this.seller.id, 'favorite', false)
,
methods:
toggleFavorite()
this.favorite = !this.favorite
saveToLocal(this.seller.id, 'favorite', this.favorite)
,
components:
SupportIco,
Star,
Split
</script>
<style lang="stylus" scoped>
@import "~common/stylus/variable"
@import "~common/stylus/mixin"
.seller
height: 100%
text-align: left
.overview
position: relative
padding: 18px
.title
margin-bottom: 8px
line-height: 14px
font-size: $fontsize-medium
color: $color-dark-grey
.desc
display: flex
align-items: center
padding-bottom: 18px
.star
margin-right: 8px
.text
margin-right: 12px
line-height: 18px
font-size: $fontsize-small-s
color: $color-grey
.remark
display: flex
padding-top: 18px
.block
flex: 1
text-align: center
border-right: 1px solid $color-col-line
&:last-child
border: none
h2
margin-bottom: 4px
line-height: 10px
font-size: $fontsize-small-s
color: $color-light-grey
.content
line-height: 24px
font-size: $fontsize-small-s
color: $color-dark-grey
.stress
font-size: $fontsize-large-xxx
.favorite
position: absolute
width: 50px
right: 11px
top: 18px
text-align: center
.icon-favorite
display: block
margin-bottom: 4px
line-height: 24px
font-size: $fontsize-large-xxx
color: $color-light-grey-s
&.active
color: $color-red
.text
line-height: 10px
font-size: $fontsize-small-s
color: $color-grey
.bulletin
padding: 18px 18px 0 18px
white-space: normal
.title
margin-bottom: 8px
line-height: 14px
color: $color-dark-grey
font-size: $fontsize-medium
.content-wrapper
padding: 0 12px 16px 12px
.content
line-height: 24px
font-size: $fontsize-small
color: $color-red
.supports
.support-item
display: flex
align-items: center
padding: 16px 12px
&:last-child
border-none()
.support-ico
margin-right: 6px
.text
line-height: 16px
font-size: $fontsize-small
color: $color-dark-grey
.pics
padding: 18px
.title
margin-bottom: 12px
line-height: 14px
color: $color-dark-grey
font-size: $fontsize-medium
.pic-wrapper
display: flex
align-items: center
.pic-list
.pic-item
display: inline-block
margin-right: 6px
width: 120px
height: 90px
&:last-child
margin: 0
.info
padding: 18px 18px 0 18px
color: $color-dark-grey
.title
padding-bottom: 12px
line-height: 14px
font-size: $fontsize-medium
.info-item
padding: 16px 12px
line-height: 16px
font-size: $fontsize-small
&:last-child
border-none()
</style>
看一下common/js/storage
//src\\common\\js\\storage.js
import storage from 'good-storage'
// vue搜索历史本地存储-good-storage
const SELLER_KEY = '__seller__'
export function saveToLocal(id, key, val)
const seller = storage.get(SELLER_KEY, )
if (!seller[id])
seller[id] =
seller[id][key] = val
storage.set(SELLER_KEY, seller)
export function loadFromLocal(id, key, def)
const seller = storage.get(SELLER_KEY, )
if (!seller[id])
return def
return seller[id][key] || def
//src\\components\\star\\star.vue
<template>
<div class="star" :class="starType">
<span v-for="(itemClass,index) in itemClasses" :class="itemClass" class="star-item" :key="index"></span>
</div>
</template>
<script>
const LENGTH = 5
const CLS_ON = 'on'
const CLS_HALF = 'half'
const CLS_OFF = 'off'
export default
props:
size:
type: Number
,
score:
type: Number
,
computed:
starType()
return 'star-' + this.size
,
itemClasses()
let result = []
const score = Math.floor(this.score * 2) / 2
const hasDecimal = score % 1 !== 0
const integer = Math.floor(score)
for (let i = 0; i < integer; i++)
result.push(CLS_ON)
if (hasDecimal)
result.push(CLS_HALF)
while (result.length < LENGTH)
result.push(CLS_OFF)
return result
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "~common/stylus/mixin.styl"
.star
display: flex
align-items: center
justify-content: center
.star-item
background-repeat: no-repeat
&.star-48
.star-item
width: 20px
height: 20px
margin-right: 22px
background-size: 20px 20px
&:last-child
margin-right: 0
&.on
bg-image('star48_on')
&.half
bg-image('star48_half')
&.off
bg-image('star48_off')
&.star-36
.star-item
width: 15px
height: 15px
margin-right: 6px
background-size: 15px 15px
&:last-child
margin-right: 0
&.on
bg-image('star36_on')
&.half
bg-image('star36_half')
&.off
bg-image('star36_off')
&.star-24
.star-item
width: 10px
height: 10px
margin-right: 3px
background-size: 10px 10px
&:last-child
margin-right: 0
&.on
bg-image('star24_on')
&.half
bg-image('star24_half')
&.off
bg-image('star24_off')
</style>
上面内容没有看懂
//split.vue
<template>
<div class="split"></div>
</template>
<script>
export default
name: 'split'
</script>
<style lang="stylus" scoped>
@import "~common/stylus/variable"
.split
width: 100%
height: 16px
border-top: 1px solid $color-row-line
border-bottom: 1px solid $color-row-line
background: $color-background-ssss
</style>
//src\\components\\support-ico\\support-ico.vue
<template>
<span class="support-ico" :class="iconCls"></span>
</template>
<script>
export default
name: 'support-ico',
props:
size:
type: Number
,
type:
type: Number
,
computed:
iconCls()
const classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']
return `icon-$this.size $classMap[this.type]`
</script>
<style lang="stylus" scoped>
@import "~common/stylus/mixin"
.support-ico
display: inline-block
background-repeat: no-repeat
.icon-1
width: 12px
height: 12px
background-size: 12px 12px
&.decrease
bg-image('decrease_1')
&.discount
bg-image('discount_1')
&.guarantee
bg-image('guarantee_1')
&.invoice
bg-image('invoice_1')
&.special
bg-image('special_1')
.icon-2
width: 16px
height: 16px
background-size: 16px 16px
&.decrease
bg-image('decrease_2')
&.discount
bg-image('discount_2')
&.guarantee
bg-image('guarantee_2')
&.invoice
bg-image('invoice_2')
&.special
bg-image('special_2')
.icon-3
width: 12px
height: 12px
background-size: 12px 12px
&.decrease
bg-image('decrease_3')
&.discount
bg-image('discount_3')
&.guarantee
bg-image('guarantee_3')
&.invoice
bg-image('invoice_3')
&.special
bg-image('special_3')
.icon-4
width: 16px
height: 16px
background-size: 16px 16px
&.decrease
bg-image('decrease_4')
&.discount
bg-image('discount_4')
&.guarantee
bg-image('guarantee_4')
&.invoice
bg-image('invoice_4')
&.special
bg-image('special_4')
</style>
上面内容我没有看懂
接下来看tab里面的内容
在App.vue中引入了goods组件
//goods.vue
<template>
<div class="goods">
<div class="scroll-nav-wrapper">
<cube-scroll-nav
:side=true
:data="goods"
:options="scrollOptions"
v-if="goods.length"
>
<template slot="bar" slot-scope="props">
<cube-scroll-nav-bar
direction="vertical"
:labels="props.labels"
:txts="barTxts"
:current="props.current"
>
<template slot-scope="props">
<div class="text">
<support-ico
v-if="props.txt.type>=1"
:size=3
:type="props.txt.type"
></support-ico>
<span>props.txt.name</span>
<span class="num" v-if="props.txt.count">
<bubble :num="props.txt.count"></bubble>
</span>
</div>
</template>
</cube-scroll-nav-bar>
</template>
<cube-scroll-nav-panel
v-for="good in goods"
:key="good.name"
:label="good.name"
:title="good.name"
>
<ul>
<li
@click="selectFood(food)"
v-for="food in good.foods"
:key="food.name"
class="food-item"
>
<div class="icon">
<img width="57" height="57" :src="food.icon">
</div>
<div class="content">
<h2 class="name">food.name</h2>
<p class="desc">food.description</p>
<div class="extra">
<span class="count">月售food.sellCount份</span><span>好评率food.rating%</span>
</div>
<div class="price">
<span class="now">¥food.price</span>
<span class="old" v-show="food.oldPrice">¥food.oldPrice</span>
</div>
<div class="cart-control-wrapper">
<cart-control @add="onAdd" :food="food"></cart-control>
</div>
</div>
</li>
</ul>
</cube-scroll-nav-panel>
</cube-scroll-nav>
</div>
<div class="shop-cart-wrapper">
<shop-cart
ref="shopCart"
:select-foods="selectFoods"
:delivery-price="seller.deliveryPrice"
:min-price="seller.minPrice"></shop-cart>
</div>
<!--<food :food="selectedFood" ref="food"></food>-->
</div>
</template>
<script>
import getGoods from 'api'
import CartControl from 'components/cart-control/cart-control'
import ShopCart from 'components/shop-cart/shop-cart'
import SupportIco from 'components/support-ico/support-ico'
import Bubble from 'components/bubble/bubble'
export default
name: 'goods',
props:
data:
type: Object,
default()
return
,
data()
return
goods: [],
selectedFood: ,
scrollOptions:
click: false,
directionLockThreshold: 0
,
computed:
seller()
return this.data.seller
,
selectFoods()
let foods = []
this.goods.forEach((good) =>
console.log('good....', good)
// 将数据渲染在页面中
good.foods.forEach((food) =>
if (food.count)
foods.push(food)
)
)
return foods
,
barTxts()
let ret = []
this.goods.forEach((good) =>
console.log('......good', good)
const type, name, foods = good
let count = 0
foods.forEach((food) =>
count += food.count || 0
)
ret.push(
type,
name,
count
)
)
return ret
,
methods:
fetch()
if (!this.fetched)
this.fetched = true
getGoods(
id: this.seller.id
).then((goods) =>
console.log('.......app', goods)
this.goods = goods
)
,
selectFood(food)
this.selectedFood = food
this._showFood()
this._showShopCartSticky()
,
onAdd(target)
this.$refs.shopCart.drop(target)
,
_showFood()
this.foodComp = this.foodComp || this.$createFood(
$props:
food: 'selectedFood'
,
$events:
add: (target) =>
this.shopCartStickyComp.drop(target)
,
leave: () =>
this._hideShopCartSticky()
)
this.foodComp.show()
,
_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()
,
components:
Bubble,
SupportIco,
CartControl,
ShopCart
</script>
<style lang="stylus" scoped>
@import "~common/stylus/mixin"
@import "~common/stylus/variable"
.goods
position: relative
text-align: left
height: 100%
.scroll-nav-wrapper
position: absolute
width: 100%
top: 0
left: 0
bottom: 48px
>>> .cube-scroll-nav-bar
width: 80px
white-space: normal
overflow: hidden
>>> .cube-scroll-nav-bar-item
padding: 0 10px
display: flex
align-items: center
height: 56px
line-height: 14px
font-size: $fontsize-small
background: $color-background-ssss
.text
flex: 1
position: relative
.num
position: absolute
right: -8px
top: -10px
.support-ico
display: inline-block
vertical-align: top
margin-right: 4px
>>> .cube-scroll-nav-bar-item_active
background: $color-white
color: $color-dark-grey
>>> .cube-scroll-nav-panel-title
padding-left: 14px
height: 26px
line-height: 26px
border-left: 2px solid $color-col-line
font-size: $fontsize-small
color: $color-grey
background: $color-background-ssss
.food-item
display: flex
margin: 18px
padding-bottom: 18px
position: relative
&:last-child
border-none()
margin-bottom: 0
.icon
flex: 0 0 57px
margin-right: 10px
img
height: auto
.content
flex: 1
.name
margin: 2px 0 8px 0
height: 14px
line-height: 14px
font-size: $fontsize-medium
color: $color-dark-grey
.desc, .extra
line-height: 10px
font-size: $fontsize-small-s
color: $color-light-grey
.desc
line-height: 12px
margin-bottom: 8px
.extra
.count
margin-right: 12px
.price
font-weight: 700
line-height: 24px
.now
margin-right: 8px
font-size: $fontsize-medium
color: $color-red
.old
text-decoration: line-through
font-size: $fontsize-small-s
color: $color-light-grey
.cart-control-wrapper
position: absolute
right: 0
bottom: 12px
.shop-cart-wrapper
position: absolute
left: 0
bottom: 0
z-index: 50
width: 100%
height: 48px
</style>
//src\\components\\cart-control\\cart-control.vue
<template>
<div class="cartcontrol">
<transition name="move">
<div class="cart-decrease" v-show="food.count>0" @click.stop="decrease">
<span class="inner icon-remove_circle_outline"></span>
</div>
</transition>
<div class="cart-count" v-show="food.count>0">food.count</div>
<div class="cart-add icon-add_circle" @click.stop="add"></div>
</div>
</template>
<script>
const EVENT_ADD = 'add'
export default
name: 'cart-control',
props:
food:
type: Object
,
methods:
add(event)
if (!this.food.count)
this.$set(this.food, 'count', 1)
else
this.food.count++
this.$emit(EVENT_ADD, event.target)
,
decrease()
if (this.food.count)
this.food.count--
</script>
<style lang="stylus" scoped>
@import "~common/stylus/variable"
.cartcontrol
display: flex
align-items: center
.cart-decrease
display: inline-block
padding: 6px
opacity: 1
.inner
display: inline-block
line-height: 24px
font-size: $fontsize-large-xxx
color: $color-blue
transition: all 0.4s linear
transform: rotate(0)
&.move-enter-active, &.move-leave-active
transition: all 0.4s linear
&.move-enter, &.move-leave-active
opacity: 0
transform: translate3d(24px, 0, 0)
.inner
transform: rotate(180deg)
.cart-count
width: 12px
line-height: 24px
text-align: center
font-size: $fontsize-small-s
color: $color-grey
.cart-add
display: inline-block
padding: 6px
line-height: 24px
font-size: $fontsize-large-xxx
color: $color-blue
</style>
//src\\components\\shop-cart\\shop-cart.vue
<template>
<div>
<div class="shopcart">
<div class="content" @click="toggleList">
<div class="content-left">
<div class="logo-wrapper">
<div class="logo" :class="'highlight':totalCount>0">
<i class="icon-shopping_cart" :class="'highlight':totalCount>0"></i>
</div>
<div class="num" v-show="totalCount>0">
<bubble :num="totalCount"></bubble>
</div>
</div>
<div class="price" :class="'highlight':totalPrice>0">¥totalPrice</div>
<div class="desc">另需配送费¥deliveryPrice元</div>
</div>
<div class="content-right" @click="pay">
<div class="pay" :class="payClass">
payDesc
</div>
</div>
</div>
<!-- 关于抛物线的那个 -->
<div class="ball-container">
<div v-for="(ball,index) in balls" :key="index">
<transition
@before-enter="beforeDrop"
@enter="dropping"
@after-enter="afterDrop">
<div class="ball" v-show="ball.show">
<div class="inner inner-hook"></div>
</div>
</transition>
</div>
</div>
</div>
</div>
</template>
<script>
import Bubble from 'components/bubble/bubble'
const BALL_LEN = 10
const innerClsHook = 'inner-hook'
function createBalls()
let balls = []
for (let i = 0; i < BALL_LEN; i++)
balls.push( show: false )
return balls
export default
name: 'shop-cart',
props:
selectFoods:
type: Array,
default()
return []
,
deliveryPrice:
type: Number,
default: 0
,
minPrice:
type: Number,
default: 0
,
sticky:
type: Boolean,
default: false
,
fold:
type: Boolean,
default: true
,
data()
return
balls: createBalls(),
listFold: this.fold
,
created()
this.dropBalls = []
,
computed:
totalPrice()
let total = 0
this.selectFoods.forEach((food) =>
total += food.price * food.count
)
return total
,
totalCount()
let count = 0
this.selectFoods.forEach((food) =>
count += food.count
)
return count
,
payDesc()
if (this.totalPrice === 0)
return `¥$this.minPrice元起送`
else if (this.totalPrice < this.minPrice)
let diff = this.minPrice - this.totalPrice
return `还差¥$diff元起送`
else
return '去结算'
,
payClass()
if (!this.totalCount || this.totalPrice < this.minPrice)
return 'not-enough'
else
return 'enough'
,
methods:
toggleList()
if (this.listFold)
if (!this.totalCount)
return
this.listFold = false
this._showShopCartList()
this._showShopCartSticky()
else
this.listFold = true
this._hideShopCartList()
,
pay(e)
if (this.totalPrice < this.minPrice)
return
this.$createDialog(
title: '支付',
content: `您需要支付$this.totalPrice元`
).show()
e.stopPropagation()
,
drop(el)
for (let i = 0; i < this.balls.length; i++)
const ball = this.balls[i]
if (!ball.show)
ball.show = true
ball.el = el
this.dropBalls.push(ball)
return
,
// 丢之前
beforeDrop(el)
const ball = this.dropBalls[this.dropBalls.length - 1]
const rect = ball.el.getBoundingClientRect()
// getBoundingClientRect用于获取元素相对与浏览器视口的位置
const x = rect.left - 32
const y = -(window.innerHeight - rect.top - 22)
el.style.display = ''
el.style.transform = el.style.webkitTransform = `translate3d(0,$ypx,0)`
const inner = el.getElementsByClassName(innerClsHook)[0]
inner.style.transform = inner.style.webkitTransform = `translate3d($xpx,0,0)`
,
dropping(el, done)
this._reflow = document.body.offsetHeight
el.style.transform = el.style.webkitTransform = `translate3d(0,0,0)`
const inner = el.getElementsByClassName(innerClsHook)[0]
inner.style.transform = inner.style.webkitTransform = `translate3d(0,0,0)`
el.addEventListener('transitionend', done)
,
afterDrop(el)
const ball = this.dropBalls.shift()
if (ball)
ball.show = false
el.style.display = 'none'
,
_showShopCartList()
this.shopCartListComp = this.shopCartListComp || this.$createShopCartList(
$props:
selectFoods: 'selectFoods'
,
$events:
leave: () =>
this._hideShopCartSticky()
,
hide: () =>
this.listFold = true
,
add: (el) =>
this.shopCartStickyComp.drop(el)
)
this.shopCartListComp.show()
,
_showShopCartSticky()
this.shopCartStickyComp = this.shopCartStickyComp || this.$createShopCartSticky(
$props:
selectFoods: 'selectFoods',
deliveryPrice: 'deliveryPrice',
minPrice: 'minPrice',
fold: 'listFold',
list: this.shopCartListComp
)
this.shopCartStickyComp.show()
,
_hideShopCartList()
const list = this.sticky ? this.$parent.list : this.shopCartListComp
list.hide && list.hide()
,
_hideShopCartSticky()
this.shopCartStickyComp.hide()
,
watch:
fold(newVal)
this.listFold = newVal
,
totalCount(count)
if (!this.fold && count === 0)
this._hideShopCartList()
,
components:
Bubble
</script>
<style lang="stylus" scoped>
@import "~common/stylus/mixin"
@import "~common/stylus/variable"
.shopcart
height: 100%
.content
display: flex
background: $color-background
font-size: 0
color: $color-light-grey
.content-left
flex: 1
.logo-wrapper
display: inline-block
vertical-align: top
position: relative
top: -10px
margin: 0 12px
padding: 6px
width: 56px
height: 56px
box-sizing: border-box
border-radius: 50%
background: $color-background
.logo
width: 100%
height: 100%
border-radius: 50%
text-align: center
background: $color-dark-grey
&.highlight
background: $color-blue
.icon-shopping_cart
line-height: 44px
font-size: $fontsize-large-xxx
color: $color-light-grey
&.highlight
color: $color-white
.num
position: absolute
top: 0
right: 0
.price
display: inline-block
vertical-align: top
margin-top: 12px
line-height: 24px
padding-right: 12px
box-sizing: border-box
border-right: 1px solid rgba(255, 255, 255, 0.1)
font-weight: 700
font-size: $fontsize-large
&.highlight
color: $color-white
.desc
display: inline-block
vertical-align: top
margin: 12px 0 0 12px
line-height: 24px
font-size: $fontsize-small-s
.content-right
flex: 0 0 105px
width: 105px
.pay
height: 48px
line-height: 48px
text-align: center
font-weight: 700
font-size: $fontsize-small
&.not-enough
background: $color-dark-grey
&.enough
background: $color-green
color: $color-white
.ball-container
.ball
position: fixed
left: 32px
bottom: 22px
z-index: 200
transition: all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41)
.inner
width: 16px
height: 16px
border-radius: 50%
background: $color-blue
transition: all 0.4s linear
</style>
//src\\components\\bubble\\bubble.vue
<template>
<span class="bubble">
num
</span>
</template>
<script>
export default
name: 'bubble',
props:
num:
type: Number
</script>
<style lang="stylus" scoped>
@import "~common/stylus/variable"
.bubble
display: inline-block
padding: 0 5px
height: 16px
line-height: 16px
text-align: center
border-radius: 16px
font-family: Helvetica
font-weight: 700
font-size: $fontsize-small-s
color: $color-white
background: linear-gradient(to right, $color-orange, $color-red)
</style>
再看rating组件
<template>
<cube-scroll ref="scroll" class="ratings" :options="scrollOptions">
<div class="ratings-content">
<div class="overview">
<div class="overview-left">
<h1 class="score">seller.score</h1>
<div class="title">综合评分</div>
<div class="rank">高于周边商家seller.rankRate%</div>
</div>
<div class="overview-right">
<div class="score-wrapper">
<span class="title">服务态度</span>
<star :size="36" :score="seller.serviceScore"></star>
<span class="score">seller.serviceScore</span>
</div>
<div class="score-wrapper">
<span class="title">商品评分</span>
<star :size="36" :score="seller.foodScore"></star>
<span class="score">seller.foodScore</span>
</div>
<div class="delivery-wrapper">
<span class="title">送达时间</span>
<span class="delivery">seller.deliveryTime分钟</span>
</div>
</div>
</div>
<split></split>
<rating-select
@select="onSelect"
@toggle="onToggle"
:selectType="selectType"
:onlyContent="onlyContent"
:ratings="ratings"
>
</rating-select>
<div class="rating-wrapper">
<ul>
<li
v-for="(rating,index) in computedRatings"
:key="index"
class="rating-item border-bottom-1px"
>
<div class="avatar">
<img width="28" height="28" :src="rating.avatar">
</div>
<div class="content">
<h1 class="name">rating.username</h1>
<div class="star-wrapper">
<star :size="24" :score="rating.score"></star>
<span class="delivery" v-show="rating.deliveryTime">rating.deliveryTime</span>
</div>
<p class="text">rating.text</p>
<div class="recommend" v-show="rating.recommend && rating.recommend.length">
<span class="icon-thumb_up"></span>
<span
class="item"
v-for="(item,index) in rating.recommend"
:key="index"
>
item
</span>
</div>
<div class="time">
format(rating.rateTime)
</div>
</div>
</li>
</ul>
</div>
</div>
</cube-scroll>
</template>
<script>
import Star from 'components/star/star'
import RatingSelect from 'components/rating-select/rating-select'
import Split from 'components/split/split'
import ratingMixin from 'common/mixins/rating'
import getRatings from 'api'
import moment from 'moment'
export default
name: 'ratings',
mixins: [ratingMixin],
props:
data:
type: Object
,
data ()
return
ratings: [],
scrollOptions:
click: false,
directionLockThreshold: 0
,
computed:
seller ()
return this.data.seller ||
,
methods:
fetch ()
if (!this.fetched)
this.fetched = true
getRatings(
id: this.seller.id
).then((ratings) =>
this.ratings = ratings
)
,
format (time)
return moment(time).format('YYYY-MM-DD hh:mm')
,
components:
Star,
Split,
RatingSelect
,
watch:
selectType ()
this.$nextTick(() =>
this.$refs.scroll.refresh()
)
</script>
<style lang="stylus" scoped>
@import "~common/stylus/variable"
@import "~common/stylus/mixin"
.ratings
position: relative
text-align: left
white-space: normal
height: 100%
.overview
display: flex
padding: 18px 0
.overview-left
flex: 0 0 137px
padding: 6px 0
width: 137px
border-right: 1px solid $color-col-line
text-align: center
@media only screen and (max-width: 320px)
flex: 0 0 120px
width: 120px
.score
margin-bottom: 6px
line-height: 28px
font-size: $fontsize-large-xxx
color: $color-orange
.title
margin-bottom: 8px
line-height: 12px
font-size: $fontsize-small
color: $color-dark-grey
.rank
line-height: 10px
font-size: $fontsize-small-s
color: $color-light-grey
.overview-right
flex: 1
padding: 6px 0 6px 24px
@media only screen and (max-width: 320px)
padding-left: 6px
.score-wrapper
display: flex
align-items: center
margin-bottom: 8px
.title
line-height: 18px
font-size: $fontsize-small
color: $color-dark-grey
.star
margin: 0 12px
.score
line-height: 18px
font-size: $fontsize-small
color: $color-orange
.delivery-wrapper
display: flex
align-items: center
.title
line-height: 18px
font-size: $fontsize-small
color: $color-dark-grey
.delivery
margin-left: 12px
font-size: $fontsize-small
color: $color-light-grey
.rating-wrapper
padding: 0 18px
.rating-item
display: flex
padding: 18px 0
&:last-child
border-none()
.avatar
flex: 0 0 28px
width: 28px
margin-right: 12px
img
height: auto
border-radius: 50%
.content
position: relative
flex: 1
.name
margin-bottom: 4px
line-height: 12px
font-size: $fontsize-small-s
color: $color-dark-grey
.star-wrapper
margin-bottom: 6px
display: flex
align-items: center
.star
margin-right: 6px
.delivery
font-size: $fontsize-small-s
color: $color-light-grey
.text
margin-bottom: 8px
line-height: 18px
color: $color-dark-grey
font-size: $fontsize-small
.recommend
display: flex
align-items: center
flex-wrap: wrap
line-height: 16px
.icon-thumb_up, .item
margin: 0 8px 4px 0
font-size: $fontsize-small-s
.icon-thumb_up
color: $color-blue
.item
padding: 0 6px
border: 1px solid $color-row-line
border-radius: 1px
color: $color-light-grey
background: $color-white
.time
position: absolute
top: 0
right: 0
line-height: 12px
font-size: $fontsize-small
color: $color-light-grey
</style>
//src\\components\\tab\\tab.vue
<template>
<div class="tab">
<cube-tab-bar
:useTransition=false
:showSlider=true
v-model="selectedLabel"
:data="tabs"
ref="tabBar"
class="border-bottom-1px"
>
</cube-tab-bar>
<div class="slide-wrapper">
<cube-slide
:loop=false
:auto-play=false
:show-dots=false
:initial-index="index"
ref="slide"
:options="slideOptions"
@scroll="onScroll"
@change="onChange"
>
<cube-slide-item v-for="(tab,index) in tabs" :key="index">
<component ref="component" :is="tab.component" :data="tab.data"></component>
</cube-slide-item>
</cube-slide>
</div>
</div>
</template>
<script>
export default
name: 'tab',
props:
tabs:
type: Array,
default()
return []
,
initialIndex:
type: Number,
default: 0
,
data()
return
index: this.initialIndex,
slideOptions:
listenScroll: true,
probeType: 3,
directionLockThreshold: 0
,
computed:
selectedLabel:
get()
return this.tabs[this.index].label
,
set(newVal)
this.index = this.tabs.findIndex((value) =>
return value.label === newVal
)
,
mounted()
this.onChange(this.index)
,
methods:
onScroll(pos)
const tabBarWidth = this.$refs.tabBar.$el.clientWidth
const slideWidth = this.$refs.slide.slide.scrollerWidth
const transform = -pos.x / slideWidth * tabBarWidth
this.$refs.tabBar.setSliderTransform(transform)
,
onChange(current)
this.index = current
const instance = this.$refs.component[current]
if (instance && instance.fetch)
instance.fetch()
</script>
<style lang="stylus" scoped>
@import "~common/stylus/variable"
.tab
display: flex
flex-direction: column
height: 100%
>>> .cube-tab
padding: 10px 0
.slide-wrapper
flex: 1
overflow: hidden
</style>
上面的代码:我并没有看懂,应该花一段时间专门学习,还有我发现有些基础知识点我不会。
以上是关于vue/vue-ele-project的主要内容,如果未能解决你的问题,请参考以下文章