基於uni-app的通用搜索元件(歷史記錄,app語音輸入,搜尋推薦)解析 zy-search
阿新 • • 發佈:2021-12-31
基於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>