开发预览 更新于 2026-05-10

Combobox 组合选择

Select 的可输入变体,键入即过滤选项,可选择允许新增条目。

基础用法

options 传进去,v-model 绑定当前值。点开后可以直接输入过滤;↑↓ 移动,Enter 选中,Esc 收起。

背景 视口
×
当前:vue
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfCombobox, type ComboboxOption } from '@chufix-design/vue';

const value = ref<string | number | null>('vue');
const options: ComboboxOption[] = [
  { value: 'vue', label: 'Vue 3' },
  { value: 'react', label: 'React 18' },
  { value: 'svelte', label: 'Svelte 5' },
  { value: 'solid', label: 'SolidJS' },
  { value: 'angular', label: 'Angular' },
  { value: 'qwik', label: 'Qwik' },
];
</script>
<template>
  <div style="display:flex; gap: 12px; align-items: flex-start;">
    <CfCombobox v-model="value" :options="options" clearable />
    <span style="font-size: 12px; color: var(--fg-3); align-self: center;">
      当前:{{ value ?? '(无)' }}
    </span>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfCombobox } from '@chufix-design/vue';

const value = ref<string | number | null>('vue');
const options= [
  { value: 'vue', label: 'Vue 3' },
  { value: 'react', label: 'React 18' },
  { value: 'svelte', label: 'Svelte 5' },
  { value: 'solid', label: 'SolidJS' },
  { value: 'angular', label: 'Angular' },
  { value: 'qwik', label: 'Qwik' },
];
</script>
<template>
  <div style="display:flex; gap: 12px; align-items: flex-start;">
    <CfCombobox v-model="value" :options="options" clearable />
    <span style="font-size: 12px; color: var(--fg-3); align-self: center;">
      当前:{{ value ?? '(无)' }}
    </span>
  </div>
</template>
import { useState } from 'react';
import { CfCombobox, type ComboboxOption } from '@chufix-design/react';

const options: ComboboxOption[] = [
{ value: 'vue', label: 'Vue 3' },
{ value: 'react', label: 'React 18' },
{ value: 'svelte', label: 'Svelte 5' },
{ value: 'solid', label: 'SolidJS' },
];

export default function Demo() {
const [value, setValue] = useState<string | number | null>('vue');
return <CfCombobox value={value} options={options} clearable onChange={setValue} />;
}
import { useState } from 'react';
import { CfCombobox } from '@chufix-design/react';

const options= [
{ value: 'vue', label: 'Vue 3' },
{ value: 'react', label: 'React 18' },
{ value: 'svelte', label: 'Svelte 5' },
{ value: 'solid', label: 'SolidJS' },
];

export default function Demo() {
const [value, setValue] = useState<string | number | null>('vue');
return <CfCombobox value={value} options={options} clearable onChange={setValue} />;
}

允许新增

allow-create 打开后,输入未匹配的内容时菜单底部会出现「创建 “xxx”」一项;按 Enter 或点击它都能把当前输入作为新值提交。@create(Vue)/ onCreate(React)会同时回调。

背景 视口
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfCombobox, type ComboboxOption } from '@chufix-design/vue';

const value = ref<string | number | null>(null);
const options = ref<ComboboxOption[]>([
  { value: 'frontend', label: '前端' },
  { value: 'backend', label: '后端' },
  { value: 'devops', label: '运维' },
]);

function onCreate(v: string) {
  options.value.push({ value: v.toLowerCase(), label: v });
}
</script>
<template>
  <CfCombobox
    v-model="value"
    :options="options"
    allow-create
    clearable
    placeholder="选择或新增分类"
    @create="onCreate"
  />
</template>
<script setup>
import { ref } from 'vue';
import { CfCombobox } from '@chufix-design/vue';

const value = ref<string | number | null>(null);
const options = ref<ComboboxOption[]>([
  { value: 'frontend', label: '前端' },
  { value: 'backend', label: '后端' },
  { value: 'devops', label: '运维' },
]);

