# 搜索模块
# 搜索组件基本布局
目标:实现搜索组件基本布局
- 点击搜索按钮跳转路由
<van-nav-bar fixed title="黑马头条" right-text="搜索" @click-right="$router.push('/search')" />
- 组件基本布局
<template>
<div>
<!-- 导航栏 -->
<van-nav-bar title="搜索中心" left-arrow @click-left="$router.back()" />
<!-- 搜索框 -->
<van-search v-model.trim="q" placeholder="请输入搜索关键词" shape="round"/>
</div>
</template>
<script>
export default {
name: 'Search',
data () {
return {
// 搜索关键字
q: ''
}
}
}
</script>
# 完善搜索列表布局
目标:完善搜索列表布局
- 搜索历史布局
<!-- 搜索历史 -->
<div class="history-box">
<div class="head">
<span>历史记录</span>
<van-icon name="delete"></van-icon>
</div>
<van-cell-group>
<van-cell>
<a class="word_btn">电脑</a>
<van-icon class="close_btn" slot="right-icon" name="cross"/>
</van-cell>
</van-cell-group>
</div>
- 搜索历史样式
.history-box {
padding: 0 20px;
.head{
line-height: 36px;
color: #999;
.van-icon{
font-size: 16px;
float: right;
margin-top: 10px;;
}
}
.van-cell{
padding: 10px 0;
}
.word_btn{
color:#3296fa;
}
.close_btn{
margin-top:5px;
color: #999;
}
}
- 关键字联想列表
<!-- 联想列表 -->
<van-cell-group class="suggest-box">
<van-cell icon="search"><p><span>j</span>ava</p></van-cell>
</van-cell-group>
- 关键字联想列表样式
.suggest-box{
/deep/ .van-cell{
padding: 10px 20px;
color: #999;
p{
span{
color: red;
}
}
}
}
- 控制联想列表和搜索历史的切换
<!-- 联想列表 -->
<van-cell-group class="suggest-box" v-show='!q'>
<van-cell icon="search"><p><span>j</span>ava</p></van-cell>
</van-cell-group>
<!-- 搜索历史 -->
<div class="history-box" v-show='q'>
<div class="head">
<span>历史记录</span>
<van-icon name="delete"></van-icon>
</div>
<van-cell-group>
<van-cell>
<a class="word_btn">电脑</a>
<van-icon class="close_btn" slot="right-icon" name="cross"/>
</van-cell>
</van-cell-group>
</div>
# 历史记录功能
目标:实现搜索历史记录功能
- 初始化历史记录数据
const SEARCHKEY = 'hm-taotiao-search-123'
history: JSON.parse(window.localStorage.getItem(SEARCHKEY) || '[]')
- 历史记录有数据才显示
<div class="history-box" v-show="history.length&&!q">
- 渲染历史数据
<van-cell v-for="(item, index) in history" :key="index">
<a @click="toSearch(key)" class="word_btn">{{item}}</a>
<van-icon @click="delHistory(key)" class="close_btn" slot="right-icon" name="cross"/>
</van-cell>
- 删除历史
deleteHistory (index) {
// 点击叉号删除对应的历史关键字
this.history.splice(index, 1)
// 缓存也要更新
window.localStorage.setItem(SEARCH_KEY, JSON.stringify(this.history))
},
- 清空历史
<van-icon name="delete" @click="clearHistory()"></van-icon>
clearHistory () {
// 清除所有的历史关键字
this.history = []
// 缓存也要清空
window.localStorage.removeItem(SEARCH_KEY)
},
- 实现搜索功能
<van-search v-model.trim="q" placeholder="请输入搜索关键词" shape="round" @search="onSearch" />
onSearch (key) {
// 把搜索的历史关键字进行存储(把新输入的关键字放到前面)
this.history.unshift(this.q)
// 对数组进行去重操作(Set结构类似数组,但是内部自动去重)
const set = new Set(this.history)
// 把去重之后的结果再转换为数组
// this.history = Array.from(set)
this.history = [...set]
// 把数据保存到缓存
localStorage.setItem(SEARCHKEY, JSON.stringify(this.history))
// 跳转搜索结果
// this.$router.push('/result?kw=' + this.q)
this.$router.push('/result/' + this.q)
},
# 联想搜索功能
目标:实现联想搜索功能
- 封装搜索接口方法
export const suggest = (q) => {
return request({
method: 'get',
url: 'app/v1_0/suggestion',
params: {
q
}
})
}
- 监听关键字变化
searchList () {
// 根据输入的关键字查询匹配的数据列表
if (!this.q) return
try {
window.clearTimeout(this.timer)
this.timer = window.setTimeout(async () => {
const ret = await suggest(this.q)
this.list = ret.data.options
}, 300)
} catch (e) {
console.log(e)
this.$toast.fail('查询失败!')
}
},
- 渲染搜索列表结果
<van-cell :key='index' v-for='(item, index) in list' icon="search">
<p v-html='item'></p>
</van-cell>
- 实现搜索页面的跳转
- 回车后跳转,参数为输入的关键字
- 点击联想的列表跳转,参数为列表项内容
<van-cell :key='index' v-for='(item, index) in list' icon="search">
<p @click='handleJump(item)' v-html='item'></p>
</van-cell>
handleJump (item) {
// 需要在跳转之前,把列表项内容还原回原始数据
const reg = new RegExp('<span>' + this.q + '</span>', 'ig')
this.$router.push('/result/' + item.replace(reg, this.q))
},
# 搜索结果-基本布局
目标:实现搜索列表页面基本布局
- 搜索列表布局
<div class="container">
<!-- 导航固定定位 fixed -->
<van-nav-bar fixed title="搜索结果" left-arrow @click-left="$router.back()" />
<!-- 文章列表 -->
<van-list v-model="loading" :finished="finished" finished-text="没有更多了">
<van-cell-group>
<van-cell>
<div class="article_item">
<h3 class="van-ellipsis">PullRefresh下拉刷新PullRefresh下拉刷新下拉刷新下拉刷新</h3>
<div class="img_box">
<van-image class="w33" fit="cover" src="https://img.yzcdn.cn/vant/cat.jpeg" />
<van-image class="w33" fit="cover" src="https://img.yzcdn.cn/vant/cat.jpeg" />
<van-image class="w33" fit="cover" src="https://img.yzcdn.cn/vant/cat.jpeg" />
</div>
<div class="img_box">
<van-image class="w100" fit="cover" src="https://img.yzcdn.cn/vant/cat.jpeg" />
</div>
<div class="info_box">
<span>你像一阵风</span>
<span>8评论</span>
<span>10分钟前</span>
</div>
</div>
</van-cell>
</van-cell-group>
</van-list>
</div>
- 搜索结果列表样式
.container {
padding-top: 46px;
height: 100%;
overflow-y: auto;
box-sizing: border-box;
}
.article_item {
h3 {
font-weight: normal;
line-height: 2;
}
.img_box {
display: flex;
justify-content: space-between;
.w33 {
width: 33%;
height: 90px;
}
.w100 {
height: 180px;
width: 100%;
}
}
.info_box {
color: #999;
line-height: 2;
position: relative;
span {
padding-right: 10px;
}
}
}
# 搜索结果-上拉功能
目标:实现搜索上拉功能
- 封装上拉加载接口
// 根据关键字搜索文章列表
export const searchArticles = (options) => {
return request({
method: 'get',
url: 'app/v1_0/search',
params: {
// 当前的页码
page: options.page,
// 每页的条数
per_page: options.perPage,
// 搜索的关键字
q: options.q
}
})
}
- 列表相关数据
import { searchArticles } from '@/api/channel.js'
export default {
name: 'Result',
props: ['kw'],
data () {
return {
// 单次加载数据的状态
loading: false,
// 所有数据加载完成的标准
finished: false,
// 加载的数据列表
list: [],
// 查询参数
filterParams: {
page: 1,
per_page: 10,
q: ''
},
// 列表总数
total: 0
}
},
methods: {
async onLoad () {
// 分页加载数据
try {
const ret = await searchArticles(this.filterParams)
this.list.push(...ret.data.results)
this.total = ret.data.total_count
this.loading = false
// 加载完成一页数据后,页码需要累加
this.filterParams.page += 1
// 判断列表加载完成的标志
if (this.list.length >= this.total) {
// 没有更多数据了
this.finished = true
}
} catch (e) {
console.log(e)
this.$toast.fail('加载数据失败!')
}
}
},
created () {
// console.log(this.$route.query.kw)
this.filterParams.q = this.kw
}
}
- 渲染列表内容
<!-- 文章列表 -->
<van-list @load="onLoad" v-model="loading" :finished="finished" finished-text="没有更多了">
<van-cell-group>
<van-cell :to="{name:'article',params:{id:item.art_id.toString()}}" v-for="item in list" :key="item.art_id.toString()">
<div class="article_item">
<h3 class="van-ellipsis">{{item.title}}</h3>
<div class="img_box" v-if="item.cover.type===3">
<van-image class="w33" fit="cover" :src="item.cover.images[0]" />
<van-image class="w33" fit="cover" :src="item.cover.images[1]" />
<van-image class="w33" fit="cover" :src="item.cover.images[2]" />
</div>
<div class="img_box" v-if="item.cover.type===1">
<van-image class="w100" fit="cover" :src="item.cover.images[0]" />
</div>
<div class="info_box">
<span>{{item.aut_name}}</span>
<span>{{item.comm_count}}评论</span>
<span>{{item.pubdate|relativeTime}}</span>
</div>
</div>
</van-cell>
</van-cell-group>
</van-list>