说明:组件代码依赖uview-ui,请先安装uview
一、远程搜索效果展示

二、模糊搜索效果

三、代码
<template>
<view class="uni-combox" :class="border ? '' : 'uni-combox__no-border'">
<view v-if="!showInput" @click='showAda' class="cr-box">
<text class="cr-value" v-if="crVal">{{crVal}}</text>
<text class="cr-placeholder" v-else="crVal">{{placeholderV}}</text>
</view>
<view v-else class="uni-combox__input-box">
<u-input class="uni-combox__input" border='none' :disabled="disabled" type="textarea" :placeholder="placeholder"
v-model="inputVal" @change="change" @focus="onFocus" :focus='true' />
<u-icon :name="!showSelector ? 'arrow-down' : 'arrow-up'" @click='toggleSelector'></u-icon>
</view>
<view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" />
<view class="uni-combox__selector" v-if="showSelector">
<view class="uni-popper__arrow"></view>
<view style="display: flex;flex: 1;">
<ui-srcoll :isPull="false" @reachBottom='reachBottom'>
<view class="uni-combox__selector-empty" v-if="option.length === 0">
<text>{{emptyTips}}</text>
</view>
<view class="uni-combox__selector-item" v-for="(item,index) in option" :key="index"
@click="onSelectorClick(item,index)">
<text>{{formatItemName(item)}} </text>
</view>
</ui-srcoll>
</view>
</view>
</view>
</template>
<script>
/**
* Combox 组合输入框
* @description 组合输入框一般用于既可以输入也可以选择的场景
* @tutorial https://ext.dcloud.net.cn/plugin?id=1261
* @property {String} placeholder 输入框占位符
* @property {Array} data 候选项列表
* @property {String} emptyTips 筛选结果为空时显示的文字
* @property {String} value 组合框的值
*/
import UiSrcoll from '@/components/scroll/scroll.vue';
export default {
name: 'uniCombox',
components: {
UiSrcoll
},
emits: ['input', 'update:modelValue'],
props: {
type: {
type: String,
default: 'normal'
},
longrange: {
type: Boolean,
default: false
},
keyName: {
type: String,
default: 'text'
},
valueName: {
type: String,
default: 'value'
},
border: {
type: Boolean,
default: true
},
disabled: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ''
},
data: {
type: Array,
default () {
return []
}
},
emptyTips: {
type: String,
default: '无匹配项'
},
// #ifndef VUE3
value: {
type: [String, Number],
default: ''
},
// #endif
// #ifdef VUE3
modelValue: {
type: [String, Number],
default: '',
inputValkey: ''
},
// #endif
},
data() {
return {
showSelector: false,
showInput: false,
crVal: "",
option: [],
inputVal: '',
placeholderVal: ''
}
},
computed: {
valueCom() {
return this.value
},
placeholderV() {
return this.placeholder
}
},
watch: {
placeholder: {
handler(newv) {
this.placeholderVal = newv
},
immediate: true
},
data: {
handler(val, old) {
if ( Array.isArray(val) && old !== val) {
this.option = val;
this.initDefVal()
}
this.option = val;
},
immediate: true
},
inputVal(val, old) {
if (val !== this.crVal && val !== old && this.longrange) {
this.$emit('changeVal', val)
}
},
valueCom: {
handler(val, old) {
this.initDefVal()
},
}
},
methods: {
initDefVal() {
if (this.longrange)
{
this.crVal = this.valueCom
} else {
let defValue = '';
if ((this.valueCom || this.valueCom === 0)) defValue = this.valueCom
const def = this.data.find(item => item[this.valueName] === defValue);
this.crVal = def ? this.formatItemName(def) : this.valueCom;
}
},
fliterString(val){
const arr = this.data.filter((item) => { return item[this.keyName] && item[this.keyName].toString().indexOf(val) > -1 })
this.option = arr;
},
showAda() {
this.inputVal = this.crVal;
this.showInput = true
},
toggleSelector() {
this.showInput = false
this.showSelector = !this.showSelector;
this.inputVal = '';
},
onFocus() {
this.showSelector = true;
this.inputVal = this.crVal
if(!this.longrange){
this.option = this.data
}
},
onSelectorClick(item, index) {
this.$emit('input', item[this.valueName]);
this.$emit('change', item)
this.showSelector = false;
this.showInput = false
this.inputVal = ''
},
change(value) {
if(this.showSelector && !this.longrange){
this.fliterString(value)
}
},
formatItemName(item) {
return item[this.keyName]
},
reachBottom() {
if (this.longrange) {
this.$emit('reachBottom')
}
}
}
}
</script>
<style lang="scss" scoped>
.uni-combox {
font-size: 14px;
// border: 0.5px solid #DCDFE6;
border-radius: 4px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
// height: 40px;
flex-direction: row;
align-items: center;
flex: 1;
// border-bottom: solid 1px #DDDDDD;
}
.uni-combox__label {
font-size: 16px;
line-height: 22px;
padding-right: 10px;
// color: #999999;
}
.uni-combox__input-box {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
.uni-combox__input {
flex: 1;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.uni-combox__input-plac {
font-size: 14px;
// color: #999;
}
.uni-combox__selector {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: absolute;
top: calc(100% + 12px);
left: 0;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #EBEEF5;
border-radius: 6px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 9999999;
padding: 4px 0;
max-height: 400rpx;
overflow: hidden;
display: flex;
}
.uni-combox__selector-scroll {
/* #ifndef APP-NVUE */
max-height: 200px;
box-sizing: border-box;
/* #endif */
}
.cr-box{
line-height: 22px;
color: rgb(48, 49, 51);
font-size: 15px;
min-height: 22px;
width: 100%;
}
.cr-value{
flex: 1;
height: 100%;
}
.cr-placeholder{
color: rgb(192, 196, 204);
height: 100%;
}
.uni-combox__selector-empty,
.uni-combox__selector-item {
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
line-height: 36px;
font-size: 14px;
text-align: center;
// border-bottom: solid 1px #DDDDDD;
padding: 0px 10px;
}
.uni-combox__selector-item:hover {
background-color: #f9f9f9;
}
.uni-combox__selector-empty:last-child,
.uni-combox__selector-item:last-child {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
// picker 弹出层通用的指示小三角
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
.uni-combox__no-border {
border: none;
}
.uni-select--mask {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 2;
}
.scroll-box {
overflow: hidden;
height: 300rpx;
}
</style>