CheckboxGroup 复选框组

一组复选框,共用一个数组值。

<script setup lang="ts">
  import { ref } from 'vue'
  import { CheckboxGroup } from '@hina-ui/vue'

  const types = ref(['gal', 'ln'])
  const options = [
    { value: 'gal', label: 'Galgame' },
    { value: 'ln', label: '轻小说' },
    { value: 'manga', label: '漫画' },
  ]
</script>

<template>
  <CheckboxGroup v-model="types" :options="options" aria-label="作品类型" />
</template>
vue

用法

import { CheckboxGroup } from '@hina-ui/vue'
ts

复选框组按 options 渲染一列 Checkbox,v-model 绑定已选值的数组,选项的类型见 Select。未声明的属性都会传给根元素,应当用 aria-label 或者 aria-labelledby 给整组命名。

当前值:[ "mail" ]

<script setup lang="ts">
  import { ref } from 'vue'
  import { CheckboxGroup, Stack, Text } from '@hina-ui/vue'

  const channels = ref(['mail'])
  const options = [
    { value: 'mail', label: '邮件' },
    { value: 'push', label: '站内推送' },
    { value: 'sms', label: '短信' },
  ]
</script>

<template>
  <Stack gap="sm">
    <CheckboxGroup v-model="channels" :options="options" aria-label="通知方式" />
    <Text size="sm" tone="muted">当前值:{{ channels }}</Text>
  </Stack>
</template>
vue

示例

横排

orientation 为 horizontal 时选项横向排列,放不下时折行。

<script setup lang="ts">
  import { ref } from 'vue'
  import { CheckboxGroup } from '@hina-ui/vue'

  const days = ref(['sat', 'sun'])
  const options = [
    { value: 'mon', label: '周一' },
    { value: 'wed', label: '周三' },
    { value: 'fri', label: '周五' },
    { value: 'sat', label: '周六' },
    { value: 'sun', label: '周日' },
  ]
</script>

<template>
  <CheckboxGroup v-model="days" :options="options" orientation="horizontal" aria-label="更新日" />
</template>
vue

描述

选项的 description 显示在文字下方。

<script setup lang="ts">
  import { ref } from 'vue'
  import { CheckboxGroup } from '@hina-ui/vue'

  const scopes = ref(['read'])
  const options = [
    { value: 'read', label: '读取', description: '查看收藏与阅读进度' },
    { value: 'write', label: '写入', description: '修改收藏与评分' },
    { value: 'admin', label: '管理', description: '管理令牌与授权范围' },
  ]
</script>

<template>
  <CheckboxGroup v-model="scopes" :options="options" aria-label="授权范围" />
</template>
vue

设置行

使用 control-placement="end" 将控件放到文案末端,配合 block 撑满容器宽度。说明始终位于标题下方;start 和 end 会跟随文字方向。启用 block 后,纵向选项各自撑满一行,横向选项等分行宽。orientation 仍表示多个选项的排列方向。

<script setup lang="ts">
  import { ref } from 'vue'
  import { CheckboxGroup } from '@hina-ui/vue'

  const scopes = ref(['read'])
  const options = [
    { value: 'read', label: '读取', description: '查看收藏与阅读进度' },
    { value: 'write', label: '写入', description: '修改收藏与评分' },
    { value: 'admin', label: '管理', description: '管理令牌与授权范围' },
  ]
</script>

<template>
  <CheckboxGroup
    v-model="scopes"
    control-placement="end"
    block
    :options="options"
    aria-label="授权范围"
  />
</template>
vue

尺寸

size 下发到每个复选框。

<script setup lang="ts">
  import { CheckboxGroup, Inline } from '@hina-ui/vue'

  const options = [
    { value: 'gal', label: 'Galgame' },
    { value: 'ln', label: '轻小说' },
  ]
</script>

<template>
  <Inline gap="lg" align="start">
    <CheckboxGroup size="sm" :options="options" :model-value="['gal']" aria-label="小号" />
    <CheckboxGroup size="md" :options="options" :model-value="['gal']" aria-label="中号" />
    <CheckboxGroup size="lg" :options="options" :model-value="['gal']" aria-label="大号" />
  </Inline>
</template>
vue

状态

选项上的 disabled 只禁用该项,组上的 disabled 禁用整组;invalid 落到每个方框。

