基于uni-app的通用搜索组件(历史记录,app语音输入,搜索推荐)解析 zy-search

一个通用的搜索组件,包含搜索历史记录,语音输入,搜索推荐功能

插件内容:

<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>

语音识别相关内容参考:

https://www.dcloud.io/docs/api/zh_cn/speech.html