单选与复选框

单选框和复选框是表单中常用的选择控件。虽然使用 v-model 绑定很简单,但不同场景下绑定值的方式有所不同,需要理解其中的区别。

复选框

单个复选框 - 布尔值绑定

单个复选框绑定布尔值,选中为 true,未选中为 false

<div id="app">
  <label>
    <input type="checkbox" v-model="agree">
    同意用户协议
  </label>
  <p>状态:{{ agree }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    agree: false
  }
})
</script>

自定义 true/false 值

使用 true-valuefalse-value 自定义选中/未选中的值:

<div id="app">
  <label>
    <input 
      type="checkbox" 
      v-model="status"
      true-value="yes"
      false-value="no"
    >
    订阅邮件
  </label>
  <p>状态:{{ status }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    status: 'no'
  }
})
</script>

选中时 status'yes',未选中时为 'no'

多个复选框 - 数组绑定

多个复选框绑定同一个数组,选中的值会自动添加到数组中:

<div id="app">
  <p>选择你喜欢的水果:</p>
  <label>
    <input type="checkbox" v-model="fruits" value="apple"> 苹果
  </label>
  <label>
    <input type="checkbox" v-model="fruits" value="banana"> 香蕉
  </label>
  <label>
    <input type="checkbox" v-model="fruits" value="orange"> 橙子
  </label>
  <p>已选择:{{ fruits }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    fruits: []  // 初始化为空数组
  }
})
</script>

选中苹果和香蕉后,fruits['apple', 'banana']

动态绑定值

使用 v-bind 动态绑定值:

<div id="app">
  <label v-for="item in items" :key="item.id">
    <input 
      type="checkbox" 
      v-model="selected"
      :value="item"
    >
    {{ item.name }}
  </label>
  <pre>{{ selected }}</pre>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    items: [
      { id: 1, name: '选项一' },
      { id: 2, name: '选项二' },
      { id: 3, name: '选项三' }
    ],
    selected: []
  }
})
</script>

选中的是完整的对象,而不只是字符串值。

全选功能

<div id="app">
  <label>
    <input 
      type="checkbox" 
      :checked="allSelected"
      @change="toggleAll"
    >
    全选
  </label>
  
  <hr>
  
  <label v-for="item in items" :key="item.id">
    <input 
      type="checkbox" 
      v-model="selected"
      :value="item.id"
    >
    {{ item.name }}
  </label>
  
  <p>已选择:{{ selected.length }} / {{ items.length }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    items: [
      { id: 1, name: '选项一' },
      { id: 2, name: '选项二' },
      { id: 3, name: '选项三' },
      { id: 4, name: '选项四' }
    ],
    selected: []
  },
  computed: {
    allSelected: function() {
      return this.items.length > 0 && 
             this.selected.length === this.items.length
    }
  },
  methods: {
    toggleAll: function() {
      if (this.allSelected) {
        this.selected = []
      } else {
        this.selected = this.items.map(item => item.id)
      }
    }
  }
})
</script>

单选框

基本用法

单选框绑定单个值,选中的 value 会赋给绑定变量:

<div id="app">
  <p>选择性别:</p>
  <label>
    <input type="radio" v-model="gender" value="male"></label>
  <label>
    <input type="radio" v-model="gender" value="female"></label>
  <p>已选择:{{ gender }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    gender: ''
  }
})
</script>

动态生成单选框

<div id="app">
  <label v-for="option in options" :key="option.value">
    <input 
      type="radio" 
      v-model="selected"
      :value="option.value"
    >
    {{ option.label }}
  </label>
  <p>已选择:{{ selected }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    options: [
      { value: 'option1', label: '选项一' },
      { value: 'option2', label: '选项二' },
      { value: 'option3', label: '选项三' }
    ],
    selected: ''
  }
})
</script>

绑定对象值

<div id="app">
  <label v-for="plan in plans" :key="plan.id">
    <input 
      type="radio" 
      v-model="selectedPlan"
      :value="plan"
    >
    {{ plan.name }} - ¥{{ plan.price }}/月
  </label>
  
  <div v-if="selectedPlan">
    <h3>已选择:{{ selectedPlan.name }}</h3>
    <p>价格:¥{{ selectedPlan.price }}/月</p>
    <p>描述:{{ selectedPlan.description }}</p>
  </div>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    plans: [
      { id: 1, name: '基础版', price: 9.9, description: '适合个人用户' },
      { id: 2, name: '专业版', price: 29.9, description: '适合小型团队' },
      { id: 3, name: '企业版', price: 99.9, description: '适合大型企业' }
    ],
    selectedPlan: null
  }
})
</script>

