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)為陣列 | |
options | SwSelectOption[] | ✓ | — | 選項列表,每項 { value, label, disabled?, group? } |
multiple | boolean | false | 多選模式(v-model 為陣列) | |
placeholder | string | '请选择' | 未選時觸發器佔位文案(元件預設值為簡體中文) | |
disabled | boolean | false | 整體禁用 | |
clearable | boolean | false | 有選中值時顯示一鍵清空按鈕 | |
searchable | boolean | false | 可搜尋形態:觸發器變輸入框,輸入按 label 本地過濾(大小寫不敏感) | |
creatable | boolean | false | 輸入無精確命中時顯示「创建 "xxx"」項並 emit create;僅 searchable 下生效 | |
remote | boolean | false | 遠端搜尋:關掉本地 label 過濾(options 即遠端結果集),配合 @search;僅 searchable 下有意義 | |
loading | boolean | false | 遠端搜尋進行中:下拉顯示「載入中…」並壓掉空態,當前列表保留不清空 | |
searchDebounce | number | 300 | @search 防抖毫秒數,0 = 不防抖 |
SwSelectOption:{ value: string \| number; label: string; disabled?: boolean; group?: string }(可從套件名匯入:import type { SwSelectOption } from '@swift-web/framework')
Events
| 名稱 | 攜帶 | 說明 |
|---|---|---|
update:modelValue | value | value[] | undefined | 選中變化(清空時單選為 undefined、多選為 []) |
update:open | open: boolean | 彈層開 / 合 |
clear | — | 點擊清空按鈕觸發 |
create | label: string | (searchable + creatable)選中「创建 "xxx"」項時觸發;元件不改 v-model,業務建項後自行推 options + v-model |
search | term: 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-width | 8rem | 觸發器最小寬 |
--sw-select-trigger-min-height | var(--control-height) | 觸發器最小高 |
--sw-select-trigger-padding | 0.25rem 0.75rem | 觸發器內邊距 |
--sw-select-trigger-border | var(--input) | 觸發器描邊色 |
--sw-select-trigger-radius | var(--radius) | 觸發器圓角 |
--sw-select-trigger-bg | var(--background) | 觸發器底色 |
--sw-select-trigger-color | var(--foreground) | 觸發器文字色 |
--sw-select-trigger-font-size | var(--control-font-size) | 觸發器字號 |
--sw-select-chip-bg | var(--muted) | 多選 chips 底色(multiple + searchable) |
--sw-select-chip-color | var(--foreground) | 多選 chips 文字色 |
--sw-select-chip-radius | calc(var(--radius) - 0.25rem) | 多選 chips 圓角 |
--sw-select-content-max-height | 18rem | 彈層最大高 |
--sw-select-content-bg | var(--popover) | 彈層底色 |
--sw-select-content-border | var(--border) | 彈層描邊色 |
--sw-select-content-radius | var(--radius) | 彈層圓角 |
--sw-select-content-shadow | var(--shadow-md) | 彈層陰影 |
--sw-select-content-font-size | var(--control-font-size) | 彈層字號 |
--sw-select-content-z | 1100 | 彈層層級(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 排序。