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


一、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="所属城市" />
相关推荐
kyriewen8 小时前
别再 console.log 了:5 个 Chrome DevTools 调试技巧,用过就回不去了
前端·javascript·面试
IT_陈寒10 小时前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
To_OC10 小时前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
DigitalOcean11 小时前
Laravel 开发者已在 DigitalOcean 上开通超过 10 万台服务器
前端·laravel
星始流年11 小时前
从 Tool 到 Skill——基于 LangChain 的服务端Skill实现
前端·langchain·agent
李惟11 小时前
开源本地通信库,纯客户端 RPC,像聊天一样通信
前端
YAwu1111 小时前
深入解析 React 炫彩鼠标跟随标题组件:从坐标定位到动画性能
前端·react.js
GuWenyue11 小时前
排序效率低?5分钟吃透快速排序,性能飙升至O(nlogn)
前端·javascript·面试
OpenTiny社区11 小时前
🎨 看完 GenUI SDK 源码我悟了!
前端·vue.js·github
叁两11 小时前
前端转型AI Agent该如何学习?(前置篇)
前端·人工智能·node.js