实战示例

权限设置

<div id="app">
  <h3>设置用户权限</h3>
  
  <div class="permission-group">
    <h4>基础权限</h4>
    <label>
      <input type="checkbox" v-model="permissions" value="read">
      查看内容
    </label>
    <label>
      <input type="checkbox" v-model="permissions" value="write">
      编辑内容
    </label>
  </div>
  
  <div class="permission-group">
    <h4>管理权限</h4>
    <label>
      <input type="checkbox" v-model="permissions" value="delete">
      删除内容
    </label>
    <label>
      <input type="checkbox" v-model="permissions" value="admin">
      管理用户
    </label>
  </div>
  
  <p>当前权限:{{ permissions.join(', ') || '无' }}</p>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    permissions: ['read']  // 默认有查看权限
  }
})
</script>

评分组件

<div id="app">
  <h3>请评分</h3>
  <div class="rating">
    <label v-for="n in 5" :key="n">
      <input 
        type="radio" 
        v-model="rating" 
        :value="n"
        class="rating-input"
      >
      <span class="rating-star" :class="{ active: n <= rating }"></span>
    </label>
  </div>
  <p>评分:{{ rating }} 星</p>
</div>

<style>
.rating-input {
  display: none;
}
.rating-star {
  font-size: 32px;
  color: #ddd;
  cursor: pointer;
}
.rating-star.active {
  color: #f5a623;
}
</style>

<script>
new Vue({
  el: '#app',
  data: {
    rating: 0
  }
})
</script>

标签选择

<div id="app">
  <h3>选择标签</h3>
  <div class="tags">
    <label 
      v-for="tag in allTags" 
      :key="tag"
      class="tag-label"
      :class="{ selected: selectedTags.includes(tag) }"
    >
      <input 
        type="checkbox" 
        v-model="selectedTags"
        :value="tag"
        class="tag-input"
      >
      {{ tag }}
    </label>
  </div>
  <p>已选标签:{{ selectedTags.join(', ') || '无' }}</p>
</div>

<style>
.tag-input {
  display: none;
}
.tag-label {
  display: inline-block;
  padding: 4px 12px;
  margin: 4px;
  border: 1px solid #ddd;
  border-radius: 16px;
  cursor: pointer;
}
.tag-label.selected {
  background: #42b983;
  color: white;
  border-color: #42b983;
}
</style>

<script>
new Vue({
  el: '#app',
  data: {
    allTags: ['前端', '后端', 'Vue', 'React', 'Node.js', 'Python', 'Java'],
    selectedTags: []
  }
})
</script>

开关组件

<div id="app">
  <div class="switch-group">
    <span>夜间模式</span>
    <label class="switch">
      <input type="checkbox" v-model="darkMode">
      <span class="slider"></span>
    </label>
  </div>
  
  <div class="switch-group">
    <span>消息通知</span>
    <label class="switch">
      <input type="checkbox" v-model="notifications">
      <span class="slider"></span>
    </label>
  </div>
  
  <div class="switch-group">
    <span>自动播放</span>
    <label class="switch">
      <input type="checkbox" v-model="autoPlay">
      <span class="slider"></span>
    </label>
  </div>
</div>

<style>
.switch {
  position: relative;
  display: inline-block;
  width: 50px;
  height: 26px;
}
.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}
.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  transition: .3s;
  border-radius: 26px;
}
.slider:before {
  position: absolute;
  content: "";
  height: 20px;
  width: 20px;
  left: 3px;
  bottom: 3px;
  background-color: white;
  transition: .3s;
  border-radius: 50%;
}
input:checked + .slider {
  background-color: #42b983;
}
input:checked + .slider:before {
  transform: translateX(24px);
}
.switch-group {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 0;
  border-bottom: 1px solid #eee;
}
</style>

<script>
new Vue({
  el: '#app',
  data: {
    darkMode: false,
    notifications: true,
    autoPlay: false
  }
})
</script>

注意事项

复选框数组初始化

多个复选框绑定数组时,必须初始化为空数组,否则会绑定为布尔值:

// ✅ 正确
data: {
  selected: []
}

// ❌ 错误(会变成布尔值绑定)
data: {
  selected: ''
}

单选框默认选中

设置初始值即可默认选中:

data: {
  gender: 'male'  // 默认选中男
}

小结

单选框和复选框的处理要点:

  1. 单个复选框:绑定布尔值或使用 true-value/false-value
  2. 多个复选框:绑定数组,选中的值自动添加
  3. 单选框:绑定单个值,选中的 value 赋给变量
  4. 动态值:使用 v-bind 绑定对象或动态值