一、搜索模块组件集成
清除src\pages\search\search.vue页面之前的测试代码,修改为:
<template>
<div>
<SearchBar
:focus="searchFocus"
@onChange="onChange"
/>
<TagGroup
header-text="热门搜索"
btn-text="换一批"
:value="hotSearch"
@onBtnClick="changeHotSearch"
@onTagClick="showBookDetail"
v-if="hotSearch.length > 0 && !showList"
/>
<TagGroup
header-text="历史搜索"
btn-text="清空"
:value="historySearch"
@onBtnClick="clearHistorySearch"
@onTagClick="searchKeyWord"
v-if="historySearch.length > 0 && !showList"
/>
<SearchList
:data="searchList"
v-if="showList"
/>
</div>
</template>
<script>
import SearchBar from '../../components/home/SearchBar'
import SearchList from '../../components/search/SearchList'
export default {
components: {SearchList, SearchBar},
computed: {
showList() {
const keys = Object.keys(this.searchList)
return keys.length > 0
}
},
data() {
return {
hotSearch: [],
historySearch: [],
searchList: {},
searchFocus: true
}
},
methods: {
changeHotSearch() {
console.log('change hot search')
},
showBookDetail() {
console.log('showBookDetail')
},
clearHistorySearch() {
console.log('clearHistorySearch')
},
searchKeyWord() {
console.log('searchKeyWord')
},
onChange(keyword) {
console.log(keyword)
}
}
}
</script>
<style lang="scss" scoped>
</style>
这里的显示与否取决于内容是否为空:
- 数组:Array.length > 0
- 对象:Object.keys.length > 0
二、搜索模块API对接
src\api\index.js中新增搜索接口调用方法
export function search(params) {
return get(`${API_URL}/book/search`, params)
}
修改src\components\search\SearchList.vue:
<template>
<div class="search-list-wrapper">
<SearchItem
icon="apps-o"
:title="category"
sub-title="Category"
@onClick="showList(category.title, 'category')"
v-if="category"
/>
<SearchItem
icon="user-o"
:title="author"
sub-title="Author"
@onClick="showList(author.title, 'author')"
v-if="author"
/>
<SearchItem
icon="newspaper-o"
:title="publisher"
sub-title="Publisher"
@onClick="showList(publisher.title, 'publisher')"
v-if="publisher"
/>
<SearchTable :data="data.book" @onClick="onBookClick"/>
</div>
</template>
<script>
import SearchItem from './SearchItem'
import SearchTable from './SearchTable'
export default {
components: {SearchTable, SearchItem},
props: {
data: Object
},
computed: {
category() {
if (this.data && this.data.category && this.data.category.length > 0) {
return this.data.category[0].categoryText
} else {
return null
}
},
author() {
if (this.data && this.data.author && this.data.author.length > 0) {
return this.data.author[0].author
} else {
return null
}
},
publisher() {
if (this.data && this.data.publisher && this.data.publisher.length > 0) {
return this.data.publisher[0].publisher
} else {
return null
}
}
},
methods: {
showList(text, key) {
console.log(text, key)
},
onBookClick(book) {
console.log(book)
}
}
}
</script>
<style lang="scss" scoped>
</style>
修改src\components\search\SearchItem.vue的部分样式:
.search-item-info {
width: 80%;
...
.search-item-title {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
...
}
...
修改src\pages\search\search.vue:
- 修改onChange方法并新增onSearch方法
onChange(keyword) {
if (!keyword || keyword.trim().length === 0) {
this.searchList = {}
return
}
this.onSearch(keyword)
},
onSearch(keyword) {
search({
keyword, openId: this.openId
}).then(res => {
this.searchList = res.data.data
})
}
- 新增data:
openId: ''
并通过本地缓存获取:
mounted() {
this.openId = getStorageSync('openId')
},
预览:
三、热门搜索API对接
在src\pages\search\search.vue为SearchBar新增onClear,用来在点击清除按钮时进行数据清除:
<SearchBar
:focus="searchFocus"
@onChange="onChange"
@onClear="onClear"
/>
onClear() {
this.searchList = {}
}
src\api\index.js中新增热门搜索接口调用方法
export function hotSearch(params) {
return get(`${API_URL}/book/hot-search`, params)
}
修改src\pages\index\index.vue中跳转搜索页面的事件,使在跳转搜索页面时携带参数hotSearch:
onSearchBarClick() {
this.$router.push({
path: '/pages/search/main',
query: {
hotSearch: this.hotSearch
}
})
},
在src\pages\search\search.vue
页面加载时调用hotSearch
接口方法,并拿到首页传来的hotSearch
赋给搜索页面新增的data
:hotSearchKeyword
:
data() {
return {
hotSearch: [],
historySearch: [],
hotSearchKeyword: '',
searchList: {},
searchFocus: true,
openId: ''
}
},
mounted() {
this.openId = getStorageSync('openId')
hotSearch().then(res => {
this.hotSearch = res.data.data
})
this.hotSearchKeyword = this.$route.query.hotSearch
},
在计算属性中创建hotSearchArray方法来获取热门搜索标题,并在中调用
hotSearchArray() {
const _hotSearch = []
this.hotSearch.forEach(obj => _hotSearch.push(obj.title))
return _hotSearch
}
修改src\components\base\TagGroup.vue的样式
<style lang="scss" scoped>
.tag-group-wrapper {
...
width: 100%;
padding-bottom: 10px;
...
.tag-goup {
width: 100%;
box-sizing: border-box;
...
.tag-group-inner {
max-width: 100%;
box-sizing: border-box;
...
}
}
}
</style>
修改src\components\base\Tag.vue的样式:
<style lang="scss" scoped>
.tag-wrapper {
width: 100%;
box-sizing: border-box;
...
.tag {
width: 100%;
...
}
}
</style>
这样热门搜索就可以正常显示了
修改showBookDetail方法
showBookDetail(text, index) {
console.log('showBookDetail', index, text)
},
四、历史搜索和热搜更新功能开发
在src\pages\search\search.vue中新增onConfirm方法:
onConfirm(keyword) {
// 1.判断是否有搜索关键词
if (!keyword || keyword.trim().length === 0) {
// 如果没有,则获取热门搜索词,通过热门搜索词发起请求
keyword = this.hotSearchKeyword
this.$refs.searchBar.setValue(keyword)
} else {
// 如果有,使用搜索关键词发起请求
}
this.onSearch(keyword)
// 2.将搜索结果写入历史搜索
if (!this.historySearch.includes(keyword)) {
this.historySearch.push(keyword)
// 将历史搜索写入缓存
setStorageSync(KEY_HISTORY_SEARCH, this.historySearch)
}
// 3.将搜索框失去焦点
this.searchFocus = false
}
清空历史记录:
clearHistorySearch() {
this.historySearch = []
setStorageSync(KEY_HISTORY_SEARCH, [])
},
在搜索页面加载时从缓存中拿到历史搜索记录:
mounted() {
this.openId = getStorageSync('openId')
hotSearch().then(res => {
// 从接口中拿到热门搜索关键词内容,并赋值本页面
this.hotSearch = res.data.data
})
// 将首页传来的参数(热门搜索关键词)赋给本页面的热门搜索关键词,完成参数传递
this.hotSearchKeyword = this.$route.query.hotSearch
// 若缓存中有历史搜索记录,页面渲染完毕就拿到,否则置空
this.historySearch = getStorageSync(KEY_HISTORY_SEARCH) || []
},
为了方便,可以定义一个historySearch常量,然后在需要的地方引用:
const KEY_HISTORY_SEARCH = 'historySearch'
换一批其实也就是重新从hotSearch接口拿一遍数据:
hotSearch().then(res => {
// 从接口中拿到热门搜索关键词内容,并赋值本页面
this.hotSearch = res.data.data
})
点击历史搜索的tag触发searchKeyWord事件:
searchKeyWord(text) {
this.$refs.searchBar.setValue(text)
this.onSearch(text)
},
预览:
五、触底自动刷新功能开发
页面滑动时搜索框失去焦点(调用的是一个页面的生命周期函数):
onPageScroll() {
if (this.searchFocus) {
this.searchFocus = false
}
},
新增data
:page: 1
,在onSearch给search接口传参时传入:
onSearch(keyword) {
search({
keyword, openId: this.openId, page: this.page
}).then(res => {
this.searchList = res.data.data
})
},
在src\api\wechat.js中新增
export function showToast(title) {
mpvue.showToast({
title,
duration: 2000
})
}
到达页面底部触发页面周期函数onReachBottom:
onReachBottom() {
if (this.showList) {
// 再加载20条数据,即一页数据
this.page++
// 拿到搜索关键词
const searchWord = this.$refs.searchBar.getValue()
// 这时就不能直接调用onSearch方法,否则整个页面都会加载新的一页数据来替换掉原有页数据
search({
keyword: searchWord,
openId: this.openId,
page: this.page
}).then(res => {
// 通过解构的方式拿到数据
const { book } = res.data.data
if (book && book.length > 0) {
// 这里使用拓展运算符可以把book数组里面的元素散着push进去而不是整个数组push进去
this.searchList.book.push(...book)
} else {
showToast('没有更多数据了')
}
})
}
},
记得要在关键词改变时初始化page为1
onChange(keyword) {
...
this.page = 1
this.searchFocus = true // 为了防止焦点丢失,建议加上
...
},
在mounted中也是如此:
mounted() {
...
this.page = 1
...
},