自定义组件(移动端下拉多选)中使用 v-model


一、Vue 3 示例
v-model 的本质
v-model 默认绑定 modelValue 属性并监听 update:modelValue 事件。
MultipleSelect.vue
<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>
使用
<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 也可以接受一个参数:
<MultipleSelect v-model:value="selected" placeholder="请选择" :options="options" />
<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+示例
<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.valuemodel.value = selectedconsole.log(selected)
}
</script><style lang="scss" scoped></style>
<MultipleSelect v-model="selected" placeholder="请选择" :options="options" />
组件上的 v-model 也可以接受一个参数:
<MultipleSelect v-model:value="selected" placeholder="请选择" :options="options" />
<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.valuevalue.value = selectedconsole.log(selected)
}
</script><style lang="scss" scoped></style>
三、vue2示例
<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>
<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
<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>
<MultipleSelect2 v-model="selected" placeholder="请选择" :options="options" label="所属城市" />
