You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
370 lines
8.4 KiB
370 lines
8.4 KiB
2 years ago
|
<template>
|
||
|
<view class="uni-combox">
|
||
|
<view class="uni-combox-dialog" v-if="isDialogInput && showSelector">
|
||
|
<view class="uni-combox-input">
|
||
|
<input
|
||
|
class="uni-combox__input"
|
||
|
type="text"
|
||
|
:placeholder="selectItem.value ? selectItem.value : placeholder"
|
||
|
v-model="inputVal"
|
||
|
@input="onInput"
|
||
|
@blur="onBlur"
|
||
|
:focus="'true'"
|
||
|
style="height: 90upx;"
|
||
|
/>
|
||
|
<uni-icons style="line-height: 90upx;" class="uni-combox__input-arrow" type="close" size="14" @click="clearSelector"></uni-icons>
|
||
|
<view class="uni-combox__selector">
|
||
|
<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
|
||
|
<view class="uni-combox__selector-empty" v-if="isLoading"><text>数据加载...</text></view>
|
||
|
<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0 && !isLoading">
|
||
|
<text>{{ emptyTips }}</text>
|
||
|
</view>
|
||
|
<view v-if="!isLoading" class="uni-combox__selector-item" v-for="(item, index) in filterCandidates" :key="index" @click="onSelectorClick(item)">
|
||
|
<view class="addr_title">
|
||
|
<text>{{ item.value }}</text>
|
||
|
</view>
|
||
|
<view class="detail_info">
|
||
|
<text>{{item.detail}}</text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</scroll-view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view v-if="label" class="uni-combox__label" :style="labelStyle">
|
||
|
<text>{{ label }}</text>
|
||
|
</view>
|
||
|
<view class="uni-combox__input-box">
|
||
|
<input
|
||
|
v-if="isDialogInput"
|
||
|
class="uni-combox__input"
|
||
|
type="text"
|
||
|
:placeholder="selectItem.value ? selectItem.value : placeholder"
|
||
|
v-model="inputVal"
|
||
|
@focus="onFocus"
|
||
|
/>
|
||
|
<input
|
||
|
v-else
|
||
|
class="uni-combox__input"
|
||
|
type="text"
|
||
|
:placeholder="selectItem.value ? selectItem.value : placeholder"
|
||
|
v-model="inputVal"
|
||
|
@focus="onFocus"
|
||
|
@input="onInput"
|
||
|
@blur="onBlur"
|
||
|
/>
|
||
|
<uni-icons v-if="!showSelector && !inputVal" class="uni-combox__input-arrow" type="arrowdown" size="14" @click="toggleSelector"></uni-icons>
|
||
|
<uni-icons v-else class="uni-combox__input-arrow" type="close" size="14" @click="clearSelector"></uni-icons>
|
||
|
<view class="uni-combox__selector" v-if="!isDialogInput && showSelector">
|
||
|
<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
|
||
|
<view class="uni-combox__selector-empty" v-if="isLoading"><text>数据加载...</text></view>
|
||
|
<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0 && !isLoading">
|
||
|
<text>{{ emptyTips }}</text>
|
||
|
</view>
|
||
|
<view v-if="!isLoading" class="uni-combox__selector-item" v-for="(item, index) in filterCandidates" :key="index" @click="onSelectorClick(item)">
|
||
|
<view class="addr_title">
|
||
|
<text >{{ item.value }}</text>
|
||
|
</view>
|
||
|
<view class="detail_info">
|
||
|
<text >{{item.detail}}</text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</scroll-view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</view>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
import uniIcons from '../uni-icons/uni-icons.vue';
|
||
|
/**
|
||
|
* Combox 组合输入框
|
||
|
* @description 组合输入框一般用于既可以输入也可以选择的场景
|
||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=1261
|
||
|
* @property {String} label 左侧文字
|
||
|
* @property {String} labelWidth 左侧内容宽度
|
||
|
* @property {String} placeholder 输入框占位符
|
||
|
* @property {Array} candidates 候选项列表
|
||
|
* @property {String} emptyTips 筛选结果为空时显示的文字
|
||
|
* @property {String} value 组合框的值
|
||
|
* @property {String} isDialogInput 弹出框输入和选择避免键盘遮挡
|
||
|
*/
|
||
|
export default {
|
||
|
name: 'uniCombox',
|
||
|
components: {
|
||
|
uniIcons
|
||
|
},
|
||
|
props: {
|
||
|
label: {
|
||
|
type: String,
|
||
|
default: ''
|
||
|
},
|
||
|
labelWidth: {
|
||
|
type: String,
|
||
|
default: 'auto'
|
||
|
},
|
||
|
placeholder: {
|
||
|
type: String,
|
||
|
default: ''
|
||
|
},
|
||
|
candidates: {
|
||
|
type: Array,
|
||
|
default() {
|
||
|
return [];
|
||
|
}
|
||
|
},
|
||
|
emptyTips: {
|
||
|
type: String,
|
||
|
default: '无匹配项'
|
||
|
},
|
||
|
value: {
|
||
|
type: String,
|
||
|
default: ''
|
||
|
},
|
||
|
isDialogInput: {
|
||
|
type: Boolean,
|
||
|
default: false
|
||
|
}
|
||
|
},
|
||
|
data() {
|
||
|
return {
|
||
|
showSelector: false,
|
||
|
inputVal: '',
|
||
|
isLoading: false,
|
||
|
showDowm: true,
|
||
|
selectItem: { key: '', value: '' }
|
||
|
};
|
||
|
},
|
||
|
computed: {
|
||
|
labelStyle() {
|
||
|
if (this.labelWidth === 'auto') {
|
||
|
return {};
|
||
|
}
|
||
|
return {
|
||
|
width: this.labelWidth
|
||
|
};
|
||
|
},
|
||
|
filterCandidates() {
|
||
|
return this.candidates.filter(item => {
|
||
|
return item;
|
||
|
});
|
||
|
},
|
||
|
filterCandidatesLength() {
|
||
|
return this.filterCandidates.length;
|
||
|
}
|
||
|
},
|
||
|
onLoad() {
|
||
|
if(this.value){
|
||
|
this.inputVal = this.value
|
||
|
console.log("这里是组件内部值为:"+this.inputVal)
|
||
|
}
|
||
|
},
|
||
|
watch: {
|
||
|
value: {
|
||
|
handler(newVal) {
|
||
|
if(newVal) {
|
||
|
this.candidates.filter(item => {
|
||
|
if (this.value === item.key) {
|
||
|
this.selectItem = item;
|
||
|
this.inputVal = item.value;
|
||
|
}
|
||
|
});
|
||
|
}else {
|
||
|
this.selectItem = { key: '', value: '' }
|
||
|
this.inputVal = ''
|
||
|
}
|
||
|
},
|
||
|
immediate: true
|
||
|
},
|
||
|
candidates: {
|
||
|
handler(newVal) {
|
||
|
this.isLoading = false;
|
||
|
},
|
||
|
immediate: true
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
toggleSelector() {
|
||
|
this.showSelector = !this.showSelector;
|
||
|
},
|
||
|
clearSelector() {
|
||
|
console.log('这里是组件里清除')
|
||
|
this.selectItem = { key: '', value: '' };
|
||
|
this.inputVal = '';
|
||
|
this.showSelector = false;
|
||
|
this.isLoading = false;
|
||
|
this.$emit('clear');
|
||
|
},
|
||
|
onFocus() {
|
||
|
console.log('这里是是input foucus')
|
||
|
if (this.inputVal) {
|
||
|
this.showSelector = false;
|
||
|
// this.inputVal = '';
|
||
|
}
|
||
|
},
|
||
|
// 组件input失焦
|
||
|
onBlur() {
|
||
|
console.log('这里是input失焦')
|
||
|
// this.showSelector = !this.showSelector;
|
||
|
setTimeout(() => {
|
||
|
if (this.selectItem.value) {
|
||
|
console.log('这里是this.selectItem.value',this.selectItem.value)
|
||
|
const selectItem = JSON.parse(JSON.stringify(this.selectItem));
|
||
|
this.inputVal = selectItem.value;
|
||
|
} else {
|
||
|
console.log('123')
|
||
|
this.inputVal = '';
|
||
|
}
|
||
|
// this.showSelector = false;
|
||
|
}, 50);
|
||
|
},
|
||
|
onSelectorClick(item) {
|
||
|
console.log('这类似组件内',item)
|
||
|
this.selectItem = item;
|
||
|
this.inputVal = item.value;
|
||
|
this.showSelector = false;
|
||
|
this.$emit('select', item.key, item);
|
||
|
},
|
||
|
onInput() {
|
||
|
console.log('这类似inputchange',this.inputVal)
|
||
|
setTimeout(() => {
|
||
|
if (this.inputVal && this.inputVal!=='') {
|
||
|
this.isLoading = true;
|
||
|
this.showSelector = true;
|
||
|
this.$emit('input', this.inputVal);
|
||
|
} else {
|
||
|
this.isLoading = false;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
|
||
|
.uni-combox {
|
||
|
/* #ifndef APP-NVUE */
|
||
|
display: flex;
|
||
|
/* #endif */
|
||
|
height: 40upx;
|
||
|
flex-direction: row;
|
||
|
align-items: center;
|
||
|
// border-bottom: solid 1px #DDDDDD;
|
||
|
}
|
||
|
|
||
|
.uni-combox__label {
|
||
|
font-size: 28upx;
|
||
|
line-height: 40upx;
|
||
|
padding-right: 10px;
|
||
|
color: #999999;
|
||
|
}
|
||
|
|
||
|
.uni-combox__input-box {
|
||
|
position: relative;
|
||
|
/* #ifndef APP-NVUE */
|
||
|
display: flex;
|
||
|
/* #endif */
|
||
|
flex: 1;
|
||
|
width: 320rpx;
|
||
|
flex-direction: row;
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
.uni-combox__input {
|
||
|
flex: 1;
|
||
|
font-size: 28upx;
|
||
|
}
|
||
|
|
||
|
.uni-combox__input-arrow {
|
||
|
padding-left: 15upx;
|
||
|
padding-right: 15upx;
|
||
|
}
|
||
|
|
||
|
.uni-combox__selector {
|
||
|
box-sizing: border-box;
|
||
|
position: absolute;
|
||
|
top: 40px;
|
||
|
left: -145rpx;
|
||
|
right: 150rpx;
|
||
|
width: 139%!important;
|
||
|
background-color: #FFFFFF;
|
||
|
border-radius: 6px;
|
||
|
box-shadow: #dddddd 4px 4px 8px, #dddddd -4px -4px 8px;
|
||
|
z-index: 2222;
|
||
|
}
|
||
|
|
||
|
.uni-combox__selector-scroll {
|
||
|
max-height: 550upx;
|
||
|
box-sizing: border-box;
|
||
|
}
|
||
|
|
||
|
.uni-combox__selector::before {
|
||
|
content: '';
|
||
|
position: absolute;
|
||
|
width: 0;
|
||
|
height: 0;
|
||
|
border-bottom: solid 6px #ffffff;
|
||
|
border-right: solid 6px transparent;
|
||
|
border-left: solid 6px transparent;
|
||
|
left: 50%;
|
||
|
top: -6px;
|
||
|
margin-left: -6px;
|
||
|
}
|
||
|
|
||
|
.uni-combox__selector-empty,
|
||
|
.uni-combox__selector-item {
|
||
|
/* #ifdef APP-NVUE */
|
||
|
display: flex;
|
||
|
/* #endif */
|
||
|
line-height: 40upx;
|
||
|
font-size: 28upx;
|
||
|
text-align: center;
|
||
|
border-bottom: solid 1px #dddddd;
|
||
|
padding: 4rpx 9rpx;
|
||
|
margin: 0px 10px;
|
||
|
}
|
||
|
.uni-combox__selector-empty{
|
||
|
line-height: 70upx;
|
||
|
}
|
||
|
|
||
|
.uni-combox__selector-empty:last-child,
|
||
|
.uni-combox__selector-item:last-child {
|
||
|
border-bottom: none;
|
||
|
}
|
||
|
.uni-combox-dialog {
|
||
|
background: #cccccc7d;
|
||
|
position: absolute;
|
||
|
left: 0;
|
||
|
right: 0;
|
||
|
top: 0;
|
||
|
bottom: 0;
|
||
|
z-index: 100;
|
||
|
}
|
||
|
.uni-combox-input {
|
||
|
background: #fff;
|
||
|
display: -webkit-box;
|
||
|
display: -webkit-flex;
|
||
|
display: flex;
|
||
|
height: 90upx;
|
||
|
}
|
||
|
|
||
|
.addr_title{
|
||
|
font-weight: 700;
|
||
|
font-size: 30rpx;
|
||
|
text-align: left;
|
||
|
margin-bottom: 0;
|
||
|
padding-bottom: 0;
|
||
|
margin-top: 10rpx;
|
||
|
margin-left: 20rpx;
|
||
|
}
|
||
|
|
||
|
.detail_info{
|
||
|
text-align: left;
|
||
|
color: #CCCCCC;
|
||
|
margin-top: 0;
|
||
|
padding-top: 0;
|
||
|
font-size: 25rpx;
|
||
|
margin-left: 20rpx;
|
||
|
}
|
||
|
</style>
|