function onCreate(v) {
  options.value.push({ value: v.toLowerCase(), label: v });
}
</script>
<template>
  <CfCombobox
    v-model="value"
    :options="options"
    allow-create
    clearable
    placeholder="选择或新增分类"
    @create="onCreate"
  />
</template>
import { useState } from 'react';
import { CfCombobox } from '@chufix-design/react';

export default function Demo() {
  const [value, setValue] = useState<string | number | null>(null);
  const [options, setOptions] = useState<ComboboxOption[]>([
    { value: 'frontend', label: '前端' },
    { value: 'backend', label: '后端' },
    { value: 'devops', label: '运维' },
  ]);

  function onCreate(v: string) {
    options.push({ value: v.toLowerCase(), label: v });
  }
  return (
    <>
      <CfCombobox value={value} onChange={setValue} options={options} allowCreate clearable placeholder="选择或新增分类" onCreate={onCreate} />
    </>
  );
}
import { useState } from 'react';
import { CfCombobox } from '@chufix-design/react';

export default function Demo() {
  const [value, setValue] = useState<string | number | null>(null);
  const [options, setOptions] = useState<ComboboxOption[]>([
    { value: 'frontend', label: '前端' },
    { value: 'backend', label: '后端' },
    { value: 'devops', label: '运维' },
  ]);

  function onCreate(v) {
    options.push({ value: v.toLowerCase(), label: v });
  }
  return (
    <>
      <CfCombobox value={value} onChange={setValue} options={options} allowCreate clearable placeholder="选择或新增分类" onCreate={onCreate} />
    </>
  );
}

尺寸

背景 视口
src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfCombobox, type ComboboxOption } from '@chufix-design/vue';

const a = ref<string | number | null>(null);
const b = ref<string | number | null>(null);
const c = ref<string | number | null>(null);
const options: ComboboxOption[] = [
  { value: 'apple', label: 'Apple' },
  { value: 'banana', label: 'Banana' },
  { value: 'cherry', label: 'Cherry' },
];
</script>
<template>
  <div style="display: flex; flex-direction: column; gap: 8px; max-width: 320px;">
    <CfCombobox v-model="a" :options="options" size="sm" placeholder="sm" />
    <CfCombobox v-model="b" :options="options" size="md" placeholder="md" />
    <CfCombobox v-model="c" :options="options" size="lg" placeholder="lg" />
  </div>
</template>
<script setup>
import { ref } from 'vue';
import { CfCombobox } from '@chufix-design/vue';

const a = ref<string | number | null>(null);
const b = ref<string | number | null>(null);
const c = ref<string | number | null>(null);
const options= [
  { value: 'apple', label: 'Apple' },
  { value: 'banana', label: 'Banana' },
  { value: 'cherry', label: 'Cherry' },
];
</script>
<template>
  <div style="display: flex; flex-direction: column; gap: 8px; max-width: 320px;">
    <CfCombobox v-model="a" :options="options" size="sm" placeholder="sm" />
    <CfCombobox v-model="b" :options="options" size="md" placeholder="md" />
    <CfCombobox v-model="c" :options="options" size="lg" placeholder="lg" />
  </div>
</template>
import { useState } from 'react';
import { CfCombobox } from '@chufix-design/react';

export default function Demo() {
  const [a, setA] = useState<string | number | null>(null);
  const [b, setB] = useState<string | number | null>(null);
  const [c, setC] = useState<string | number | null>(null);
  const options: ComboboxOption[] = [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'cherry', label: 'Cherry' },
  ];
  return (
    <>
      <div style={{ display: "flex", flexDirection: "column", gap: 8, maxWidth: 320 }}>
          <CfCombobox value={a} onChange={setA} options={options} size="sm" placeholder="sm" />
          <CfCombobox value={b} onChange={setB} options={options} size="md" placeholder="md" />
          <CfCombobox value={c} onChange={setC} options={options} size="lg" placeholder="lg" />
        </div>
    </>
  );
}
import { useState } from 'react';
import { CfCombobox } from '@chufix-design/react';

