Fork me on GitHub
余鸢

饿了么45个页面重构(六):搜索页面开发

点击首页的搜索图标跳转到搜索页面,效果如图:

点击搜索按钮跳转

搜索组件

搜索页面分为四个部分:头部、搜索框、商家列表、搜索历史。输入搜索内容展示搜索数据,搜索食品或者餐厅时隐藏搜索历史。

头部

引入e-header.vue组件,设置头部名称,返回按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div class="paddingTop search_page">
<e-header :head-title="headTitle" :go-back="goBack"></e-header>
</div>
</template>
export default{
data(){
return {
headTitle: '搜索',
goBack: true
}
}
}

搜索框

添加输入框和提交按钮dom,绑定搜索内容v-model="searchValue",在输入框内输入搜索内容直接显示搜索到的数据。点击提交按钮,搜索结果并显示。

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
<form class="search_form">
<input type="search" name="search" :placeholder="placeholder" v-model="searchValue" class="search_input">
<input type="submit" name="submit" class="search_submit" @click.prevent="searchTarget('')">
</form>
import { searchRestaurant } from 'api/search'
data () {
return {
placeholder: '请输入商家或美食名称',
searchValue: '',
extras: '',
restaurant: [], // 搜索返回结果中的餐厅
restaurantList: [], // 搜索返回的结果
restaurantFood: [] // 搜索返回结果中的food
}
},
computed: {
...mapState([
'latitude', 'longitude'
])
},
methods: {
searchTarget () {
this.extras = ['activities', 'coupon']
// 获取搜索结果
searchRestaurant(this.searchValue, this.latitude, this.longitude, this.extras).then((res) => {
this.restaurantList = res.inside[3].restaurant_with_foods
this.restaurantList.forEach(item => {
this.restaurant = item.restaurant
this.restaurantFood = item.foods
// console.log(this.restaurantList)
})
})
},
}

searchRestaurant()方法中需要用到参数latitudelongitude,我直接通过vuex mapState获取到它们的值。

搜索结果商家列表

对搜索到的数据渲染在dom上。使用具有路由功能的router-link应用(点击)导航,通过 to 属性指定目标地址,默认渲染成带有正确链接的 <a> 标签,可以通过配置 tag 属性生成别的标签.。另外,当目标路由成功激活时,链接元素自动设置一个表示激活的 CSS 类名。对搜索结果restaurantFood进行遍历获取 v-for="item in restaurantFood",设置key值:key="item.id"。设置路由路径:to="{path: '/shop', query: {id:item.id}}",query是带查询参数,结果为 /shop?id=xxx。对获取到的每个Item进行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
<template>
<div class="paddingTop search_page">
...
<section v-if="restaurantList.length">
<h4 class="title_restaurant">商家</h4>
<ul class="list_container">
<router-link :to="{path: '/shop', query: {id:item.id}}" tag="li" v-for="item in restaurantFood" :key="item.id" class="list_li">
<section class="item_left">
<img :src="getImgPath(item.image_path)" class="restaurant_img">
</section>
<section class="item_right">
<div class="item_right_text">
<p>
<span>{{item.name}}</span>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24" height="14" class="pay_icon">
<polygon points="0,14 4,0 24,0 20,14" style="fill:none;stroke:#FF6000;stroke-width:1"/>
<line x1="1.5" y1="12" x2="20" y2="12" style="stroke:#FF6000;stroke-width:1.5"/>
<text x="3.5" y="9" style="fill:#FF6000;font-size:9px;font-weight:bold;">支付</text>
</svg>
</p>
<p>月售 {{item.month_sales}} 单</p>
<!--<p>{{item.delivery_fee}} 元起送 / 距离{{item.distance}}</p>-->
<p>¥{{item.price}}</p>
</div>
<ul class="item_right_detail">
<li v-for="activitiy in item.activities" :key="activitiy.id">
<span :style="{backgroundColor: '#' + activitiy.icon_color}"
class="activities_icon">{{activitiy.icon_name}}</span>
<span>{{activitiy.name}}</span>
<span class="only_phone">(手机客户端专享)</span>
</li>
</ul>
</section>
</router-link>
</ul>
</section>
</div>
</template>

