WebberUI

超商取貨選店

超商取貨三段選擇:通路→縣市/區→門市清單,含搜尋、營業資訊與已選門市摘要卡;品牌中性、資料由 props 提供

這是 WebberUI Pro 元件

上線活動期間免費:註冊或登入後,在上方預覽區按「複製安裝指令」就能直接安裝,不需付費、不用綁信用卡。下方那條指令未登入時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

台灣電商結帳最常見的「超商取貨」流程,做成一個自足的步驟式面板:第一步用色塊 chips 選通路,第二步從門市資料自動推導出縣市與鄉鎮市區下拉,第三步在可搜尋的清單裡挑門市——每筆顯示地址、營業時間、24 小時徽章與(可選的)距離文字。點選門市後面板收合成摘要卡(通路色塊+門市名+地址+「更換」按鈕)並回呼 onSelect(store)。步驟切換用水平滑動過場、清單項目依序進場、選中項打勾;支援受控 value 與非受控 defaultValueisLoading 載入骨架與全部可覆寫的文案。元件不含任何真實品牌 logo 或名稱,通路與門市資料一律由 props 傳入。

載入預覽⋯
npx shadcn@latest add "https://webberui.com/r/convenience-store-pickup.json?t=<安裝 token>"

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

<ConvenienceStorePickup />

安裝

npx shadcn@latest add "https://webberui.com/r/convenience-store-pickup.json?t=<安裝 token>"

或在 components.json 設定 registries 後,改用 @webberui/convenience-store-pickup 安裝。

使用

import {
  ConvenienceStorePickup,
  type PickupChain,
  type PickupStore,
} from "@/components/ui/convenience-store-pickup";

// 通路與門市皆由你的 API 提供;此處為虛構示意
const chains: PickupChain[] = [
  { id: "sudah", name: "速達超商", color: "#e8541e" },
  { id: "neighbor", name: "好鄰居便利店", color: "#2e7d32" },
];

const stores: PickupStore[] = [
  {
    id: "sd-001",
    chainId: "sudah",
    name: "大安樂活門市",
    address: "臺北市大安區樂活路 12 號",
    city: "臺北市",
    district: "大安區",
    open24h: true,
    distanceText: "350 公尺",
  },
  {
    id: "nb-001",
    chainId: "neighbor",
    name: "大安木棉店",
    address: "臺北市大安區木棉巷 6 號",
    city: "臺北市",
    district: "大安區",
    hours: "07:00–22:30",
  },
];

// 受控:把門市 id 存進結帳表單狀態
<ConvenienceStorePickup
  chains={chains}
  stores={stores}
  value={form.pickupStoreId}
  onChange={(id) => setForm({ ...form, pickupStoreId: id })}
  onSelect={(store) => console.log("取貨門市", store.name, store.address)}
/>

// 非受控:只想知道最後選了誰
<ConvenienceStorePickup
  chains={chains}
  stores={stores}
  defaultValue="sd-001"
  onSelect={(store) => console.log(store.id)}
  compact
/>

Props

Prop型別預設值說明
chainsPickupChain[]通路清單,決定第一步 chips 的內容與順序
storesPickupStore[]門市清單;縣市/區選項與門市清單都由這份資料推導
valuestring | null受控值:目前選中的門市 id(null 代表尚未選擇);不提供時為非受控模式
defaultValuestring | nullnull非受控模式的初始門市 id
onSelect(store: PickupStore) => void使用者點選門市時回呼,帶入完整門市物件
onChange(storeId: string) => void選中門市 id 變動時回呼;受控模式請在此更新 value
titlestring面板標題;等同 labels.title 的捷徑,兩者同時提供時以此為準
labelsPartial<ConvenienceStorePickupLabels>內建繁中覆寫介面文案(部分覆寫即可,其餘沿用內建)
isLoadingbooleanfalse顯示載入骨架(門市資料尚未就緒時)
showDistancebooleantrue是否顯示門市的 distanceText
compactbooleanfalse緊湊模式:縮小間距、隱藏提示、步驟副標與清單裡的營業時間,適合放進結帳側欄
classNamestring透傳到最外層容器

PickupChain

欄位型別預設值說明
idstring通路唯一 id,對應 PickupStore.chainId
namestring通路顯示名稱
colorstring代表色(任何合法 CSS 顏色字串),用於 chips 與摘要卡的色塊;省略時為中性灰

PickupStore