<script setup lang="ts">
  import { CheckboxGroup, Inline } from '@hina-ui/vue'

  const options = [
    { value: 'gal', label: 'Galgame' },
    { value: 'ln', label: '轻小说' },
    { value: 'manga', label: '漫画', disabled: true },
  ]
</script>

<template>
  <Inline gap="lg" align="start">
    <CheckboxGroup :options="options" :model-value="['gal']" aria-label="含禁用项" />
    <CheckboxGroup disabled :options="options" :model-value="['gal']" aria-label="整组禁用" />
    <CheckboxGroup invalid :options="options" :model-value="[]" aria-label="校验未通过" />
  </Inline>
</template>
vue

定制内容

option 插槽定制每一项的文字。

组件从 options 推断完整选项类型,插槽中的 option 保留额外字段及其类型;v-model 仍绑定选项的 value 数组。类型定义见 Select。

<script setup lang="ts">
  import { ref } from 'vue'
  import { BookOpen, Gamepad2, Images } from '@lucide/vue'
  import { CheckboxGroup, Inline } from '@hina-ui/vue'

  const icons = { gal: Gamepad2, ln: BookOpen, manga: Images }
  const types = ref(['gal'])
  const options = [
    { value: 'gal', label: 'Galgame' },
    { value: 'ln', label: '轻小说' },
    { value: 'manga', label: '漫画' },
  ]
</script>

<template>
  <CheckboxGroup v-model="types" :options="options" aria-label="作品类型">
    <template #option="{ option }">
      <Inline as="span" gap="xs" align="center">
        <component :is="icons[option.value as keyof typeof icons]" class="text-muted size-4" />
        {{ option.label }}
      </Inline>
    </template>
  </CheckboxGroup>
</template>
vue

在表单中

放进 FormField 后,标签通过 aria-labelledby 关联到整组,说明与错误信息由字段渲染;校验规则与提交交给 Form。

选一到两项

<script setup lang="ts">
  import { reactive, ref } from 'vue'
  import * as v from 'valibot'
  import { Button, CheckboxGroup, Form, FormField, Text } from '@hina-ui/vue'

  const options = [
    { label: '游戏', value: 'game' },
    { label: '小说', value: 'novel' },
    { label: '漫画', value: 'manga' },
    { label: '动画', value: 'anime' },
  ]

  const schema = v.object({
    interests: v.pipe(
      v.array(v.string('请选择兴趣')),
      v.minLength(1, '至少选择一项'),
      v.maxLength(2, '最多选择两项'),
    ),
  })

  const values = reactive({ interests: [] as Array<string | number> })
  const saved = ref('')

  async function save(data: unknown) {
    await new Promise(resolve => setTimeout(resolve, 600))
    saved.value = JSON.stringify(data)
  }
</script>

<template>
  <Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
    <FormField name="interests" label="兴趣" description="选一到两项" required>
      <CheckboxGroup v-model="values.interests" :options="options" />
    </FormField>
    <Button type="submit" :loading="submitting" class="self-start">保存</Button>
    <Text v-if="saved" tone="muted" size="sm">已保存:{{ saved }}</Text>
  </Form>
</template>
vue

行为

  • 点选把值加入数组,再点移除,数组顺序与点选顺序一致。
  • 每个复选框都是独立的 Tab 停靠点,空格切换焦点所在项,方向键不移动焦点,与原生复选框组一致。

无障碍

  • 根元素是 role="group",通过 aria-label 或者 aria-labelledby 命名;每一项沿用 Checkbox 的语义。

API

Props

T extends SelectOption 从 options 推断,默认是 SelectOption。

属性
类型
默认值
说明
modelValue
Array<string | number>
[]
已选值
options
T[]
—
选项,类型见 Select
orientation
'vertical' | 'horizontal'
'vertical'
排列方向
size
'sm' | 'md' | 'lg'
'md'
每个复选框的尺寸
controlPlacement
'start' | 'end'
'start'
控件相对于文案的位置
block
boolean
false
整组撑满,横向选项等分宽度
disabled
boolean
false
是否禁用整组
invalid
boolean
false
是否处于校验未通过状态
class
string
—
追加至根元素的类名

插槽

插槽
参数
说明
option
{ option: T }
每一项的文字

事件

事件
参数
说明
update:modelValue
value: Array<string | number>
已选值变化