搜索历史

每次把输入的搜索内容记录下来并保存。修改searchTarget方法,在获取搜索结果之前先判断是否有搜索历史记录historyValue,如果有添加到searchHistory里,同时隐藏历史记录。点击搜索结果进入下一页面时进行判断是否已经有一样的历史记录,如果没有则新增,如果有则不做重复储存,判断完成后进入下一页。

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
searchTarget (historyValue) {
this.extras = ['activities', 'coupon']
if (historyValue) {
this.searchHistory = historyValue
} else if (!this.searchValue) {
return
}
// 隐藏历史记录
this.showHistory = false
// 获取搜索结果
searchRestaurant(this.searchValue, this.latitude, this.longitude, this.extras).then((res) => {
this.restaurantList = res.inside[3].restaurant_with_foods
this.restaurantList.forEach(item => {
this.restaurant = item.restaurant
this.restaurantFood = item.foods
// console.log(this.restaurantList)
})
this.emptyResult = !this.restaurantList.length
})
/**
* 点击搜索结果进入下一页面时进行判断是否已经有一样的历史记录
* 如果没有则新增,如果有则不做重复储存,判断完成后进入下一页
*/
let history = getStore('searchHistory')
if (history) {
let checkRepeat = false
this.searchHistory = JSON.parse(history)
this.searchHistory.forEach(item => {
if (item === this.searchValue) {
checkRepeat = true
}
})
if (!checkRepeat) {
this.searchHistory.push(this.searchValue)
}
} else {
this.searchHistory.push(this.searchValue)
}
setStore('searchHistory', this.searchHistory)
},

在dom中渲染数据。首先判断是否存在searchHistory,如果有显示搜索历史块v-if="searchHistory.length&&showHistory"。遍历searchHistory得出每个item,当点击某个item时进入下一页@click="searchTarget(item)"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<section class="search_history" v-if="searchHistory.length&&showHistory">
<h4 class="title_restaurant">搜索历史</h4>
<ul>
<li v-for="(item, index) in searchHistory" :key="index" class="history_list">
<span class="history_text ellipsis" @click="searchTarget(item)">{{item}}</span>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="delete_icon" @click="deleteHistory(index)">
<line x1="8" y1="8" x2="18" y2="18" style="stroke:#999;stroke-width:3" />
<line x1="18" y1="8" x2="8" y2="18" style="stroke:#999;stroke-width:3" />
</svg>
</li>
</ul>
<footer class="clear_history" @click="clearAllHistory">清空搜索历史</footer>
</section>
<div class="search_none" v-if="emptyResult">很抱歉!无搜索结果</div>

对每个搜索历史添加个删除按钮,删除某个搜索历史,创建deleteHistory(index)事件

1
2
3
4
5
deleteHistory (index) {
// 点击删除按钮,删除当前历史记录
this.searchHistory.splice(index, 1)
setStore('searchHistory', this.searchHistory)
},

删除当前历史记录同时再对搜索历史进行存储。

点击清空搜索历史,也就是清除全部搜索历史,对searchHistory置空同时再对搜索历史进行存储。

1
2
3
4
clearAllHistory () {
this.searchHistory = []
setStore('searchHistory', this.searchHistory)
}

搜索结束后,删除搜索内容直到为空时清空搜索结果,并显示历史记录。

1
2
3
4
5
6
7
checkInput () {
if (this.searchValue === '') {
this.showHistory = true // 显示历史记录
this.restaurantList = [] // 清空搜索结果
this.emptyResult = false // 隐藏搜索为空提示
}
},

功能实现如图:

搜索