今天来实现搜索页面,首先分析一下搜索页面都要完成什么功能。第一肯定需要搜索框,在这里搜索框可以使用受控组件也可以使用非受控组件,看个人喜好。第二搜索框的下方应该有一个历史搜索记录,这个历史搜索记录可以存在localStorage中。然后我们还可以控制历史记录的显示与隐藏,当搜索框有内容时历史记录就不会展示。第三根据输入的关键字调用接口渲染数据。好了,接下来就开始实现吧!
<template> <div> <div> <van-search v-model="value" v-haha @input="input" shape="round" background="#007bff" placeholder="请输入搜索关键词" @search="search" > <template #left> <van-icon name="arrow-left" @click="jump" /> </template> </van-search> </div> <!-- 搜索联想 --> <div class="sugg-list"> <div class="sugg-item" v-for="(str, index) in suggestList" :key="index" v-html="lightFn(str, value)" @click="click(str)" > {{ str }} </div> </div> <!-- 搜索历史 --> <div class="search-history"> <!-- 标题 --> <van-cell title="搜索历史"> <!-- 使用 right-icon 插槽来自定义右侧图标 --> <template #right-icon> <van-icon name="delete" class="search-icon" @click="clear"/> </template> </van-cell> <!-- 历史列表 --> <div class="history-list" @click="historyFn(str)" v-for="(str, index) in history" :key="index"> <span class="history-item">{{str}}</span> </div> </div> </div> </template> <script> import Vue from 'vue' import { Search } from 'vant' import { suggestAPI } from '@/api/index' import { lightFn } from '@/utils/str' Vue.use(Search) export default { methods: { jump () { this.$router.back() console.log(this) }, // 防抖 input () { clearTimeout(this.timer) // 清空联想列表 if (this.value.length === 0) { this.suggestList = [] } else { this.timer = setTimeout(async () => { if (this.value.length === 0) return // 防止空内容触发下面逻辑 const res = await suggestAPI({ q: this.value }) this.suggestList = res.data.data.options }, 500) } }, lightFn, search () { // 搜索关键字保存到数组里 if (this.value.length > 0) { this.history.push(this.value) setTimeout(() => { this.$router.push(`/searchResult/${this.value}`) }) } }, // 点击联想跳结果页 click (str) { // 搜索关键字保存到数组里 this.history.push(str) setTimeout(() => { this.$router.push(`/searchResult/${str}`) }) }, // 点击历史记录跳转结果页 historyFn (str) { setTimeout(() => { this.$router.push(`/searchResult/${str}`) }) }, // 清空历史记录 clear () { this.history = [] } }, data () { return { timer: null, value: '', suggestList: [], // 建议列表 history: JSON.parse(localStorage.getItem('his')) || [] } }, watch: { history: { deep: true, handler () { // Set数组去重 const theSet = new Set(this.history) // set类型转回数组 const arr = Array.from(theSet) localStorage.setItem('his', JSON.stringify(arr)) // 将数据存储到本地 } } } } </script> <style lang="less" scoped> /deep/.van-icon-arrow-left { color: white; } .sugg-list { .sugg-item { padding: 0 15px; border-bottom: 1px solid #f8f8f8; font-size: 14px; line-height: 50px; // 实现省略号的三行代码 white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .search-icon { font-size: 16px; line-height: inherit; } } .history-list { padding: 0 10px; float: left; .history-item { display: inline-block; font-size: 12px; padding: 8px 14px; background-color: #efefef; margin: 10px 8px 0px 8px; border-radius: 10px; } } </style>
页面效果展示:
自动获取焦点的函数:
// 封装一个自定义指令,用于搜索框自动对焦
Vue.directive('haha', {
inserted (el) {
// nodeName获取原生DOM标签的标签名,注意标签名均是大写
if (el.nodeName === 'TEXTAREA' || el.nodeName === 'INPUT') {
el.focus()
} else {
const theInput = el.querySelector('input')
const theTextArea = el.querySelector('textarea')
if (theInput) theInput.focus()
if (theTextArea) theTextArea.focus()
}
},
update (el) {
// 指令所在标签,被更新时触发
if (el.nodeName === 'TEXTAREA' || el.nodeName === 'INPUT') {
el.focus()
} else {
const theInput = el.querySelector('input')
const theTextArea = el.querySelector('textarea')
if (theInput) theInput.focus()
if (theTextArea) theTextArea.focus()
}
}
})
搜索建议结果列表
<template> <div> <div> <van-nav-bar title="搜索结果" left-text="返回" left-arrow @click-left="$router.go(-1)" /> <!-- <van-cell v-for="item in list" :key="item.art_id" :title="item.title" /> --> </div> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" :immediate-check="false" @load="onLoad" > <div> <ArticleItem v-for="item in list" :key="item.art_id" :artObj="item" :isShow="false" @click.native="click(item.art_id)"/> </div> </van-list> </div> </template> <script> import Vue from 'vue' import { NavBar, Cell, CellGroup, List } from 'vant' import { searchResultAPI } from '@/api/index' import ArticleItem from '@/components/ArticleItem.vue' Vue.use(Cell) Vue.use(List) Vue.use(CellGroup) Vue.use(NavBar) export default { async created () { const res = await searchResultAPI({ q: this.$route.params.id }) console.log(res) this.list = res.data.data.results }, data () { return { list: [], // 结果列表 loading: false, // 加载状态 finished: false // 完成状态 } }, components: { ArticleItem }, methods: { async onLoad () { if (this.list.length > 0) { const res = await searchResultAPI({ q: this.$route.params.id }) console.log(res) if (res.data.data.results === 0) { this.finished = true return } this.list = [...this.list, ...res.data.data.results] this.loading = false } }, click (id) { // 点击跳转到详情页 this.$router.push({ path: `/detail?art_id=${id}` }) } } } </script> <style> </style>
用到的接口请求:
// 获取搜索结果
export const searchResultAPI = ({ q }) => request({
url: '/v1_0/search',
params: {
q: q
}
})
// 获取搜索联想结果
export const suggestAPI = ({ q }) => request({
url: '/v1_0/suggestion',
params: {
q: q
}
})
好了,到这里搜索页功能基本完成。