

一、Vue 3 示例
v-model 的本质
v-model 默认绑定 modelValue 属性并监听 update:modelValue 事件。
MultipleSelect.vue
html
<template>
<van-dropdown-menu ref="menuRef">
<van-dropdown-item :title="placeholder" ref="itemRef">
<van-cell v-for="item in options" :key="item.value" :title="item.label" center @click="handleClick(item)">
<template #right-icon>
<van-icon v-show="modelValue.includes(item.value)" name="success" color="rgb(0, 122, 255)" />
</template>
</van-cell>
</van-dropdown-item>
</van-dropdown-menu>
</template>
<script setup>
const props = defineProps({
modelValue: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: '请选择'
},
options: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:modelValue'])
const handleClick = (val) => {
const selected = [...props.modelValue]
if (selected.includes(val.value)) {
selected.splice(selected.indexOf(val.value), 1)
} else {
selected.push(val.value)
}
emit('update:modelValue', selected)
console.log(selected)
}
</script>
使用
html
<template>
<div class="home-page">
<MultipleSelect v-model="selected" placeholder="请选择" :options="options" />
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
import MultipleSelect from '@/components/MultipleSelect.vue'
const options = [
{
label: '北京',
value: 1,
},
{
label: '上海',
value: 2,
},
{
label: '广州',
value: 3,
},
{
label: '深圳',
value: 4,
},
]
const selected = ref([])
watch(selected, (newVal) => {
console.log('选中的值', newVal)
})
</script>
<style lang="scss" scoped>
.home-page {}
</style>
组件上的 v-model 也可以接受一个参数:
html
<MultipleSelect v-model:value="selected" placeholder="请选择" :options="options" />
html
<template>
<van-dropdown-menu ref="menuRef">
<van-dropdown-item :title="placeholder" ref="itemRef">
<van-cell v-for="item in options" :key="item.value" :title="item.label" center @click="handleClick(item)">
<template #right-icon>
<van-icon v-show="value.includes(item.value)" name="success" color="rgb(0, 122, 255)" />
</template>
</van-cell>
</van-dropdown-item>
</van-dropdown-menu>
</template>
<script setup>
const props = defineProps({
value: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: '请选择'
},
options: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:value'])
const handleClick = (val) => {
const selected = [...props.value]
if (selected.includes(val.value)) {
selected.splice(selected.indexOf(val.value), 1)
} else {
selected.push(val.value)
}
emit('update:value', selected)
console.log(selected)
}
</script>
二、vue3.4+示例
html
<template>
<van-dropdown-menu ref="menuRef">
<van-dropdown-item :title="placeholder" ref="itemRef">
<van-cell v-for="item in options" :key="item.value" :title="item.label" center @click="handleClick(item)">
<template #right-icon>
<van-icon v-show="model.includes(item.value)" name="success" color="rgb(0, 122, 255)" />
</template>
</van-cell>
</van-dropdown-item>
</van-dropdown-menu>
</template>
<script setup>
/**
* defineModel 是一个便利宏。编译器将其展开为以下内容:
一个名为 modelValue 的 prop,本地 ref 的值与其同步;
一个名为 update:modelValue 的事件,当本地 ref 的值发生变更时触发。
*/
const model = defineModel({ default: () => [] })
const props = defineProps({
placeholder: {
type: String,
default: '请选择'
},
options: {
type: Array,
default: () => []
}
})
const handleClick = (val) => {
const selected = [...model.value]
if (selected.includes(val.value)) {
selected.splice(selected.indexOf(val.value), 1)
} else {
selected.push(val.value)
}
// 直接赋值给 model.value
model.value = selected
console.log(selected)
}
</script>
<style lang="scss" scoped></style>
html
<MultipleSelect v-model="selected" placeholder="请选择" :options="options" />
组件上的 v-model 也可以接受一个参数:
html
<MultipleSelect v-model:value="selected" placeholder="请选择" :options="options" />
html
<template>
<van-dropdown-menu ref="menuRef">
<van-dropdown-item :title="placeholder" ref="itemRef">
<van-cell v-for="item in options" :key="item.value" :title="item.label" center @click="handleClick(item)">
<template #right-icon>
<van-icon v-show="value.includes(item.value)" name="success" color="rgb(0, 122, 255)" />
</template>
</van-cell>
</van-dropdown-item>
</van-dropdown-menu>
</template>
<script setup>
/**
* defineModel 是一个便利宏。编译器将其展开为以下内容:
一个名为 modelValue 的 prop,本地 ref 的值与其同步;
一个名为 update:modelValue 的事件,当本地 ref 的值发生变更时触发。
*/
const value = defineModel({ default: () => [] })
const props = defineProps({
placeholder: {
type: String,
default: '请选择'
},
options: {
type: Array,
default: () => []
}
})
const handleClick = (val) => {
const selected = [...value.value]
if (selected.includes(val.value)) {
selected.splice(selected.indexOf(val.value), 1)
} else {
selected.push(val.value)
}
// 直接赋值给 value.value
value.value = selected
console.log(selected)
}
</script>
<style lang="scss" scoped></style>
三、vue2示例
html
<template>
<van-dropdown-menu ref="menuRef">
<van-dropdown-item :title="placeholder" ref="itemRef">
<van-cell v-for="item in options" :key="item.value" :title="item.label" center @click="handleClick(item)">
<template #right-icon>
<van-icon v-show="value.includes(item.value)" name="success" color="rgb(0, 122, 255)" />
</template>
</van-cell>
</van-dropdown-item>
</van-dropdown-menu>
</template>
<script>
export default {
props: {
value: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: '请选择'
},
options: {
type: Array,
default: () => []
}
},
data() {
return {
}
},
methods: {
handleClick(val) {
const selected = [...this.value]
if (selected.includes(val.value)) {
selected.splice(selected.indexOf(val.value), 1)
} else {
selected.push(val.value)
}
this.$emit('update:value', selected)
console.log(selected)
}
}
}
</script>
html
<template>
<div class="home-page">
<MultipleSelect v-model:value="selected" placeholder="请选择" :options="options" />
</div>
</template>
<script>
import MultipleSelect from '@/components/MultipleSelect.vue'
export default {
components: {
MultipleSelect
},
data() {
return {
options: [
{
label: '北京',
value: 1,
},
{
label: '上海',
value: 2,
},
{
label: '广州',
value: 3,
},
{
label: '深圳',
value: 4,
},
],
selected: []
}
},
watch: {
selected(newVal) {
console.log('选中的值', newVal)
}
}
}
</script>
<style lang="scss" scoped>
.home-page {
width: 100%;
height: 100%;
}
</style>
下拉多选2
html
<template>
<van-field readonly clickable name="picker" v-model="fieldValue" :label="label" :placeholder="placeholder"
input-align="right" is-link @click="popupVisible = true" />
<!-- <van-popup v-model:show="popupVisible" position="bottom" :style="{ height: '30%' }">
<van-cell v-for="item in options" :key="item.value" :title="item.label" center @click="handleClick(item)">
<template #right-icon>
<van-icon v-show="modelValue.includes(item.value)" name="success" color="rgb(0, 122, 255)" />
</template>
</van-cell>
</van-popup> -->
<van-popup v-model:show="popupVisible" round position="bottom" closeable class="custom-popup">
<van-cell-group class="list">
<van-cell v-for="item in options" :key="item.value" :title="item.label" center @click="handleClick(item)">
<template #right-icon>
<van-icon v-show="modelValue.includes(item.value)" name="success" color="rgb(0, 122, 255)" />
</template>
</van-cell>
</van-cell-group>
</van-popup>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
modelValue: {
type: Array,
default: () => []
},
label: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
options: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['update:modelValue'])
const fieldValue = ref('')
const popupVisible = ref(false)
const handleClick = (val) => {
const selected = [...props.modelValue]
if (selected.includes(val.value)) {
selected.splice(selected.indexOf(val.value), 1)
} else {
selected.push(val.value)
}
emit('update:modelValue', selected)
let selectedText = []
props.options.forEach((item) => {
if (selected.includes(item.value)) {
selectedText.push(item.label)
}
})
fieldValue.value = selectedText.join('、')
console.log(selected)
}
</script>
<style>
.custom-popup {
padding: 52px 0 16px 0;
.list {
max-height: 70vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
}
</style>
html
<MultipleSelect2 v-model="selected" placeholder="请选择" :options="options" label="所属城市" />