vue 简单的 diy 搜索框 封装,全局组件化,对象配置
1. 目录结构:
2. main.js 全局组件
3. KjfSearch.vue 代码:
-
<template> <div class="kjf_search_box" :class="kjfSearchConfig.className"> <label class="kjf_search_search_label clearfix"> <input autocomplete="off" readonly="readonly" type="text" :placeholder="kjfSearchConfig.placeholder" class="search_input" :ref="kjfSearchConfig.refName" v-model="kjfSearchKeyWord" @blur="kjfClearPreSearchData" @focus="kjfHandleSearchEvent" @keyup="kjfHandleSearchEvent($event)" /> <a class="search_by_inputs" href="javascript:" @click="kjfHandleSearchEvent('notEvent')">搜索</a> </label> <div class="search_list" v-show="kjfPreSearchData && kjfPreSearchData.length>0"> <ul class="search_result"> <li v-for="(item, index) in kjfPreSearchData" :key="item.index" :class="{active: index === kjfPreSearchItem}" @click="kjfChoosePreSearchItem(index, item.kjfShowSearchName)"> <span class="ellipsis" :title="item.kjfShowSearchName"> {{item.kjfShowSearchName}} </span> </li> </ul> </div> </div> </template> <script> import {getElementsByCss} from '../utils'; export default { name: 'KjfSearch', props: { kjfSearchConfig: { type: Object } }, data: function() { return { kjfSearchKeyWord: '', kjfPreSearchData: [], kjfPreSearchItem: -1 }; }, computed: { }, watch: { kjfPreSearchData: { deep: true, handler(newArr) { if (newArr && typeof this.kjfSearchConfig.watchCallBack === 'function') { return this.kjfSearchConfig.watchCallBack(newArr); } else { return []; } } } }, mounted() { }, methods: { kjfClearPreSearchData() { this.$refs[this.kjfSearchConfig.refName].setAttribute('readonly', 'readonly'); window.setTimeout(() => { this.kjfPreSearchData = []; this.kjfPreSearchItem = -1; }, 200); }, kjfChoosePreSearchItem(index, searchName) { this.kjfSearchKeyWord = searchName; this.kjfPreSearchData = []; this.kjfPreSearchItem = -1; }, async kjfHandleSearchEvent(e) { this.$refs[this.kjfSearchConfig.refName].removeAttribute('readonly'); if (e && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) { this.kjfPreSearchItem = e.key === 'ArrowUp' ? (this.kjfPreSearchItem - 1) : (this.kjfPreSearchItem + 1); if (this.kjfPreSearchItem < 0) { this.kjfPreSearchItem = -1; } if (this.kjfPreSearchItem >= this.kjfPreSearchData.length) { this.kjfPreSearchItem = this.kjfPreSearchData.length - 1; } console.log('.' + this.kjfSearchConfig.className + ' .search_list .search_result'); const ele = getElementsByCss('.' + this.kjfSearchConfig.className + ' .search_list .search_result')[0]; ele && ele.scrollTo(0, this.kjfPreSearchItem * 31); return; } // 上、下选择下拉选项 if (e && (e.key === 'Enter') && (this.kjfPreSearchItem !== -1)) { this.kjfSearchKeyWord = this.kjfPreSearchData[this.kjfPreSearchItem].kjfShowSearchName; this.kjfPreSearchData = []; this.kjfPreSearchItem = -1; this.$refs[this.kjfSearchConfig.refName].focus(); return; } // 回车选中下拉选项 if ((e === 'notEvent') || (e && e.key === 'Enter')) { window.setTimeout(async () => { this.kjfSearchConfig.enterCallBack(this.kjfSearchKeyWord); this.kjfPreSearchData = []; this.kjfPreSearchItem = -1; }, 200); this.$refs[this.kjfSearchConfig.refName].focus(); return; } // 回车键按下 或者 点击搜索按钮 window.clearTimeout(this.searchTimer); this.searchTimer = window.setTimeout(async () => { if (this.kjfSearchKeyWord && this.kjfSearchKeyWord.trim()) { if (!this.kjfSearchKeyWord) { this.kjfPreSearchData = []; return; } if (typeof this.kjfSearchConfig.getPreSearchData === 'function') { this.kjfPreSearchData = await this.kjfSearchConfig.getPreSearchData(this.kjfSearchKeyWord); } } else { this.kjfPreSearchData = []; this.kjfPreSearchItem = -1; } }, 200); // 防抖 搜索 } } }; </script> <style lang="stylus" rel="stylesheet/stylus" scoped> @import "../../common/stylus/mixins.styl" .kjf_search_box width 100% height 100% input height 30px text-indent 2px border-radius 4px outline none border 1px solid #DCDFE6 .kjf_search_search_label display block width 100% height 100% >input float left display block width 240px >a float right display block width 50px height 100% border-radius 4px box-sizing border-box padding 0 10px background-color $themeColor color #fff .search_list color $fontColor width 238px max-height 300px overflow hidden .search_result position absolute top 32px left 2px z-index 100 width 238px max-height 300px overflow-x hidden overflow-y auto border-radius 0 0 10px 10px background-color #eee padding 0 0 10px 10px box-sizing border-box >li width 180px margin-top 10px color $fontColor white-space normal word-break break-all word-wrap break-word &.active, &:hover cursor pointer >span color #fff background-color $themeColor >span color $fontColor </style>
4. 使用实例 主要代码:
-
<template> <div id="company_pay_record"> <ul class="company_pay_record_nav clearfix"> <li class="right_company_pay_record_search_li clearfix"> <KjfSearch :kjfSearchConfig="companyPayRecordSearchConfig"></KjfSearch> </li> </ul> </div> </template> <script> import {mapState} from 'vuex'; import {requestMyLabs} from '../../../../axios'; export default { name: 'CompanyPayRecord', props: { isPC: Boolean }, data () { return { companyPayRecordSearchConfig: { className: 'company_pay_record_search', refName: 'companyPayRecordSearch', placeholder: '请输入操作人/编号', kjfShowSearchName: 'expName', enterCallBack: async (searchKeyWord) => { searchKeyWord = searchKeyWord && searchKeyWord.trim(); await this.$store.dispatch('changeLabsCurPageNo', 1); await this.$store.dispatch('changeLabsKeyWord', searchKeyWord); let response = ' - 223 -'; try { response = await this.$store.dispatch('getMyLabs', this.myLabsData); // 获取 myLabsData } catch (e) { this.myConsole(e); this.myConsole('response 228: '); this.myConsole(response); } }, getPreSearchData: async (searchKeyWord) => { await this.$store.dispatch('changeLabsKeyWord', searchKeyWord); const response = await requestMyLabs(this.myLabsData); if (response.status === 200) { return response.data.data.content; } }, watchCallBack: (newArr) => { let i = 0; for (; i < newArr.length; i++) { newArr[i].kjfShowSearchName = newArr[i].experiment.experimentName; } return newArr; } } }; }, computed: { ...mapState({ myLabsData: state => state.myLabs.myLabsData }) }, async mounted () { this.initCompanyPayRecord(); }, methods: { async initCompanyPayRecord() { } } }; </script> <style lang="stylus" rel="stylesheet/stylus" scoped> @import "../../../../common/stylus/mixins.styl" #company_pay_record width 100% margin 0 auto .company_pay_record_nav width 100% padding-top 20px padding-bottom 20px box-sizing border-box >li float left width 200px height 30px margin-right 14px line-height 30px &:last-child margin-right 0 .right_company_pay_record_search_li float right position relative width 300px height 30px .my_labs_pagination margin-top 15px #company_pay_record_info.is_mobile width 100% px('font-size', 14) // 用于 mobile 设计图单位 </style>
5. 效果图:
--------小尾巴
________一个人欣赏-最后一朵颜色的消逝-忠诚于我的是·一颗叫做野的心.决不受人奴役.怒火中生的那一刻·终将结束...