由于vant移动端组件中,没有提供select组件。下面我封装一个支持可模糊搜素,远程搜素select选择器
效果:
1.封装的组件(vanMulSelectPicker)
<template>
<div class="vant-mul-select-picker">
<!-- 模拟单元格样式 -->
<div class="vant-cell" @click="cellClick">
<div class="vant-label">{{ $attrs.label }}</div>
<div class="vant-tag-box">
<div v-if="state.resultLabel.length == 0">{{ $attrs.placeholder || "请选择" }}</div>
<van-tag @close="closeTag(index)" v-else size="large" type="primary" closeable v-for="(item, index) in state.resultLabel" :key="index">{{
item
}}</van-tag>
</div>
<van-icon name="arrow" />
</div>
<!-- 弹出层 -->
<van-popup teleport="body" v-model:show="state.showPicker" position="bottom">
<div class="van-picker__toolbar">
<button type="button" class="van-picker__cancel" @click="state.showPicker = false">取消</button>
<div class="van-ellipsis van-picker__title">{{ $attrs.title }}</div>
<button type="button" class="van-picker__confirm" @click="confirmClick">确认</button>
</div>
<div class="mul-sel__content">
<van-field v-if="props.isSearch" v-model="state.searchVal" input-align="left" placeholder="搜索" @update:model-value="inputChange" />
<van-loading size="30px" vertical v-if="loading">加载中...</van-loading>
<van-cell title="全选" v-if="checkAllShow">
<template #right-icon>
<van-checkbox v-model="state.checkedAll" name="all" @click="toggleAll" />
</template>
</van-cell>
<van-empty image="search" v-if="state.columns.length == 0 && !loading" description="" />
<van-checkbox-group v-else ref="checkboxGroup" v-model="state.checkboxValue">
<van-cell-group>
<van-cell
v-for="(item, index) in state.columns"
:key="item[options.value]"
:title="item[options.label]"
clickable
@click.stop="toggle(index)"
>
<template #right-icon>
<van-checkbox shape="square" ref="checkboxes" @click.stop.native="() => {}" :name="item[options.value]" />
</template>
</van-cell>
</van-cell-group>
</van-checkbox-group>
</div>
</van-popup>
</div>
</template>
<script setup>
import { reactive, ref, watch } from "vue";
import { debounce } from "lodash-es";
const emit = defineEmits(["confirm", "remoteMethed", "update:modelValue", "change"]);
//props
const props = defineProps({
checkAllShow: Boolean, //全选是否展示
modelValue: {
type: Array,
default: () => {
return [];
},
},
columns: Array,
remote: Boolean,
loading: Boolean,
options: {
type: Object,
default: () => {
return { label: "label", value: "value" };
},
},
//是否支持搜索
isSearch: {
type: Boolean,
default: false,
},
});
//data响应式数据
const state = reactive({
showPicker: false,
searchVal: "",
resultLabel: [],
checkboxValue: props.modelValue,
columns: [],
checkedAll: false,
});
//单元格点击 展示弹出层
const cellClick = () => {
showPopu();
};
//删除选项
const closeTag = (index) => {
state.checkboxValue.splice(index, 1);
};
//展示弹出层
const showPopu = () => {
state.showPicker = true;
//清空搜素栏
state.searchVal = "";
state.columns = props.columns;
};
const setResultLabel = () => {
state.resultLabel = [];
state.checkboxValue?.forEach((item) => {
let label = state.columns.find((col) => col[props.options.value] == item)?.[props.options.label];
let labelTitle = label ? label : item;
state.resultLabel.push(labelTitle);
});
};
//确定点击
const confirmClick = () => {
state.showPicker = false;
emit("confirm", state.checkboxValue);
};
//单个选择
let checkboxes = ref();
const toggle = (index) => {
checkboxes.value[index].toggle();
};
//全选
const checkboxGroup = ref();
const toggleAll = () => {
checkboxGroup.value?.toggleAll(state.checkedAll);
};
//防抖处理 模糊搜索value值改变触发
const inputChange = debounce(() => {
//获取远程数据
if (props.remote) {
emit("remoteMethed", state.searchVal);
return;
}
//获取所有的选项
if (!state.searchVal) {
state.columns = props.columns;
} else {
state.columns = props.columns.filter((item) => item[props.options.label].indexOf(state.searchVal) > -1);
}
}, 500);
watch(
() => state.checkboxValue,
(newVal) => {
setResultLabel();
emit("update:modelValue", newVal);
emit("change", newVal);
},
{
immediate: true,
deep: true,
}
);
watch(
() => props.columns,
(newVal) => {
state.columns = newVal;
},
{ immediate: true, deep: true }
);
</script>
<style lang="scss" scoped>
.vant-mul-select-picker {
padding: 0 15px;
}
.vant-cell {
position: relative;
display: flex;
font-size: 14px;
padding: 10px 0;
box-sizing: border-box;
border-bottom: 1px solid #f2f3f5;
.vant-label {
width: 100px;
}
.vant-tag-box {
margin-right: 20px;
color: #c8c9cc;
width: calc(100% - 120px);
.van-tag {
margin-right: 5px;
margin-bottom: 5px;
}
}
.van-icon-arrow {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
color: #969799;
font-size: 16px;
}
}
.mul-sel__content {
position: relative;
height: 250px;
overflow-y: auto;
padding-bottom: 20px;
.van-loading {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
</style>
注意:需要你安装一下lodash-es (因为远程搜索我加了一个防抖处理)
npm install lodash-es
2.父组件使用
<template>
<div class="sel-picker">
<van-mul-select-picker
label="城市"
placeholder="请选择城市"
v-model="state.mulVal"
:columns="state.columns"
:options="{ label: 'name', value: 'id' }"
isSearch
checkAllShow
@confirm="confirm"
@change="change"
></van-mul-select-picker>
</div>
</template>
<script setup lang="ts">
import { reactive } from "vue";
import vanMulSelectPicker from "@/components/vanMulSelectPicker.vue";
let state = reactive({
mulVal: [],
columns: [
{ name: "北京", id: "beijing" },
{ name: "上海", id: "shanghai" },
{ name: "深圳", id: "shenzhen" },
{ name: "广州", id: "guangzhou" },
{ name: "杭州", id: "hangzhou" },
{ name: "重庆", id: "chongqing" },
],
});
const change = (selVal) => {
console.log(state.mulVal);
console.log("selVal", selVal);
};
const confirm = (item) => {
console.log(item);
state.selectItem = item;
};
</script>
<style scoped lang="scss"></style>