欄位型別預設值說明
idstring門市唯一 id,也是 value 的內容
chainIdstring所屬通路 id
namestring門市名稱
addressstring完整地址(清單與摘要卡顯示)
citystring縣市,用來推導第二步的縣市下拉
districtstring鄉鎮市區,用來推導第二步的區下拉
hoursstring營業時間文字,例如「07:00–23:00」
open24hboolean是否 24 小時營業;為 true 時顯示徽章並優先於 hours
distanceTextstring距離文字(例如「350 公尺」),由呼叫端算好傳入;showDistance 為 false 時不顯示

ConvenienceStorePickupLabels

欄位型別預設值說明
titlestring"超商取貨"面板標題
stepChainstring"取貨通路"步驟一名稱
stepAreastring"取貨地區"步驟二名稱
stepStorestring"取貨門市"步驟三名稱
chainHintstring"請選擇要取貨的超商通路"步驟一提示文字
areaHintstring"請選擇縣市與鄉鎮市區"步驟二提示文字
citystring"縣市"縣市下拉的標籤
districtstring"鄉鎮市區"鄉鎮市區下拉的標籤
cityPlaceholderstring"選擇縣市"縣市下拉未選時的佔位文字
districtPlaceholderstring"選擇鄉鎮市區"鄉鎮市區下拉未選時的佔位文字
nextstring"查看門市"步驟二已選好地區後前往門市清單的按鈕文字
searchPlaceholderstring"搜尋門市名稱或地址"門市搜尋框的佔位文字
storeUnitstring"間門市"門市數量的單位(顯示為「N 間門市」)
open24hstring"24 小時"24 小時營業徽章文字
emptyChainsstring"目前沒有可選的通路"沒有任何通路可選時的空狀態文字
emptyAreasstring"此通路目前沒有可取貨的門市"該通路沒有任何門市時的空狀態文字
emptyStoresstring"找不到符合的門市"搜尋或篩選後沒有門市時的空狀態文字
selectedstring"已選門市"摘要卡標題
changestring"更換"摘要卡上重新選擇的按鈕文字
loadingstring"門市資料載入中…"載入骨架的無障礙朗讀文字

另外具名匯出 filterPickupStores(stores, { chainId, city, district, query }),與元件內部相同的篩選邏輯(關鍵字同時比對門市名稱與地址、忽略大小寫),方便在表單層計算門市數量或做伺服器端預篩。

細節

  • 三步導覽:步驟列可點回已完成的步驟;尚未達成前置條件的步驟(例如還沒選通路就想選門市)為停用。換通路會清掉已選縣市/區,同一通路則保留。選好區後自動前進到門市清單,回到第二步時另有「查看門市」按鈕。
  • 選項由資料推導:縣市與鄉鎮市區皆從該通路的 stores 去重取得,並保留你傳入的排序(例如由北到南);不需要另外維護行政區表。
  • 收合成摘要卡:點選門市後步驟面板收合、只留摘要卡,按「更換」重新展開並直接回到該門市所在的清單、預先把它設為 active。受控模式下 value 從外部變成 null 也會自動展開面板。
  • 距離文字由你決定:元件不做定位或距離計算,只顯示 distanceTextshowDistance={false} 可整體隱藏。
  • 動畫:步驟切換為方向感知的水平滑動(前進向左、後退向右)、清單前 8 筆依序進場、選中打勾用彈簧縮放、摘要卡以高度展開。

可及性

  • 步驟列為 ol 清單,目前步驟以 aria-current="step" 標示,未達前置條件的步驟以 disabled 停用
  • 通路 chips 為 role="group" 內的 aria-pressed 按鈕;縣市/區使用原生 select(含 label),行動裝置直接呼叫系統選單
  • 門市清單為 role="listbox"(可 Tab 聚焦)搭配 role="option"aria-selected,以 aria-activedescendant 指向目前項目;支援 ↑/↓/Home/End 移動、Enter/Space 選取,搜尋框按 ↓ 可直接跳進清單,Enter 選取目前項目(中文輸入法組字中不會誤觸)
  • 選定門市後焦點自動移到摘要卡的「更換」按鈕;按「更換」後焦點回到搜尋框,鍵盤流程不斷裂;門市數量以 aria-live="polite" 朗讀
  • isLoading 骨架以 role="status"aria-busy 與 sr-only 文字告知載入中
  • 使用者系統開啟「減少動態效果」時,步驟切換改為淡入淡出、清單不再依序進場、打勾與摘要卡取消縮放/高度動畫,只保留即時切換

本頁目錄