一. 搜索 -历史记录管理
目标:构建搜索页的静态布局,完成历史记录的管理
需求:
(1). 搜索历史基本渲染
(2). 点击搜索(添加历史)
点击 搜索按钮 或 底下历史记录,都能进行搜索
①. 若之前 没有 相同搜索关键字,则直接 追加到最前面
②. 若之前 已有 相同搜索关键字,将该原有关键字移除,再追加
(3). 清空历史:添加清空图标,可以清空历史记录
(4). 持久化:搜索历史需要持久化,刷新历史不丢失
1. 搜索历史基本渲染
search / index.vue
<template>
<div class="search">
<van-nav-bar title="商品搜索" left-arrow @click-left="$router.go(-1)" />
<van-search show-action shape="round" placeholder="请输入搜索关键词" clearable>
<template #action>
<div>搜索</div>
</template>
</van-search>
<!-- 搜索历史 -->
<div class="search-history">
<div class="title">
<span>最近搜索</span>
<van-icon name="delete-o" size="16" />
</div>
<div class="list">
<div class="list-item">手机</div>
<div class="list-item">电视</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SearchtailIndex',
data () {
return {
history: ['手机', '白酒', '电视'] // 历史记录
}
}
}
</script>
<style lang="less" scoped>
.search {
.searchBtn {
background-color: #fa2209;
color: #fff
}
::v-deep .van-search__action {
background-color: #c21401;
color: #fff;
padding: 0 20px;
border-radius: 0 5px 5px 0;
margin-right: 10px;
}
::v-deep .van-icon-arrow-left {
color: #333;
}
.title {
height: 40px;
line-height: 40px;
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
}
.list {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
padding: 0 10px;
gap: 5%
}
.list-item {
width: 30%;
text-align: center;
padding: 7px;
line-height: 15px;
border-radius: 50px;
background: #fff;
font-size: 13px;
border: 1px solid #efefef;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-bottom: 10px;
}
}
</style>
2. 注册Icon组件
utils / vant-ui.js
// 按需导入
import Vue from 'vue'
import {Icon} from 'vant'
Vue.use(Icon)
3. 添加操作历史记录
search / index.vue
<template>
<!-- 双向绑定获取输入框的值 -->
<van-search v-model="search" show-action shape="round" placeholder="请输入搜索关键词" clearable>
<!-- 将输入框中的search传入 -->
<div @click="goSearch(search)">搜索</div>
<!--判断是否有数据-->
<div class="search-history" v-if="history.length > 0">
<!--点击清空-->
<van-icon @click="clear" name="delete-o" size="16" />
<!-- 将循环出的数据名传入 -->
<div v-for="item in history" :key="item" class="list-item" @click="goSearch(item)">{{item}}</div>
</template>
<script>
export default {
name: 'SearchtailIndex',
data () {
return {
search: '', // 输入框的内容
history: ['手机', '白酒', '电视'] // 历史记录
}
},
methods: {
goSearch (key) {
// console.log('进行了搜索,搜索历史要更新', key)
// indexOf: 查询key在this.history中的下标,不存在返回-1
const index = this.history.indexOf(key)
if (index !== -1) {
// 存在相同的项,将原有关键字移除
// splice(从哪开始,删几个,项1,项2)
this.history.splice(index, 1)
}
// 将数据追加到第一个
this.history.unshift(key)
},
// 清空搜索记录
clear () {
this.history = []
}
}
}
</script>
4. 持久化
(1). 封装持久化工具
utils / storage.js
// 约定一个通用键名
...
const HISTORY_KEY = 'cc_history_list'
// 获取个人信息
...
// 获取搜索历史
export const getHistoryList = () => {
const result = localStorage.getItem(HISTORY_KEY)
return result ? JSON.parse(result) : []
}
// 设置搜索历史
export const setHistoryList = (arr) => {
localStorage.setItem(HISTORY_KEY, JSON.stringify(arr))
}
(2). 页面中直接调用
search / index.vue
<script>
import { getHistoryList, setHistoryList } from '@/utils/storage'
//export default {
// name: 'SearchtailIndex',
// data () {
// return {
//search: '', // 输入框的内容
history: getHistoryList() // 从本地获取历史记录
// }
// },
// methods: {
// goSearch (key) {
// console.log('进行了搜索,搜索历史要更新', key)
// indexOf: 查询key在this.history中的下标,不存在返回-1
//const index = this.history.indexOf(key)
//if (index !== -1) {
// 存在相同的项,将原有关键字移除
// splice(从哪开始,删几个,项1,项2)
// this.history.splice(index, 1)
//}
// 将数据追加到第一个
//this.history.unshift(key)
// 往本地存储
setHistoryList(this.history)
// 跳转到搜索列表页
this.$router.push(`/searchlist?search=${key}`)
},
// 清空
//clear () {
//this.history = []
setHistoryList([]) // 本地也清空
// }
// }
//}
</script>
5. 代码示例
二. 搜索列表- 静态布局 & 渲染
目标:实现搜索列表页静态结构,封装接口,完成搜索列表页的渲染
1. 静态页面渲染
search / list.vue
<template>
<div class="search">
<van-nav-bar fixed title="商品搜索" left-arrow @click-left="$router.go(-1)" />
<van-search readonly shape="round" background="#ffffff" value="手机" show-action @click="$router.push('/search')">
<template>
<van-icon class="tool" name="apps-o" />
</template>
</van-search>
<!-- 排序选项按钮 -->
<div class="sort-btns">
<div class="sort-item">综合</div>
<div class="sort-item">销量</div>
<div class="sort-item">价格</div>
</div>
<div class="goods-list">
<GoodsItem v-for="item in 10" :key="item"></GoodsItem>
</div>
</div>
</template>
<script>
import GoodsItem from '@/components/GoodsItem.vue'
export default {
name: 'ListIndex',
components: {
GoodsItem
}
}
</script>
<style lang="less" scoped>
.search {
padding-top: 46px;
::v-deep .van-icon-arrow-left {
color: #333;
}
.tool {
font-size: 24px;
height: 40px;
line-height: 40px;
}
.sort-btns {
display: flex;
height: 36px;
line-height: 36px;
.sort-item {
text-align: center;
flex: 1;
font-size: 16px;
}
}
}
// 商品样式
.goods-list {
background-color: #f6f6f6;
}
</style>
2. 封装请求数据接口
api / product.js
import request from '@/utils/request'
// 获取搜索商品列表的数据
export const getProList = (obj) => {
const { categoryId, goodsName, page } = obj
return request.get('/goods/list', {
params: {
categoryId,
goodsName,
page
}
})
}
3. 调用接口渲染页面
search / list.vue
<template>
<!--显示地址栏中的参数-->
<van-search readonly shape="round" background="#ffffff" :value="querySearch || '搜索商品'" show-action @click="$router.push('/search')">
<!--向子组件传递参数-->
<GoodsItem v-for="item in proList" :key="item.goods_id" :item="item"></GoodsItem>
</template>
<script>
import GoodsItem from '@/components/GoodsItem.vue'
import { getProList } from '@/api/product'
export default {
name: 'ListIndex',
components: {
GoodsItem
},
computed: {
// 获取地址栏的搜索关键字
querySearch() {
return this.$route.query.search
}
},
data() {
return {
page: 1,
proList: []
}
},
async created() {
const { data: { list } } = await getProList({
goodsName: this.querySearch,
page: this.page
})
this.proList = list.data
}
}
</script>