Vue type:ahead 구성 요소 기능 구현(매우 신뢰 할 수 있 음)
이전에 그 typeahead 는 너무 일찍 써 서 현재 의 업무 수 요 를 만족 시 키 지 못 했다.
그리고 하 자 도 있 고 데이터 와 응답 데이터 가 들 어 오기 도 불편 합 니 다.
그래서 밀 어 버 리 고 다시 V2 버 전 을 썼 습 니 다.
그림 을 보고 세부 적 인 고려 가 많아 졌 다.실현 되 는 논리 코드 를 간소화 하 였 다.
효과 도
실 현 된 기능
1:드 롭 다운 상자 이외 의 영역 을 마우스 로 클릭 하여 드 롭 다운 상 자 를 닫 습 니 다.
2:키보드 상하 키 선택 지원,마우스 선택 지원
3:지원 목록 필터 검색
4:외부 전송 목록 JSON 형식의 맵 지원
5:placeholder 의 전송 지원
6:선택 한 대상 의 응답(.sync vue 2.3 구성 요소 통신 의 문법 사탕)
7:화살표 icon 의 맵,느낌 이 크 지 않 습 니 다.제거 되 었 습 니 다.
사용법
<select-search
style="max-width:195px"
placeholder=" "
:asyncData.sync="adHostData"
:mapData="adHostDataList"
:mapDataFormat="{label:'userName',value:'userId'}">
</select-search>
mapData:목록 값 맵코드
selectSearch.vue
<template>
<div class="select-search" v-if="typeaheadData" ref="selectSearch" @click.native="showHideMenu($event)">
<div class="select-header">
<input type="text" autocomplete="off" readonly :placeholder="placeholder" :value="placeholderValue" @keydown.down.prevent="selectChildWidthArrowDown" @keydown.up.prevent="selectChildWidthArrowUp" @keydown.enter="selectChildWidthEnter">
<i class="fzicon " :class="isExpand?'fz-ad-jiantou1':'fz-ad-jiantou'"></i>
</div>
<div class="select-body" v-if="isExpand && typeaheadData">
<input type="text" placeholder=" " v-model="searchVal" autocomplete="off" @keydown.esc="resetDefaultStatus" @keydown.down.prevent="selectChildWidthArrowDown" @keydown.up.prevent="selectChildWidthArrowUp" @keydown.enter="selectChildWidthEnter">
<transition name="el-fade-in-linear" mode="out-in">
<div class="typeahead-filter">
<transition-group tag="ul" name="el-fade-in-linear" v-show="typeaheadData.length>0">
<li v-for="(item,index) in typeaheadData" :key="index" :class="item.active ? 'active':''" @mouseenter="setActiveClass(index)" @mouseleave="setActiveClass(index)" @click="selectChild(index)">
<a href="javascript:;" rel="external nofollow" >
{{item[mapDataFormat.label]}}
</a>
</li>
</transition-group>
<p class="noFound" v-show="typeaheadData && typeaheadData.length === 0"> , !</p>
</div>
</transition>
</div>
</div>
</template>
<script>
export default {
name: 'selectSearch',
data: function () {
return {
placeholderValue: '',//
isExpand: false,
searchVal: '', //
resultVal: '', //
searchList: [], //
currentIndex: -1, // index,
}
},
computed: {
mapFormatData () { // mapData
return this.mapData.map(item => {
item[this.mapDataFormat.value] = item[this.mapDataFormat.value];
return item;
});
},
typeaheadData () {
let temp = [];
if (this.searchVal && this.searchVal === '') {
return this.mapFormatData;
} else {
this.currentIndex = -1; //
this.mapFormatData.map(item => {
if (item[this.mapDataFormat.label].indexOf(this.searchVal.toLowerCase().trim()) !== -1) {
temp.push(item)
}
return item;
})
return temp;
}
}
},
props: {
placeholder: {
type: String,
default: '-- --'
},
emptyText: {
type: String,
default: ' '
},
mapData: { //
type: Array,
default: function () {
return []
}
},
mapDataFormat: { //
type: Object,
default: function () {
return {
label: 'text',
value: 'value',
extraText: 'extraText'
}
}
},
asyncData: { //
type: [Object, String],
default: function () {
return {}
}
}
},
methods: {
showHideMenu (e) { //
if (e) {
if (this.$refs.selectSearch && this.$refs.selectSearch.contains(e.target)) {
this.isExpand = true;
} else {
this.isExpand = false;
}
}
},
resetDefaultStatus () { //
this.searchVal = '';
this.currentIndex = -1;
this.typeaheadData.map(item => {
this.$delete(item, 'active');
})
},
setActiveClass (index) { //
this.typeaheadData.map((item, innerIndex) => {
if (index === innerIndex) {
this.$set(item, 'active', true);
this.currentIndex = index; // index, ,
} else {
this.$set(item, 'active', false)
}
})
},
selectChildWidthArrowDown () {
// index
if (this.currentIndex < this.typeaheadData.length) {
this.currentIndex++;
this.typeaheadData.map((item, index) => {
this.currentIndex === index ? this.$set(item, 'active', true) : this.$set(item, 'active', false);
})
}
},
selectChildWidthArrowUp () {
// index
if (this.currentIndex > 0) {
this.currentIndex--;
this.typeaheadData.map((item, index) => {
this.currentIndex === index ? this.$set(item, 'active', true) : this.$set(item, 'active', false);
})
}
},
selectChildWidthEnter () {
// ,
if (this.typeaheadData.length === 1) {
this.$emit('update:asyncData', this.typeaheadData[0]); // emit
this.placeholderValue = this.typeaheadData[0][this.mapDataFormat.label];
} else {
// ,
this.typeaheadData.map(item => {
if (this.searchVal === item[this.mapDataFormat.label] || item.active === true) {
this.$emit('update:asyncData', item); // emit
this.placeholderValue = item[this.mapDataFormat.label];
}
})
}
this.isExpand = false;
},
selectChild (index) {
//
this.typeaheadData.map((item, innerIndex) => {
if (index === innerIndex || item.active) {
this.placeholderValue = item[this.mapDataFormat.label];
this.$emit('update:asyncData', item); // emit
}
});
this.isExpand = false;
},
},
mounted () {
window.addEventListener('click', this.showHideMenu);
},
beforeDestroy () {
window.removeEventListener('click', this.showHideMenu);
},
watch: {
'isExpand' (newValue) {
if (newValue === false) {
this.resetDefaultStatus();
}
}
}
}
</script>
<style scoped lang="scss">
.el-fade-in-linear-enter-active,
.el-fade-in-linear-leave-active,
.fade-in-linear-enter-active,
.fade-in-linear-leave-active {
transition: opacity .2s linear;
}
.el-fade-in-enter,
.el-fade-in-leave-active,
.el-fade-in-linear-enter,
.el-fade-in-linear-leave,
.el-fade-in-linear-leave-active,
.fade-in-linear-enter,
.fade-in-linear-leave,
.fade-in-linear-leave-active {
opacity: 0;
}
.noFound {
text-align: center;
}
.select-search {
position: relative;
z-index: 1000;
a {
color: #333;
text-decoration: none;
padding: 5px;
}
ul {
list-style: none;
padding: 6px 0;
margin: 0;
max-height: 200px;
overflow-x: hidden;
overflow-y: auto;
li {
display: block;
width: 100%;
padding: 5px;
font-size: 14px;
padding: 8px 10px;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #48576a;
height: 36px;
line-height: 1.5;
box-sizing: border-box;
cursor: pointer;
&.active {
background-color: #20a0ff;
a {
color: #fff;
}
}
}
}
.select-header {
position: relative;
border-radius: 4px;
border: 1px solid #bfcbd9;
outline: 0;
padding: 0 8px;
>input {
border: none;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 100%;
outline: 0;
box-sizing: border-box;
color: #1f2d3d;
font-size: inherit;
height: 36px;
line-height: 1;
}
>i {
transition: all .3s linear;
display: inline-block;
position: absolute;
right: 3%;
top: 50%;
transform: translateY(-50%);
}
}
.select-body {
position: absolute;
border-radius: 2px;
background-color: #fff;
box-sizing: border-box;
margin: 5px 0;
padding: 8px;
width: 100%;
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
>input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
background-image: none;
border-radius: 4px;
border: 1px solid #bfcbd9;
box-sizing: border-box;
color: #1f2d3d;
font-size: inherit;
height: 36px;
line-height: 1;
outline: 0;
padding: 3px 10px;
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
width: 100%;
display: inline-block;
&:focus {
outline: 0;
border-color: #20a0ff;
}
}
}
}
</style>
총결산위 에서 말 한 것 은 소 편 이 소개 한 Vue 가 type:ahead 구성 요소 기능(매우 믿 을 수 있 습 니 다)을 실현 하 는 것 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 메 시 지 를 남 겨 주세요.소 편 은 제때에 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Fastapi websocket 및 vue 3(Composition API)1부: FastAPI virtualenv 만들기(선택 사항) FastAPI 및 필요한 모든 것을 다음과 같이 설치하십시오. 생성main.py 파일 및 실행 - 브라우저에서 이 링크 열기http://127.0.0.1:...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.