Fork me on GitHub
余鸢

饿了么45个页面重构(五)transition动画使用及food页面开发二

饿了么45个页面重构(四):vuex的使用和food页面开发一

排序

同理,与分类一样,添加个chooseType()点击事件展示排序内容。

1
2
3
4
5
6
7
8
9
10
<div class="sort_item" :class="{choose_type:sortBy == 'sort'}">
<div class="sort_item_container" @click="chooseType('sort')">
<div class="sort_item_border">
<span :class="{category_title: sortBy == 'sort'}">排序</span>
<svg width="10" height="10" xmlns="http://www.w3.org/2000/svg" version="1.1" class="sort_icon">
<polygon points="0,3 10,3 5,8"/>
</svg>
</div>
</div>
</div>

排序的筛选条件分为:智能排序、距离最近、销量最高、起步价最低、配送速度最快、评分最高。分别在dom中渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<transition name="showlist">
<section class="sort_detail_type" v-show="sortBy == 'sort'">
<ul class="sort_list_container" @click="foodSort($event)">
<li class="sort_list_li">
...
<p id="0" :class="{sort_select: sortByType == 0}">
<span>智能排序</span>
...
</p>
</li>
<li class="sort_list_li">
...
<p id="5" :class="{sort_select: sortByType == 5}">
<span>距离最近</span>
<svg v-if="sortByType == 5">...</svg>
</p>
</li>
<li class="sort_list_li">
...
<p id="6" :class="{sort_select: sortByType == 6}">
<span>销量最高</span>
<svg v-if="sortByType == 6">...</svg>
</p>
</li>
<li class="sort_list_li">
...
<p id="1" :class="{sort_select: sortByType == 1}">
<span>起送价最低</span>
<svg v-if="sortByType == 1">...</svg>
</p>
</li>
<li class="sort_list_li">
...
<p id="2" :class="{sort_select: sortByType == 2}">
<span>配送速度最快</span>
<svg v-if="sortByType == 2">...</svg>
</p>
</li>
<li class="sort_list_li">
...
<p id="3" :class="{sort_select: sortByType == 3}">
<span>评分最高</span>
<svg v-if="sortByType == 3">...</svg>
</p>
</li>
</ul>
</section>
</transition>

定义它们的id值,同时绑定当前点击的sortByType为它们的id,代码表示<p id="0" :class="{sort_select: sortByType == 0}">

点击某个选项后当前选项会有一个√的标识符。例如:<svg v-if="sortByType == 5">用来判断sortByType是否与当前id值相同,如果相同,显示√标识符。效果如图:

e13

点击某个排序方式获取相应数据

点击某个排序方式获取相应数据,创建foodSort($event)点击事件

<ul class="sort_list_container" @click="foodSort($event)">

1
2
3
4
5
6
7
8
9
10
methods: {
foodSort (event) {
this.sortByType = event.target.getAttribute('id')
console.log(this.sortByType)
getFoodSort(this.latitude, this.longitude, this.extras, this.sortByType, this.restaurant_category_id).then(res => {
this.shopListArr = Array.from(Object.keys(res.items).map(key => res.items[key].restaurant))
})
this.sortBy = ''
},
}

传入一个事件event,通过event获得触发事件的元素id。this.sortByType = event.target.getAttribute('id'),在控制台打印this.sortByType发现就是当前点击目标的id值。

获取餐厅成功,如图:

选择排序方式

transition过渡

Vue 提供了transition的封装组件,使用transition来设置过渡总体效果。

1
2
3
<transition name="showlist" v-if="category">
...
</transition>

接着为过渡类名添加规则

1
2
3
4
5
6
7
8
.showlist-enter-active, .showlist-leave-active {
transition: all .3s;
transform: translateY(0);
}
.showlist-enter, .showlist-leave-active {
opacity: 0;
transform: translateY(-100%);
}

这四个类名与上面transition的name属性有关,比如name=”showlist”,会有如下四个CSS类名:

  1. showlist-enter:进入过渡的开始状态,元素被插入时生效,只应用一帧后立即删除;
  2. showlist-enter-active:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成之后移除;
  3. showlist-leave:离开过渡的开始状态,元素被删除时触发,只应用一帧后立即删除;
  4. showlist-leave-active:离开过渡的结束状态,元素被删除时生效,离开过渡完成之后被删除

showlist-enter和showlist-leave-active类设置CSS为opacity: 0,说明过渡刚进入和离开的时候透明度为0,即不显示。

筛选

筛选包括配送方式和商家属性,选择后点击确定转跳到筛选后对应的餐厅列表。同理,与前两个一样,添加个chooseType()点击事件展示排序内容。

1
2
3
4
5
6
7
8
<div class="sort_item" :class="{choose_type:sortBy == 'activity'}">
<div class="sort_item_container" @click="chooseType('activity')">
<span :class="{category_title: sortBy == 'activity'}">筛选</span>
<svg width="10" height="10" xmlns="http://www.w3.org/2000/svg" version="1.1" class="sort_icon">
<polygon points="0,3 10,3 5,8"/>
</svg>
</div>
</div>

获取配送方式和商家属性数据

直接上代码吧!

