一、需求描述
直播源代码实现一个搜索框:
1、输入关键字,按键抬起后可以实现查询功能;
2、下拉列表展示搜索结果,搜索结果中关键字要用特殊颜色突出显示,默认选中搜索结果的第一项;
3、可以按上下键切换选中项,按回车输出选中项;
4、单击组件之外的地方可以收起下拉列表;
二、分析
直播源代码用到的插件 pinyin-match:
支持拼音全拼、简拼和汉字模糊匹配。
调用match方法,匹配成功返回数组([匹配到第一个字符的下标,匹配到最后一个字符的下标]),失败返回false。
三、代码实现
<template><div class="search-wrapper" @click.stop><div class="search-input" :class="{'is-focus':isFocus}"><!-- 搜索输入框 --><input type="text"autocomplete="off"autocorrect="off"autocapitalize="off"autofocusv-model="value"ref='searchInput'spellcheck="false"placeholder="搜索..."@click="changeInput"@keyup="clickSearch"@keydown.down.prevent="navigateOptions('next')"@keydown.up.prevent="navigateOptions('prev')"><!-- 搜索放大镜图标 --><div class="search-icons"><i class="el-icon-close" v-show="value" @click="clearSearchValue"></i> <i class="el-icon-search" v-show="!value" @click="clickSearch"></i></div> </div><!-- 搜索下拉列表 --><div class="search-list"><div class="search-result" ref="ul"><ul v-for="item in result"> <li class="select-li" :class="{'is-selected': item.isSelected}" @click="clickResultItem(item)"><img class="user-avatar" src="../../images/avatar.png"><div class="user-name"><span>{{item.keyword[0]}}<span class="keyword">{{item.keyword[1]}}</span>{{item.keyword[2]}}</span> </div></li></ul><!-- 查询结果为空时候 --><div class="result-null" v-if="value && result.length == 0">没有结果 </div> </div></div></div>
</template>
<script>
const PinyinMatch = require('pinyin-match');
export default {name: 'SearchInput',data () {return {isFocus: false,//搜索框是否获取了焦点value: '',//输入的搜索关键字result : [],//全部搜索结果curIndex: 0,//选中的搜索结果的下标selectItem: {},//选中的搜索结果userList: [{name:'安其拉',id:'a1'},{name:'安琪',id:'a2'},{name:'陈仙仙', id:'c1' },{name:'成小龙', id:'c2' },{name:'程野', id:'c3' },{name:'丁小明', id:'d1' },{name:'丁小龙', id:'d2' },{name:'丁小野', id:'d3' },{name:'冯小明', id:'f1' },{name:'冯小龙', id:'f2' },{name:'冯小野', id:'f3' },{name:'高小明', id:'g1' },{name:'高小龙', id:'g2' },{name:'高小野', id:'g3' },{name:'高小阳', id:'g4' },{name:'郭小名', id:'g5' },{name:'黄小明', id:'h1' },{name:'黄小龙', id:'h2' },{name:'黄小野', id:'h3' },{name:'郝小阳', id:'h4' },{name:'郝小名', id:'h5' },{name:'李小明',id:'l1'},{name:'李小龙',id:'l2'},{name:'李小野',id:'l3'},{name:'李小阳',id:'l4'},{name:'李小一',id:'l5'},{name:'李小二',id:'l6'},{name:'李小三',id:'l7'},{name:'李小四',id:'l8'},{name:'李小五',id:'l9'},] }},mounted () { let that = this; document.body.addEventListener('click', () => {//单击组件之外时收起下拉列表that.isFocus = false;that.value = "";}, false);},methods: {changeInput() {//单击搜索框时获取焦点this.isFocus = true; },clearSearchValue () {//清空输入的查询条件if (this.value) {this.value = "";//搜索关键字this.result = [];//全部搜索结果this.curIndex = 0;//选中的搜索结果的下标this.selectItem = {};//选中的搜索结果this.$refs.searchInput.focus();//输入查询条件清空后获取焦点}},clickSearch () {//查询if (event.code == "ArrowDown" || event.code == "ArrowUp") {//上下键return;} if (event.code == "Enter" && this.selectItem) {this.clickResultItem(this.selectItem);//有搜索结果时按下enter直接选中第一项return;}this.result = [];//全部搜索结果this.curIndex = 0;//选中的搜索结果的下标this.selectItem = {};//选中的搜索结果if (this.value.trim()) { for (let i = 0; i < this.userList.length; i++) {//根据所有人员名字匹配let name = this.userList[i].name;let search = PinyinMatch.match(name, this.value); if (search && search.length > 0) { this.userList[i].keyword = this.getKeyWord(name, search);this.userList[i].isSelected = false; this.result.push(this.userList[i]); }}if (this.result && this.result[0]) {this.result[0].isSelected = true;this.curIndex = 0;//选中的搜索结果的下标this.selectItem = this.result[0];//选中的搜索结果} } else {this.$refs.searchInput.focus();//没有输入查询条件焦点不应该失去}},getKeyWord (name, search) {//关键字let keyword = [];keyword[0] = name.substring(0, search[0]);if (search[0] == search[1]) {keyword[1] = name.charAt(search[0]);//匹配到的作为关键字keyword[2] = name.substring(search[0] + 1);} else {keyword[1] = name.substring(search[0], search[1] + 1);//匹配到的作为关键字keyword[2] = name.substring(search[1] + 1);}return keyword;}, clickResultItem (data) {//单击下拉列表中的选项 alert('您选择了' + data.name);},navigateOptions (direction) { //搜索结果上下键选择let resultLength = this.result.length;if (resultLength == 0 || resultLength == 1 ) {return;}let lastIndex = this.curIndex;if (direction === 'next') {//向下 this.curIndex++; if (this.curIndex === resultLength) {this.curIndex = 0;} }if (direction === 'prev') {//向上this.curIndex--;if (this.curIndex < 0) { this.curIndex = resultLength - 1;}}if (lastIndex < resultLength) { this.result[lastIndex].isSelected = false;}if (-1 < this.curIndex < resultLength) {this.selectItem = this.result[this.curIndex];this.selectItem.isSelected = true; // this.result[this.curIndex].isSelected = true; this.$set(this.result,this.curIndex,this.selectItem) }this.resetScrollTop();},resetScrollTop () { //设置滚动条的位置let maxShowCount = this.$refs.ul.clientHeight / 50;let diff = this.curIndex - maxShowCount; diff += 4; this.$refs.ul.scrollTop = diff * (50) + 60;},}
}
</script>
<style src="./index.scss" lang="scss"></style>
index.scss
.search-wrapper {background-color: rgb(235, 148, 148);height: 580px;width:325px; overflow: hidden;border-radius: 3px;font-family: 'Microsoft YaHei';box-sizing: border-box;padding: 10px;.search-input{position: relative;input {width: 100%;height: 35px;box-sizing: border-box;border: 0;border-radius: 3px;margin: 0;padding: 0px 40px 0px 10px;outline: 0;font-size: 14px;color: #E5EAEE;// background-color: #209df7;background-color: #fff;}input::-webkit-input-placeholder {color: #E5EAEE;}.search-icons {position: absolute;top: 7px;right: 10px;color: #209df7;font-size: 17px;}}.is-focus {overflow: visible !important;input {color: #222 !important;// border: 1px solid #fff !important;// background-color: #fff !important;box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2), 0 1px 15px hsla(0, 0%, 100%, .3) !important;}.search-icons {.el-icon-close{color: #111;background-color: #d8d8d8;border-radius: 50%;padding: 2px;box-sizing: border-box;font-size: 15px;cursor: pointer;}.el-icon-close:hover{color: #fff;background-color: #209df7;}}}.search-list{height: 480px;margin-top: 8px;border: 0 none; border-radius: 3px;box-shadow: 0 0 6px rgba(0, 0, 0, .15); background-color: #fff; .search-result {height: 100%;overflow-x: hidden;overflow-y: auto;ul, li{margin: 0px;padding: 0px;}li{list-style-type: none;}.select-li {cursor: pointer;padding: 8px 10px;overflow: hidden;border-bottom: 1px solid #ebebeb;.user-avatar {border-radius: 50%;float: left;width: 40px;height: 40px;background-size: cover;}.user-name {width: 200px;float: left;margin-left: 15px;line-height: 40px;.keyword {color: #008cee;}} }.select-li:hover{background-color: #e5f0fa;}.is-selected {background-color: rgba(140, 197, 255, 0.46)}}.result-null {margin-top: 100px;text-align: center;font-size: 20px;}}
}::-webkit-scrollbar {width: 6px;height: 6px;
}
/*滑动轨道*/
::-webkit-scrollbar-track-piece {background-color: transparent;-webkit-border-radius: 6px;
}
// 下面是滑块
/*竖向滚动条*/
::-webkit-scrollbar-thumb:vertical {height: 5px;background-color: rgb(161, 161, 161);-webkit-border-radius: 6px;
}
/*横向滚动条*/
::-webkit-scrollbar-thumb:horizontal {width: 5px;background-color: rgb(143, 144, 145);-webkit-border-radius: 6px;
}
三、效果
以上直播源代码就实现了一个简单的搜索框。
下面对直播源代码的搜索过程进行简单的优化:
在data中添加 timer:null
将搜索过程放入 setTimeout
函数中,按键抬起半秒后再去进行搜索。
clickSearch () {//查询if (event.code == "ArrowDown" || event.code == "ArrowUp") {//上下键return;} if (event.code == "Enter" && this.selectItem) {this.clickResultItem(this.selectItem);//有搜索结果时按下enter直接选中第一项return;}this.result = [];//全部搜索结果this.curIndex = 0;//选中的搜索结果的下标this.selectItem = {};//选中的搜索结果if (this.value.trim()) { clearTimeout(this.timer);this.timer = setTimeout(function () {for (let i = 0; i < this.userList.length; i++) {//根据所有人员名字匹配let name = this.userList[i].name;let search = PinyinMatch.match(name, this.value); if (search && search.length > 0) { this.userList[i].keyword = this.getKeyWord(name, search);this.userList[i].isSelected = false; this.result.push(this.userList[i]); }}if (this.result && this.result[0]) {this.result[0].isSelected = true;this.curIndex = 0;//选中的搜索结果的下标this.selectItem = this.result[0];//选中的搜索结果} }, 500) } else {this.$refs.searchInput.focus();//没有输入查询条件焦点不应该失去}},
本文由云豹科技转发自五花漏博客,如有侵权请联系作者删除