SwSelect

SwSelect 是下拉選擇器:選項收進彈層、觸發器收合態。單 / 多選一個元件(multiple 開關),支援可清空(clearable)、分組(選項 group 欄位)、可搜尋(searchable)與輸入即建(creatable)。受控 v-model,單選為單值、多選為陣列。

薄殼套 reka-ui:預設基於 Select 原語;searchable 時內部切換 Combobox 原語(觸發器變輸入框),對外仍是同一套 props / v-model / 插槽。彈層自渲染(不是原生 <select> 的 OS 彈層),故完全可定制;鍵盤導覽 / 彈層定位 / a11y 開箱自帶。視覺走「最小必要基礎樣式」——觸發器 / 彈層 / 選中 / 高亮 / 禁用態預設都肉眼可辨,顏色 / 圓角等由 applier 覆蓋。

選型:選項多、要省橫向空間時用本元件(下拉)。選項少(2–5)、要全部可見、省一次點擊時用 SwSegmented(平鋪)。兩者互補,如 antd 同時有 Select 與 Segmented。

單選

單選(狀態篩選)

加载中...
<script setup lang="ts">
const status = ref()
</script>

<template>
  <SwSelect
    v-model="status"
    :options="[
      { value: 'all', label: '全部状态' },
      { value: 'ongoing', label: '连载中' },
      { value: 'completed', label: '已完结' },
    ]"
    placeholder="选择状态"
    aria-label="状态筛选"
  />

  <!-- 整體禁用:父表單 pending / 無權限時鎖住選擇器 -->
  <SwSelect v-model="status" :options="options" disabled />
</template>

多選 + 可清空

multiple 讓 v-model 變陣列;clearable 在有選中值時顯示一鍵清空按鈕。多選時觸發器預設顯示「已選 N 項」,要展示 tags / chips 走 #trigger 插槽:

多選 + 可清空(觸發器顯示計數)

加载中...

分組

選項帶 group 欄位時,連續同名歸一組、組首渲染分組標題;不帶 group 則扁平:

選項分組(直轄市 / 浙江)

加载中...
<template>
  <SwSelect
    v-model="city"
    :options="[
      { value: 'bj', label: '北京', group: '直辖市' },
      { value: 'sh', label: '上海', group: '直辖市' },
      { value: 'hz', label: '杭州', group: '浙江' },
    ]"
  />
</template>

可搜尋(searchable)

searchable 讓觸發器變輸入框:輸入按 label 本地過濾(大小寫不敏感包含匹配,組內濾空整組隱藏),↑↓ + Enter 可選。選項幾十上百(演職員 / 標籤 / 城市)時用它;集合大到不能一次拉全(作品庫 / 全站使用者)時用下面的遠端搜尋。

可搜尋單選(輸入過濾)

加载中...

單選時輸入框常顯選中項 label(彈層開啟後未輸入前不過濾,避免被選中值鎖死列表);多選時已選項渲染為 chips(label + ✕ 移除,Backspace 空輸入刪末位)。

遠端搜尋(remote + @search + loading)

三件套組合出「從大集合裡搜一個選一個」:remote 關掉本地 label 過濾(options 本身就是遠端結果集——伺服端按拼音 / 別名 / id 命中的項 label 未必含關鍵字,本地再濾一遍會把它們藏掉);@search(term) 把使用者輸入交給業務(按 searchDebounce 防抖,預設 300ms;點清空立即發 '',據此復位列表);loading 在下拉裡顯示「載入中…」並壓掉「無相符」空態,當前列表保留不清空(防每次擊鍵閃爍)。

結果陳舊期(使用者已敲字、結果尚未回來:防抖視窗 ∪ loading)當前列表保留但不可選(選項 disabled + 變淡)——保留防塌陷跳動,不可選防「手快點下去選了與當前查詢無關的對象」;creatable 的「創建」項同期藏掉。options 被整換後,已選項即使不在新結果集裡,單選回顯 / 多選 chips 的 label 也不會丟——元件內部快取曾被選中值的 label(只記選過的,有界),同 value 的 label 後來變了以最新為準。

兩條語義歸業務:@search 收到的 '' 只表示「搜尋詞已清空」——復位 / 回初始列表 / 不搜由你定,不等於搜全部;remote 下 creatable 的「精確命中」只擋已載入頁裡的重複,真正的去重保證在伺服端(唯一約束)。

遠端搜尋(模擬 500ms 介面,按導演名也能命中)

加载中...
<SwSelect v-model="mediaId" searchable remote :loading="pending" :options="mediaOptions" @search="onSearch" />
async function onSearch(term: string) {
  pending.value = true;
  mediaOptions.value = await api.media.options({ keyword: term }); // 整換結果集
  pending.value = false;
}

輸入即建(creatable,標籤選擇場景)

creatable(僅 searchable 下生效)在輸入無精確命中(大小寫不敏感)時於列表尾部追加「创建 "xxx"」項。選中它不改 v-model,只發 @create(payload 為 trim 後輸入文字)——業務調後端建項後自行把新 option 推進 options + v-model。multiple + searchable + creatable 組合即 tag-input 形態:

標籤選擇:多選 + 搜尋 + 輸入即建

加载中...
<script setup lang="ts">
async function onCreateTag(name: string) {
  const tag = await createTag(name) // 業務調後端建項
  tagOptions.value.push({ value: tag.id, label: tag.name })
  tagIds.value = [...tagIds.value, tag.id] // 推進 options + v-model
}
</script>

<template>
  <SwSelect v-model="tagIds" multiple searchable creatable :options="tagOptions" @create="onCreateTag" />