export default function Demo() {
  const [a, setA] = useState<string | number | null>(null);
  const [b, setB] = useState<string | number | null>(null);
  const [c, setC] = useState<string | number | null>(null);
  const options= [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'cherry', label: 'Cherry' },
  ];
  return (
    <>
      <div style={{ display: "flex", flexDirection: "column", gap: 8, maxWidth: 320 }}>
          <CfCombobox value={a} onChange={setA} options={options} size="sm" placeholder="sm" />
          <CfCombobox value={b} onChange={setB} options={options} size="md" placeholder="md" />
          <CfCombobox value={c} onChange={setC} options={options} size="lg" placeholder="lg" />
        </div>
    </>
  );
}

自定义过滤

默认过滤函数对 option.labeloption.value 做子串匹配。需要更复杂的匹配(拼音首字母、模糊匹配、字段权重)时传入自定义 filter

背景 视口

自定义 filter(query, option) 同时匹配中文名、拼音和区号 — 试试输入 "021"。

src/App.vue
<script setup lang="ts">
import { ref } from 'vue';
import { CfCombobox, type ComboboxOption } from '@chufix-design/vue';

const value = ref<string | number | null>(null);

const options: ComboboxOption[] = [
  { value: 'beijing', label: '北京 · BJ · 010' },
  { value: 'shanghai', label: '上海 · SH · 021' },
  { value: 'guangzhou', label: '广州 · GZ · 020' },
  { value: 'shenzhen', label: '深圳 · SZ · 0755' },
  { value: 'hangzhou', label: '杭州 · HZ · 0571' },
  { value: 'chengdu', label: '成都 · CD · 028' },
];

// 拼音首字母 / 区号 / 全称都能匹配
function fuzzy(query: string, opt: ComboboxOption): boolean {
  if (!query) return true;
  const q = query.toLowerCase();
  return (
    opt.label.toLowerCase().includes(q) ||
    String(opt.value).toLowerCase().includes(q)
  );
}
</script>
<template>
  <CfCombobox
    v-model="value"
    :options="options"
    :filter="fuzzy"
    placeholder="输入拼音 / 区号 / 城市名"
    clearable
  />
  <p class="adm-hint">
    自定义 <code>filter(query, option)</code> 同时匹配中文名、拼音和区号 — 试试输入 "021"。
  </p>
</template>
<style scoped>
.adm-hint { color: var(--fg-3); font-size: var(--t-12); margin-top: 8px; }
code { font-family: var(--font-mono); color: var(--fg-2); }
</style>
<script setup>
import { ref } from 'vue';
import { CfCombobox } from '@chufix-design/vue';

const value = ref<string | number | null>(null);

const options= [
  { value: 'beijing', label: '北京 · BJ · 010' },
  { value: 'shanghai', label: '上海 · SH · 021' },
  { value: 'guangzhou', label: '广州 · GZ · 020' },
  { value: 'shenzhen', label: '深圳 · SZ · 0755' },
  { value: 'hangzhou', label: '杭州 · HZ · 0571' },
  { value: 'chengdu', label: '成都 · CD · 028' },
];

// 拼音首字母 / 区号 / 全称都能匹配
function fuzzy(query, opt): boolean {
  if (!query) return true;
  const q = query.toLowerCase();
  return (
    opt.label.toLowerCase().includes(q) ||
    String(opt.value).toLowerCase().includes(q)
  );
}
</script>
<template>
  <CfCombobox
    v-model="value"
    :options="options"
    :filter="fuzzy"
    placeholder="输入拼音 / 区号 / 城市名"
    clearable
  />
  <p class="adm-hint">
    自定义 <code>filter(query, option)</code> 同时匹配中文名、拼音和区号 — 试试输入 "021"。
  </p>
