配合PC端用户交互逻辑,移动端需要有类似el-select交互的选择器,即主要功能考虑为:多选,单选,过滤,远程搜索。
为了延续el-select的代码逻辑,可以将我们的选择器分成两部分:选项item和外面处理层。
<template>
<van-cell v-show="visible" clickable :title="currentLabel" @click="toggle()">
<template #right-icon>
<van-checkbox ref="checkboxes" :name="currentValue" @click="clickItem">
<template #icon="props">
<van-icon v-if="props.checked" name="success" />
<span v-else></span>
</template>
</van-checkbox>
</template>
</van-cell>
</template>
<script>
export default {
name: 'VantSelect',
props: {
value: {
required: true
},
label: {
type: [String, Number],
default: ''
},
created: Boolean,
disabled: {
type: Boolean,
default: false
}
},
inject: ['select'],
data() {
return {
visible: true
};
},
computed: {
isObject() {
return Object.prototype.toString.call(this.value).toLowerCase() === '[object object]';
},
currentLabel() {
return this.label || (this.isObject ? '' : this.value);
},
currentValue() {
return this.value || this.label || '';
}
},
created() {
this.select.checkBox.push(this);
},
methods: {
escapeRegexpString(value = '') {
return String(value).replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
},
queryChange(query) {
this.visible = new RegExp(this.escapeRegexpString(query), 'i').test(this.currentLabel);
},
clickItem(item) {
this.$emit('click', item);
},
toggle() {
if (!this.select.multiple) this.select.clear();
this.$refs.checkboxes.toggle();
}
}
};
</script>
<style></style>
<!--
* @Author:Min Huang
* @Date: 2023/6/1
* @Desc: 选择器
-->
<template>
<div class="vant-select">
<van-popup
v-model="showPicker"
position="bottom"
round
:style="{ height: '45%' }"
:close-on-click-overlay="false"
@click-overlay="close"
>
<div>
<van-search v-if="remote || filterable" v-model="searchText" placeholder="搜索" @input="onSearch"> </van-search>
<div class="btn-nav">
<span @click="close">取消</span>
<span @click="onConfirm">确认</span>
</div>
<div :style="{ height: filterable ? 'calc(45vh - 2.2rem)' : 'calc(45vh - 1rem)', overflowY: 'scroll' }">
<van-checkbox-group
ref="checkBoxGroup"
v-bind="Object.assign($attrs, bindsData)"
v-on="$listeners"
@change="changeItem"
>
<van-cell-group>
<slot :data="{ multiple }"></slot>
</van-cell-group>
</van-checkbox-group>
</div>
</div>
</van-popup>
</div>
</template>
<script>
export default {
name: 'VantSelect',
provide() {
return {
select: this
};
},
props: {
showPicker: {
type: Boolean,
default: false
},
filterable: {
type: Boolean,
default: false
},
//是否为远程搜索
remote: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: false
}
},
data() {
return {
searchText: '',
checkBox: []
};
},
computed: {
bindsData() {
if (!this.multiple) {
return {
max: 1
};
}
return null;
}
},
watch: {},
methods: {
changeItem(item) {
this.$emit('change', item);
},
/**
* 搜索时
*/
onSearch() {
if (this.remote) {
this.$emit('search', this.searchText);
} else if (this.filterable) {
this.checkBox.forEach(item => item.queryChange(this.searchText));
}
},
/**
* 确认回调
* @param value
*/
onConfirm() {
this.searchText = '';
this.clear();
this.$emit('update:showPicker', false);
this.$emit('confirm', this.$refs.checkBoxGroup.value);
},
close() {
this.searchText = '';
this.clear();
this.$emit('update:showPicker', false);
this.$emit('close');
},
clear() {
this.$refs.checkBoxGroup.$emit('input', []);
}
}
};
</script>
<style lang="scss" scoped>
.vant-select {
.van-popup {
overflow-y: hidden;
}
}
.search-dev {
.van-cell {
font-size: 0.28rem;
line-height: 0.36rem;
}
background: #ffffff;
position: relative;
.search-text {
}
}
.btn-nav {
margin: 0.28rem;
display: flex;
justify-content: space-between;
font-size: 0.24rem;
color: #aaa6a6;
}
</style>
<template>
<div>
<van-button color="#46B0E5" round plain size="small" @click="showPicker = true">
点击
</van-button>
<van-select v-model="value" :show-picker.sync="showPicker" filterable multiple @confirm="confirm">
<template>
<van-select-item v-for="item in selectList" :key="item.key" :label="item.label" :value="item.value">
</van-select-item>
</template>
</van-select>
</div>
<script>
export default {
data() {
return {
showPicker: false,
value: [],
selectList: [
{
key: '1',
label: '黄油',
value: '0001'
},
{
key: '2',
label: '面包',
value: '0002'
},
{
key: '3',
label: '拿铁',
value: '0003'
},
{
key: '4',
label: '橙汁',
value: '0004'
},
{
key: '5',
label: '泡芙',
value: '0005'
}
]
},
methods: {
confirm(res) {
console.log(res);
}
}
}
}
</script>
1. icon自定义,可以加个插槽
2. 目前不论单选还是多选,绑定的值都必须是数组,所以可以针对绑定值进行处理,另外添加一个定制值,checkboxGroup的值就放在组件内部进行监听自处理