webpack.dev.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get('/api/foodDelivery', (req, res) => {
const url = 'https://h5.ele.me/restapi/shopping/v1/restaurants/filter-bar/attributes'
axios.get(url, {
headers: {
Host: 'h5.ele.me',
Referer: 'https://h5.ele.me/msite/food/'
},
params: req.query
}).then((response) => {
res.json(response.data)
}).catch((e) => {
console.log(e)
})
}),

food.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export function getFoodDelivery (latitude, longitude) {
const url = '/api/foodDelivery'
const data = Object.assign({}, {
latitude,
longitude,
terminal: 'h5'
})
return axios.get(url, {
params: data
}).then((res) => {
return Promise.resolve(res.data)
})
}

food.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
created () {
msiteAdress().then(res => {
// 记录当前经度纬度
this.SET_LATITUDE(res.latitude)
this.SET_LONGITUDE(res.longitude)
getFoodDelivery(this.latitude, this.longitude).then(res3 => {
this.delivery = res3.delivery_mode
this.deliveryId = res3.delivery_mode.id
this.deliveryName = res3.delivery_mode.text
this.supports = res3.supports
this.supports.forEach((item, index) => {
this.support_ids[index] = {status: false, id: item.id}
})
})
}

配送方式

创建点击事件selectDeliveryMode(id),选中配送方式。在data中定义要用到的数据属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
data () {
return {
delivery: null, // 配送方式数据
supports: [], // 商家支持活动数据
deliveryMode: null, // 选中的配送方式
support_ids: [], // 选中的商铺活动列表
filterNum: 0, // 所选中的所有样式的集合
deliveryId: '',
deliveryName: ''
}
},
methods: {
selectDeliveryMode (id) {
if (this.deliveryMode === null) {
this.filterNum++
this.deliveryMode = id
} else if (this.deliveryMode === id) {
this.filterNum--
this.deliveryMode = null
} else {
this.deliveryMode = id
}
},
}

当delivery_mode为空时,选中当前项同时filterNum加一;反之delivery_mode为当前已有值时,清空所选项,并且filterNum减一;如果delivery_mode已有值且不等于当前选择值,则赋值delivery_mode为当前所选id。

将获取到的数据填充dom

1
2
3
4
5
6
7
8
9
10
11
<section style="width: 100%;">
<header class="filter_header_style">配送方式</header>
<ul class="filter_ul">
<li class="filter_li" :key="deliveryId" @click="selectDeliveryMode(deliveryId)">
<svg :style="{opacity: (deliveryId == 0)&&(deliveryMode !== 0)? 0: 1}">
<use xmlns:xlink="http://www.w3.org/1999/xlink" :xlink:href="deliveryMode == deliveryId? '#selected':'#fengniao'"></use>
</svg>
<span :class="{selected_filter: deliveryMode == deliveryId}">{{deliveryName}}</span>
</li>
</ul>
</section>

商家属性

将获取到的商家属性数据填充dom

1
2
3
4
5
6
7
8
9
10
11
12
<section style="width: 100%">
<header class="filter_header_style">商家属性(可以多选)</header>
<ul class="filter_ul" style="padding-bottom: .5rem;">
<li class="filter_li" v-for="(item,index) in supports" :key="item.id" @click="selectSupportIds(index, item.id)">
<svg v-show="support_ids[index].status" class="activity_svg">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#selected"></use>
</svg>
<span class="filter_icon" :style="{color: '#' + item.icon_color, borderColor: '#' + item.icon_color}">{{item.icon_name}}</span>
<span :class="{selected_filter: support_ids[index].status}">{{item.name}}</span>
</li>
</ul>
</section>

遍历supports商家属性v-for="(item,index) in supports",获取每个item的属性。点击商家活动,选中取反当前状态,创建点击事件selectSupportIds(index, item.id)。绑定当前选中的商家属性,表示为:class="{selected_filter: support_ids[index].status}"

1
2
3
4
5
6
7
8
9
10
11
selectSupportIds (index, id) {
// 数组替换新的值
this.support_ids.splice(index, 1, {status: !this.support_ids[index].status, id})
// 重新计算filterNum的个数
this.filterNum = this.delivery_mode === null ? 0 : 1
this.support_ids.forEach(item => {
if (item.status) {
this.filterNum++
}
})
},

清空与确定

点击确定或清空按钮,触发相应的事件处理。

1
2
3
4
5
6
7
<footer class="confirm_filter">
<div class="clear_all filter_button_style" @click="clearSelect">清空</div>
<div class="confirm_select filter_button_style" @click="confirmSelectFun">
确定
<span v-show="filterNum">({{filterNum}})</span>
</div>
</footer>

清空事件

点击清空按钮清空数据,否则一直保持原有状态

1
2
3
4
5
6
clearSelect () {
this.support_ids.map(item => {
item.status = false
})
this.filterNum = 0
},

确定事件

点击确认后更新相对应的餐厅列表数据。

1
2
3
4
5
6
confirmSelectFun () {
getFoodFilter(this.latitude, this.longitude, this.extras, this.support_ids, this.deliveryMode, this.restaurant_category_id).then(res => {
this.shopListArr = Array.from(Object.keys(res.items).map(key => res.items[key].restaurant))
})
this.sortBy = ''
},

筛选选项整个效果如图:

筛选

到此food页面开发完毕!

具体代码见:https://github.com/kakajing/vue-elmm