项目中遇到了要求搞个根据输入内容实时显示搜索列表并且要求做个搜索历史,如下图
因为我项目中UI框架用的是vux,而vux正好有个search组件满足UI效果和一些交互逻辑,但是搜索历史还是要自己搞的。
思路是在点击搜索列表的某一项时,将数据推入缓存中,在输入框为空并失焦的状态下,取缓存中是数组数据并显示搜索历史列表。
因为HTML5原生的localStorage api需要将数组转化成字符串进行存储,所以在这里我们使用good-storage来满足我们项目中的需求
1.安装good-storage
npm install good-storage (npm安装)
yarn add good-storage (yarn安装)
2.在公共方法utils文件夹下新建cache.js编写本地存储的方法逻辑
1)得到当前的数据存储情况,将关键字存到数组中
2)缓存到本地的最大数据为15条,将新添加的数据放在第一位
3)如果缓存中已有此数据,则把之前重复的数据删除并将新的关键字存在前面
cache.js
/*
点击搜索结果后把选择的结果保存下来
将方法暴露出去
定义存储搜索的key _search_定义内部使用的key
*/
import storage from 'good-storage'
const SEARCH_KEY = '_search_'
const SEARCH_MAX_LENGHT = 15
// 插入函数 arr存储的数组 val传入储存的值 compare前后比较的函数 maxlen存入的最大长度
function insertArray (arr, val, compare, maxlen) {
// 查找要传入的值是否已经在原存储的数据中存在,没有就返回-1
const index = arr.findIndex(compare)
if (index === 0) { // 如果传入值是原数组中的第一个元素,不做处理
return
}
if (index > 0) { // 如果传入值在原数组中已经存在并且不是第一个,则删除原数组中的该元素并把这条数据插入到数组的第一位
arr.splice(index, 1)
}
arr.unshift(val)
if (maxlen && arr.length > maxlen) {
// 如果有条数限制并且数组长度大于设置的最大长度,则将原数组的最后一个元素删除
arr.pop()
}
}
export function saveSearch (query) {
let searches = storage.get(SEARCH_KEY, [])
insertArray(searches, query, (item) => {
return item.materialCode === query.materialCode
}, SEARCH_MAX_LENGHT)
storage.set(SEARCH_KEY, searches)
return searches
}
index.vue
<template>
<search
ref="search"
:results="result"
:placeholder="'物料编码/物料描述'"
v-model="materialModel"
position="absolute"
auto-scroll-to-top
@on-result-click="materialResultClick"
@on-change="getMaterialResult"
@on-focus="onFocus"
@on-cancel="onCancel"
@on-submit="onSubmit">
<slot>
<scroller
ref="materialList"
v-model="materialStatus"
:pullup-config="pullupConfig"
:scroll-bottom-offset="10"
:height="scrollHeight"
use-pullup
lock-x
scrollbar-y
@on-pullup-loading="moreMaterial">
<div>
<div v-if="materialResults.length>0" >
<div v-for="(item, index) in materialResults" :key="index" class="history-item border-bottom-line" @click="materialResultClick(item)">
<p>物料描述:{{ item.materialDescribe }}</p>
<p>物料编码:{{ item.materialCode }}</p>
<p>路局物料编码:{{ item.routeMaterialCode }}</p>
</div>
</div>
<p v-else style="height:100%;line-height:200px;text-align:center">查无数据~</p>
<div slot="pullup" class="xs-plugin-pullup-container xs-plugin-pullup-down" style="position: absolute; width: 100%; height: 60px; line-height: 60px; bottom: -30px; text-align: center;">
<span v-show="materialStatus.pullupStatus === 'default'" />
<span v-show="materialStatus.pullupStatus === 'down' || materialStatus.pullupStatus === 'up'" :class="{'rotate': materialStatus.pullupStatus === 'up'}" class="pulldown-arrow">↓</span>
<span v-show="materialStatus.pullupStatus === 'loading'"><spinner type="ios-small"/></span>
</div>
</div>
</scroller>
</slot>
</search>
<div v-if="historyFlag && searchMaterialList.length>0" class="search-history">
<div class="title">历史搜索</div>
<div v-for="(item, index) in searchMaterialList" :key="index" class="history-item border-bottom-line" @click="materialResultClick(item)">
<p>物料描述:{{ item.materialDescribe }}</p>
<p>物料编码:{{ item.materialCode }}</p>
<p>路局物料编码:{{ item.routeMaterialCode }}</p>
</div>
<p class="clear-history" @click="clearHistory"><img :src="deleteIcon">清除搜索历史</p>
</div>
</template>
<script>
activated () {
let vm = this
let searches = storage.get('_search_')
vm.searchMaterialList = searches || []
if (vm.searchMaterialList.length > 0) {
vm.historyFlag = true
} else {
vm.historyFlag = false
}
},
methods: {
// 点击选择检索到的某项物料
materialResultClick (item) {
let vm = this
saveSearch(item)
vm.materials = []
vm.searchMaterialList.push(item)
storage.set('_search_', vm.searchMaterialList)
vm.materialFlag = false
},
}
</script>