</template>

Props

名稱類型必填預設值說明
modelValue(v-model)string | number | (string | number)[]undefined受控值。單選為單值,多選(multiple)為陣列
optionsSwSelectOption[]✓—選項列表,每項 { value, label, disabled?, group? }
multiplebooleanfalse多選模式(v-model 為陣列)
placeholderstring'请选择'未選時觸發器佔位文案(元件預設值為簡體中文)
disabledbooleanfalse整體禁用
clearablebooleanfalse有選中值時顯示一鍵清空按鈕
searchablebooleanfalse可搜尋形態:觸發器變輸入框,輸入按 label 本地過濾(大小寫不敏感)
creatablebooleanfalse輸入無精確命中時顯示「创建 "xxx"」項並 emit create;僅 searchable 下生效
remotebooleanfalse遠端搜尋:關掉本地 label 過濾(options 即遠端結果集),配合 @search;僅 searchable 下有意義
loadingbooleanfalse遠端搜尋進行中:下拉顯示「載入中…」並壓掉空態,當前列表保留不清空
searchDebouncenumber300@search 防抖毫秒數,0 = 不防抖

SwSelectOption:{ value: string \| number; label: string; disabled?: boolean; group?: string }(可從套件名匯入:import type { SwSelectOption } from '@swift-web/framework')

Events

名稱攜帶說明
update:modelValuevalue | value[] | undefined選中變化(清空時單選為 undefined、多選為 [])
update:openopen: boolean彈層開 / 合
clear—點擊清空按鈕觸發
createlabel: string(searchable + creatable)選中「创建 "xxx"」項時觸發;元件不改 v-model,業務建項後自行推 options + v-model
searchterm: string(searchable)使用者輸入即發,payload 為 trim 後的搜尋詞,按 searchDebounce 防抖;點清空立即發 ''。單選回顯 label 的程式性同步不發;非 remote 下也發(本地過濾照舊),便於埋點 / 聯動

Slots

名稱作用域參數說明
trigger{ selectedLabel, selectedOptions, count, hasValue }自訂觸發器內容。不傳則裸文案。僅非 searchable 分支有效(searchable 觸發器是輸入框)
option{ option, selected }自訂單個選項視覺。不傳則渲染 option.label

樣式定制 Token

觸發器與彈層皮膚經 --sw-select-* token 覆蓋(fallback 即預設視覺)。彈層 Portal 到 body,token 在 :root(或任意 body 祖先)覆蓋即可,無需 :global 穿透:

Token預設值作用點
--sw-select-trigger-min-width8rem觸發器最小寬
--sw-select-trigger-min-heightvar(--control-height)觸發器最小高
--sw-select-trigger-padding0.25rem 0.75rem觸發器內邊距
--sw-select-trigger-bordervar(--input)觸發器描邊色
--sw-select-trigger-radiusvar(--radius)觸發器圓角
--sw-select-trigger-bgvar(--background)觸發器底色
--sw-select-trigger-colorvar(--foreground)觸發器文字色
--sw-select-trigger-font-sizevar(--control-font-size)觸發器字號
--sw-select-chip-bgvar(--muted)多選 chips 底色(multiple + searchable)
--sw-select-chip-colorvar(--foreground)多選 chips 文字色
--sw-select-chip-radiuscalc(var(--radius) - 0.25rem)多選 chips 圓角
--sw-select-content-max-height18rem彈層最大高
--sw-select-content-bgvar(--popover)彈層底色
--sw-select-content-bordervar(--border)彈層描邊色
--sw-select-content-radiusvar(--radius)彈層圓角
--sw-select-content-shadowvar(--shadow-md)彈層陰影
--sw-select-content-font-sizevar(--control-font-size)彈層字號
--sw-select-content-z1100彈層層級(fw#332 z 層旋鈕)。彈層 Portal 到 body,與其它浮層撞層時改這個

高度 / 字號預設吃表單控件族語義 token --control-height(32px)/ --control-font-size(14px)——SwInput / SwSelect / SwSegmented 同行混排預設等高;覆蓋這兩個 token 全站控件同步縮放,元件級 token 優先級更高。

注意事項

  • 彈層經 Portal 傳送到 body:applier 要改彈層樣式(如字級)須用全域 / :global 選擇器(scoped :deep 搆不到),命中 .sw-select-content。
  • 無內建視覺品牌:元件只給最小必要基礎樣式;顏色 / 圓角等由 applier 覆蓋。
  • 多選觸發器預設顯示「已選 N 項」:要展示 tags / chips 等用 #trigger 插槽自訂。
  • searchable 預設是本地過濾:只按 label 大小寫不敏感包含匹配;遠端搜尋必須加 remote 關掉它,否則伺服端按別名 / 拼音命中的項會被本地過濾藏掉。資料來源仍歸業務(元件只交出 @search 的搜尋詞、承接 options)。
  • @create 不改 v-model:選中「创建 "xxx"」項只發事件——業務建項後必須自己把新 option 推進 options 並更新 v-model,否則介面無變化。
  • searchable 時 #trigger 插槽不生效:搜尋形態的觸發器是輸入框(多選為 chips + 輸入框),無自訂觸發器插槽。
  • multiple 時 v-model 必須是陣列:多選把 v-model 初始化為單值(如 ref(''))會讓選中態、「已選 N 項」計數全錯且型別崩——多選用 ref([])、單選用單值。
  • 分組按「連續同名」歸組:同名 group 的選項必須相鄰排列,散落的同名 group 會渲染成多個同名分組塊——options 傳入前先按 group 排序。

相關