基於uni-app的通用搜索組件(歷史記錄,app語音輸入,搜索推薦)解析 zy-search
一個通用的搜索組件,包含搜索歷史記錄,語音輸入,搜索推薦功能
插件地址:https://ext.dcloud.net.cn/plugin?id=512
插件內容:
<template name="zy-search">
<view>
<view class="search">
<!-- 運行在app端的代碼 -->
<!-- #ifdef APP-PLUS -->
<image src="../../static/zy-search/voice.svg" mode="aspectFit" @click="startRecognize()" class="voice-icon"></image>
<!-- #endif -->
<template v-if="isFocus">
<input maxlength="20" focus type="text" value="" confirm-type="search" @confirm="searchStart()" placeholder="請輸入關鍵詞搜索" v-model.trim="searchText"/>
</template>
<template v-else>
<input maxlength="20" type="text" value="" confirm-type="search" @confirm="searchStart()" placeholder="請輸入關鍵詞搜索" v-model.trim="searchText"/>
</template>
<image src="../../static/zy-search/search.svg" mode="aspectFit" @click="searchStart()" class="search-icon"></image>
</view>
<view :class="'s-' + theme" v-if="hList.length > 0">
<view class="header">
歷史記錄
<image src="../../static/zy-search/delete.svg" mode="aspectFit" @click="delhistory"></image>
</view>
<view class="list">
<view v-for="(item,index) in hList" :key="index" @click="keywordsClick(item)">{{item}}</view>
</view>
</view>
<view :class="'wanted-' + theme" v-if="showWant">
<view class="header">猜你想搜的</view>
<view class="list">
<view v-for="(item,index) in hotList" :key="index" @click="keywordsClick(item)">{{item}}</view>
</view>
</view>
</view>
</template>
<script>
export default{
name:"zy-search",
props:{
isFocus:{ //是否自動獲取焦點
type: Boolean,
default: false
},
theme:{ //選擇塊級顯示還是圓形顯示
type: String,
default: 'block'
},
showWant:{ //是否展示推薦菜單
type: Boolean,
default: false
},
hotList: { //推薦列表數據
type: Array,
default () {
return []
}
},
speechEngine: { //語音引擎=>訊飛:iFly,百度:'baidu'
type: String,
default: 'baidu'
}
},
data() {
return {
searchText:'', //搜索關鍵詞
hList:uni.getStorageSync('search_cache') //歷史記錄
};
},
methods: {
//觸發搜索 並加入歷史記錄
searchStart: function() {
let _this = this;
// 判斷輸入值為空的情況
if (_this.searchText == '') {
uni.showToast({
title: '請輸入關鍵字',
icon: 'none',
duration: 1000
});
}else{
// 通知父組件進行搜索
_this.$emit('getSearchText', _this.searchText);
// 從本地緩存中異步獲取指定 key 對應的內容。
uni.getStorage({
key:'search_cache',
success(res){
let list = res.data;
if(list.length > 5){
// 如果數組的長度大於5
// 循環該數組
for(let item of list){
// 如果數組中有一項和當前輸入框中輸入的值相等的話,終止操作流程
if(item == _this.searchText){
return;
}
}
// 刪除數組最后一項
list.pop();
// 在數組最前面增加一項
list.unshift(_this.searchText);
}else{
// 如果數組的長度小於5
// 循環該數組
for(let item of list){
// 如果數組中有一項和當前輸入框中輸入的值相等的話,終止操作流程
if(item == _this.searchText){
return;
}
}
// 在數組最前面增加一項
list.unshift(_this.searchText);
}
// 將當前新數組賦值給列表上顯示
_this.hList = list;
// 向本地緩存中異步存儲指定 key 對應的內容。
uni.setStorage({
key: 'search_cache',
data: _this.hList
});
},
fail() {
// 如果 從本地緩存中異步獲取指定 key 對應的內容 失敗
// 清空列表上顯示的數組
_this.hList = [];
// 將當前輸入的值給列表上顯示的數組
_this.hList.push(_this.searchText);
// 向本地緩存中異步存儲指定 key 對應的內容。
uni.setStorage({
key: 'search_cache',
data: _this.hList
});
// 通知父組件進行搜索 (這里的搜索多余了)
_this.$emit('getSearchText', _this.searchText);
}
})
}
},
// 關鍵詞點擊的事件 關鍵詞搜索與歷史搜索
keywordsClick (item) {
// 將點擊的關鍵詞內容賦值給搜索的變量
this.searchText = item;
//觸發搜索事件
this.searchStart();
},
//清空歷史記錄
delhistory () {
this.hList = [];
uni.setStorage({
key: 'search_cache',
data: []
});
},
//語音輸入
startRecognize: function() {
let _this = this;
let options = {};
// /語音引擎=>訊飛:iFly,百度:'baidu' 父級傳入的 默認baidu
options.engine = _this.speechEngine;
// 是否需要標點符號
options.punctuation = false;
// 語音識別超時時間
options.timeout = 10 * 1000;
// 啟動語音識別
// 啟動語音識別時調用,當語音識別成功后通過successCallback回調返回識別出文本內容,調用語音識別失敗則通過errorCallback回調返回。
// plus.speech.startRecognize( options, successCB, errorCB );
// options: ( SpeechRecognizeOption ) 必選 語音識別參數,用於控制語音引擎的各種技術參數
// successCB: ( RecognitionSuccessCallback ) 可選 語音識別成功回調
// 當語音識別引擎識別數據成功時的回調函數,並返回識別出的文本內容。
// errorCB: ( RecognitionErrorCallback ) 可選 語音識別失敗時的回調函數
// 當語音識別引擎識別數據失敗時的回調函數,並返回失敗的錯誤信息。
plus.speech.startRecognize(options, function(s) {
// 將語言識別到的內容拼接上輸入框中的內容賦值給輸入框
_this.searchText = _this.searchText + s;
});
}
}
}
</script>
<style lang="less" scoped>
.search{
width: 640upx;
margin: 30upx auto 0;
position: relative;
input{
background-color: #F7F7F7;
padding: 10upx 74upx;
font-size: 28upx;
border-radius: 50upx;
}
.voice-icon{
width: 36upx;
height: 36upx;
padding: 16upx 20upx 16upx 0;
position: absolute;
left: 16upx;
top: 4upx;
z-index: 10;
}
.search-icon{
width: 36upx;
height: 36upx;
padding: 16upx 20upx 16upx 0;
position: absolute;
right: 0;
top: -2upx;
z-index: 10;
}
}
.s-block{
margin-top: 30upx;
.header{
font-size: 32upx;
padding: 30upx;
position: relative;
image{
width: 36upx;
height: 36upx;
padding: 10upx;
position: absolute;
right: 40upx;
top: 24upx;
}
}
.list{
display: flex;
flex-wrap: wrap;
view{
width: 50%;
color: #8A8A8A;
font-size: 28upx;
box-sizing: border-box;
text-align: center;
padding: 20upx 0;
border-top: 2upx solid #FFF;
border-left: 2upx solid #FFF;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
background-color: #F7F7F7;
}
}
}
.s-circle{
margin-top: 30upx;
.header{
font-size: 32upx;
padding: 30upx;
border-bottom: 2upx solid #F9F9F9;
position: relative;
image{
width: 36upx;
height: 36upx;
padding: 10upx;
position: absolute;
right: 40upx;
top: 24upx;
}
}
.list{
display: flex;
flex-wrap: wrap;
padding: 0 30upx 20upx;
view{
padding: 8upx 30upx;
margin: 20upx 30upx 0 0;
font-size: 28upx;
color: #8A8A8A;
background-color: #F7F7F7;
box-sizing: border-box;
text-align: center;
border-radius: 20upx;
}
}
}
.wanted-block{
margin-top: 30upx;
.header{
font-size: 32upx;
padding: 30upx;
}
.list{
display: flex;
flex-wrap: wrap;
view{
width: 50%;
color: #8A8A8A;
font-size: 28upx;
box-sizing: border-box;
text-align: center;
padding: 20upx 0;
border-top: 2upx solid #FFF;
border-left: 2upx solid #FFF;
background-color: #F7F7F7;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.wanted-circle{
margin-top: 30upx;
.header{
font-size: 32upx;
padding: 30upx;
}
.list{
display: flex;
flex-wrap: wrap;
padding: 0 30upx 20upx;
view{
padding: 8upx 30upx;
margin: 20upx 30upx 0 0;
font-size: 28upx;
color: #8A8A8A;
background-color: #F7F7F7;
box-sizing: border-box;
text-align: center;
border-radius: 20upx;
}
}
}
</style>
父組件使用:
<template>
<view>
<zy-search :is-focus="true" :theme="themeClass" :show-want="true" :hot-list="hotList" @getSearchText="getSearchText"></zy-search>
</view>
</template>
<script>
import zySearch from './zy-search/zy-search.vue';
export default {
components: {
zySearch
},
data() {
return {
themeClass: 'circle',
hotList: [] //初始化推薦列表
};
},
onShow() {
this.getHotSearch();
},
methods: {
getHotSearch() {
this.http('', {}).then(res => {
if (res.success) {
this.hotList = []
res.data.hottest_list.map((item, index) => {
this.hotList.push(item.content);
});
} else {
}
});
},
getSearchText(e) {
uni.navigateTo({
url: '/pagesCourse/index?keyWords=' + e
});
}
}
};
</script>