電商購物體驗
付款成功面板
底部付款面板的完整流程 React 元件:面板滑上、付款鈕縮成處理中圓環、圓環形變成勾並脈衝一次、訂單摘要依序展開。
結帳最後一步的底部面板。按下付款後,全寬的付款鈕寬度收合成 56px 的圓,裡面轉起進度環;付款成功時環收合、勾一筆畫出,整顆按鈕脈衝一次,接著標題換成「付款成功」,訂單編號、付款時間、預計到貨等摘要列以 70ms 間隔依序展開,最後呼叫 onDone。面板可拖曳把手往下關閉,處理中則鎖住所有關閉途徑。開啟狀態與付款階段各自支援受控/非受控雙模式。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add payment-success-sheetPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
340
編輯中的呼叫預設屬性
<PaymentSuccessSheet/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/payment-success-sheet.json或在 components.json 設定 registries 後,改用 @webberui/payment-success-sheet 安裝。
使用
import { PaymentSuccessSheet } from "@/components/ui/payment-success-sheet";
<PaymentSuccessSheet
items={[
{ id: "cushion", name: "亞麻抱枕套", qty: 1, price: 680 },
{ id: "tray", name: "手作木托盤", qty: 1, price: 600 },
]}
total={1280}
currency="TWD"
paymentMethod="•••• 4242"
summary={[
{ label: "訂單編號", value: "MB-20261025-0412" },
{ label: "合計", value: "NT$1,280" },
{ label: "預計到貨", value: "10/28(二)" },
]}
onPay={async () => {
// 呼叫你的金流 API;resolve 進入成功,reject 回到待付款
await chargeCard();
}}
onDone={() => console.log("動畫結束,可以接著出單")}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
open | boolean | — | 受控的面板開啟狀態;未提供時由元件內部管理 |
defaultOpen | boolean | false | 非受控模式的初始開啟狀態 |
onOpenChange | (open: boolean) => void | — | 開啟狀態變更時回呼 |
stage | "idle" | "processing" | "success" | — | 受控的付款階段;提供時由父層推進 |
defaultStage | "idle" | "processing" | "success" | "idle" | 非受控模式的初始階段 |
onStageChange | (stage: PaymentSuccessSheetStage) => void | — | 付款階段變更時回呼 |
items | PaymentSuccessSheetItem[] | — | 購物清單 |
total | number | — | 應付合計 |
currency | string | "TWD" | 幣別代碼,決定金額前綴(TWD → NT$、USD → $、JPY → ¥、EUR → €、HKD → HK$,其他直接顯示代碼) |
paymentMethod | ReactNode | — | 付款方式列內容;未提供則不顯示該列 |
summary | PaymentSuccessSheetSummaryRow[] | 付款方式+合計 | 成功後依序展開的摘要列 |
onPay | () => Promise<void> | void | — | 按下付款鈕時呼叫;非受控時 resolve 進入 success、reject 回到 idle |
onDone | () => void | — | 成功動畫與摘要全部展開後呼叫 |
stiffness | number | 340 | 面板滑上 spring 的 stiffness(damping 固定 34) |
showToast | boolean | true | 成功後在面板上方浮出「已加入訂單」提示 2 秒 |
showTrigger | boolean | true | 是否顯示內建的「前往結帳」觸發鈕 |
contained | boolean | false | 面板定位在最近的 positioned 祖先內,且不鎖 body 捲動(適合示範框、預覽) |
labels | Partial<PaymentSuccessSheetLabels> | — | 介面文字覆寫 |
className | string | — | 追加到根元素 |
PaymentSuccessSheetItem
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別 |
name | string | — | 商品名稱 |
qty | number | — | 數量 |
price | number | — | 單價;列上顯示單價 × 數量 |
thumb | ReactNode | 名稱首字方塊 | 縮圖 |
PaymentSuccessSheetSummaryRow
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
label | string | — | 摘要列標題 |
value | ReactNode | — | 摘要列內容 |
PaymentSuccessSheetLabels
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
trigger | string | "前往結帳" | 內建觸發鈕文字 |
title | string | "確認付款" | 付款前標題 |
successTitle | string | "付款成功" | 成功後標題 |
pay | string | "付款" | 付款鈕前綴,後接合計金額 |
processing | string | "付款處理中" | 處理中的螢幕閱讀器宣告 |
processingButton | string | "處理中…" | 減少動態效果時按鈕上的文字 |
success | string | "付款成功" | 成功的螢幕閱讀器宣告 |
paymentMethod | string | "付款方式" | 付款方式列標題 |
total | string | "合計" | 合計列標題 |
close | string | "關閉" | 關閉鈕的無障礙名稱 |
dragHandle | string | "往下拖曳以關閉" | 拖曳把手提示 |
toast | string | "已加入訂單" | 成功提示文字 |
細節
- 節奏:面板以 spring(
stiffness預設 340、damping 34)從底部滑上,遮罩 200ms 淡入;付款鈕寬度 morph 用 bounce 0、0.26 秒的 spring;勾 400ms 畫出、脈衝 500ms(scale 1 → 1.12 → 1,外圈擴散淡出)。 - 成功色走 CSS 變數
--wb-pss-success(預設#10b981),可在主題層覆寫。 - 非受控時處理中至少停留 0.8 秒,避免
onPay太快 resolve 讓進度環一閃而過;受控時元件只通知onStageChange("processing"),由你決定何時切到success。 - 非受控模式下,成功後關閉面板會重置回
idle,下次打開是新的一筆結帳。 - 拖曳只從頂部把手啟動:往下超過 100px 或速度超過 500px/s 即關閉,未達門檻彈回。
- 想在付款成功後出一張收據,可把同批次的 receipt-printer(Pro)接在
onDone之後;本元件不依賴它。 - 金額格式化是確定性的(自行加千分位,不用
Intl.NumberFormat),伺服器與瀏覽器輸出一致,不會造成 hydration 不一致。
可及性
- 面板為
role="dialog"、aria-modal="true",以aria-labelledby指向標題;開啟時焦點移到付款鈕,Tab/Shift+Tab 鎖在面板內,關閉後焦點回到開啟前的元素 - Esc、點遮罩、拖曳把手、關閉鈕都能關閉面板;付款處理中這些途徑全部停用
- 付款鈕處理中設定
aria-busy並停用,縮成圓形時以aria-label保留可讀名稱 - 隱藏的
role="status"、aria-live="polite"區域依序宣告「付款處理中」「付款成功」 - 使用者系統開啟「減少動態效果」時,面板改為 150ms 淡入、付款鈕不 morph 而是直接換成「處理中…」文字、勾直接出現且不脈衝、摘要列直接顯示、停用拖曳