</template>
<style scoped>
.adm-hint { color: var(--fg-3); font-size: var(--t-12); margin-top: 8px; }
code { font-family: var(--font-mono); color: var(--fg-2); }
</style>
import { useState } from 'react';
import { CfCombobox } from '@chufix-design/react';

export default function Demo() {
  const [value, setValue] = useState<string | number | null>(null);

  const options: ComboboxOption[] = [
    { value: 'beijing', label: '北京 · BJ · 010' },
    { value: 'shanghai', label: '上海 · SH · 021' },
    { value: 'guangzhou', label: '广州 · GZ · 020' },
    { value: 'shenzhen', label: '深圳 · SZ · 0755' },
    { value: 'hangzhou', label: '杭州 · HZ · 0571' },
    { value: 'chengdu', label: '成都 · CD · 028' },
  ];

  // 拼音首字母 / 区号 / 全称都能匹配
  function fuzzy(query: string, opt: ComboboxOption): boolean {
    if (!query) return true;
    const q = query.toLowerCase();
    return (
      opt.label.toLowerCase().includes(q) ||
      String(opt.value).toLowerCase().includes(q)
    );
  }
  return (
    <>
      <CfCombobox value={value} onChange={setValue} options={options} filter={fuzzy} placeholder="输入拼音 / 区号 / 城市名" clearable />
        <p className="adm-hint">
          自定义 <code>filter(query, option)</code> 同时匹配中文名、拼音和区号 — 试试输入 "021"。
        </p>
    </>
  );
}
import { useState } from 'react';
import { CfCombobox } from '@chufix-design/react';

export default function Demo() {
  const [value, setValue] = useState<string | number | null>(null);

  const options= [
    { value: 'beijing', label: '北京 · BJ · 010' },
    { value: 'shanghai', label: '上海 · SH · 021' },
    { value: 'guangzhou', label: '广州 · GZ · 020' },
    { value: 'shenzhen', label: '深圳 · SZ · 0755' },
    { value: 'hangzhou', label: '杭州 · HZ · 0571' },
    { value: 'chengdu', label: '成都 · CD · 028' },
  ];

  // 拼音首字母 / 区号 / 全称都能匹配
  function fuzzy(query, opt): boolean {
    if (!query) return true;
    const q = query.toLowerCase();
    return (
      opt.label.toLowerCase().includes(q) ||
      String(opt.value).toLowerCase().includes(q)
    );
  }
  return (
    <>
      <CfCombobox value={value} onChange={setValue} options={options} filter={fuzzy} placeholder="输入拼音 / 区号 / 城市名" clearable />
        <p className="adm-hint">
          自定义 <code>filter(query, option)</code> 同时匹配中文名、拼音和区号 — 试试输入 "021"。
        </p>
    </>
  );
}

API

Props

属性类型默认值说明
modelValue (Vue) / value (React)string | number | nullnull当前值
optionsComboboxOption[][]{ value, label, disabled? } 列表
placeholderstring'请选择或输入'占位文案
variant'outline' | 'filled' | 'ghost''outline'视觉模式
size'sm' | 'md' | 'lg''md'尺寸
disabledbooleanfalse禁用
clearablebooleanfalse显示清除按钮
errorbooleanfalse错误态
allowCreatebooleanfalse是否允许把输入作为新值提交
filter(query: string, option: ComboboxOption) => boolean默认按 label 与 value 子串匹配自定义过滤函数
emptyTextstring'无匹配项'无结果时的占位文字
namestring透传给隐式表单字段,用于原生表单提交
idstring触发器的 id,便于外部 <label for> 关联

Events

Vue 事件React 回调载荷类型说明
update:modelValueonChangestring | number | null值变化时触发
changeonChangestring | number | nullupdate:modelValue 同步触发,载荷相同
createonCreatestringallowCreate=true 时,按 Enter 或点击「创建 xxx」选项时触发,载荷是用户输入的原始字符串

反馈与讨论

Combobox 组合选择 的讨论

0
0 / 600
正在加载评论...