US-302: 客戶列表與搜尋
User Story
作為 店員 我想要 能夠快速搜尋與過濾客戶列表 以便 找到目標客戶並查看其詳細資訊
驗收標準 (Acceptance Criteria)
Scenario 1: 查看客戶列表(預設排序)
- Given 我是已登入的店員
- And 系統中有 50 筆客戶資料
- When 我進入「客戶管理」頁面
- Then 系統應顯示客戶列表(預設每頁 20 筆)
- And 列表應依「註冊日期」降序排列(最新客戶在最上方)
- And 每筆客戶應顯示:
- 客戶編號
- 姓名/公司名稱
- 客戶類型(個人/企業)
- 客戶等級徽章(VIP/VVIP)
- 電話
- 累計消費金額
- 最後消費日期
- 客戶狀態(啟用/停用)
- And 列表底部應顯示分頁導航(頁碼 1/3,每頁 20 筆)
Scenario 2: 搜尋客戶(姓名)
- Given 我在「客戶管理」頁面
- And 系統中有客戶「王小明」與「王大明」
- When 我在搜尋框輸入「王小明」
- And 系統防抖 500ms 後自動搜尋
- Then 列表應僅顯示「王小明」
- And 搜尋框右側應顯示「清空」按鈕
Scenario 3: 搜尋客戶(部分電話號碼)
- Given 我在「客戶管理」頁面
- And 系統中有客戶「王小明」電話「0912-345-678」
- When 我在搜尋框輸入「0912」
- Then 列表應顯示電話包含「0912」的所有客戶
- And 「王小明」應出現在結果中
Scenario 4: 依客戶等級過濾
- Given 我在「客戶管理」頁面
- And 系統中有 10 筆 VIP 客戶
- When 我點擊「過濾」按鈕
- And 我勾選「客戶等級: VIP」
- And 我點擊「套用」按鈕
- Then 列表應僅顯示 VIP 客戶
- And 過濾條件應以標籤形式顯示在搜尋框下方
- And 標籤應可單獨移除
Scenario 5: 組合搜尋與過濾(客戶類型 + 日期範圍)
- Given 我在「客戶管理」頁面
- When 我勾選「客戶類型: 企業客戶」
- And 我設定「註冊日期範圍: 2025-10-01 ~ 2025-10-31」
- And 我點擊「套用」按鈕
- Then 列表應僅顯示符合條件的企業客戶
- And 顯示筆數應更新
- And 過濾條件標籤應顯示「企業客戶」與「2025-10-01 ~ 2025-10-31」
Scenario 6: 清空過濾條件
- Given 我已套用過濾條件(客戶類型: 企業客戶)
- When 我點擊過濾條件標籤上的「移除」圖示
- Then 該過濾條件應被移除
- And 列表應恢復顯示所有客戶
- And 分頁應重置為第一頁
Scenario 7: 切換每頁筆數
- Given 我在「客戶管理」頁面
- And 當前每頁顯示 20 筆
- When 我選擇「每頁筆數: 50」
- Then 列表應重新載入並顯示 50 筆客戶
- And 分頁資訊應更新
Scenario 8: 排序客戶列表(依累計消費金額)
- Given 我在「客戶管理」頁面
- When 我點擊「累計消費金額」欄位標題
- Then 列表應依累計消費金額降序排列(高 → 低)
- And 欄位標題應顯示向下箭頭
- When 我再次點擊「累計消費金額」欄位標題
- Then 列表應改為升序排列(低 → 高)
- And 欄位標題應顯示向上箭頭
Scenario 9: 點擊客戶進入詳情頁
- Given 我在「客戶管理」頁面
- And 列表中顯示客戶「王小明」
- When 我點擊「王小明」的客戶編號或姓名
- Then 系統應導航至客戶詳情頁
- And 詳情頁應顯示完整的客戶資訊
Scenario 10: 空搜尋結果處理
- Given 我在「客戶管理」頁面
- When 我搜尋「不存在的客戶名稱XYZABC」
- And 系統無任何符合條件的結果
- Then 列表應顯示「查無符合條件的客戶」訊息
- And 應顯示「清空搜尋條件」按鈕
- And 分頁導航應隱藏
Scenario 11: 響應式設計(手機版)
- Given 我使用手機瀏覽器訪問「客戶管理」頁面
- Then 列表應以卡片形式顯示(非表格)
- And 每張卡片應包含:
- 客戶編號與等級徽章
- 姓名/公司名稱
- 電話
- 累計消費金額
- 最後消費日期
- And 過濾面板應以摺疊方式顯示
業務規則 (Business Rules)
-
預設排序
- 初次進入頁面: 依註冊日期降序(最新客戶在上)
- 用戶可自行切換排序欄位與順序
-
搜尋邏輯
- 支援模糊搜尋(姓名/公司名稱/電話/地址)
- 不區分大小寫
- 防抖延遲: 500ms
- 電話搜尋支援部分號碼匹配
-
過濾條件
- 客戶類型: 個人 / 企業(單選)
- 客戶等級: 普通 / VIP / VVIP(多選)
- 客戶狀態: 啟用 / 停用(單選)
- 客戶標籤: 可多選
- 註冊日期範圍: 起始日期 ~ 結束日期(閉區間)
- 最後消費日期範圍: 起始日期 ~ 結束日期(閉區間)
- 累計消費金額區間: 最低金額 ~ 最高金額(閉區間)
-
分頁規則
- 預設每頁 20 筆
- 可選: 10 / 20 / 50 / 100 筆/頁
- 搜尋或過濾變更時,自動重置為第一頁
-
停用客戶顯示
- 預設不顯示停用客戶
- 若過濾條件選擇「客戶狀態: 停用」,則顯示停用客戶
- 停用客戶以灰色標示
-
多租戶隔離
- 列表僅顯示當前租戶的客戶
- API 層級自動過濾
UI/UX 需求 (UI/UX Requirements)
頁面佈局
桌面版
┌─────────────────────────────────────────────────┐
│ 客戶管理 [新增客戶] │
├─────────────────────────────────────────────────┤
│ [搜尋框................................] 🔍 │
│ [過濾條件: 企業客戶 ×] [VIP ×] [過濾 ▼] │
├─────────────────────────────────────────────────┤
│ 客戶編號 姓名 類型 等級 電話 消費金額 │
│ ABC-001 王小明 個人 VIP 0912.. $5,000 │
│ ABC-002 XX公司 企業 VVIP 02-12.. $25,000 │
│ ... │
├─────────────────────────────────────────────────┤
│ 顯示 1-20 / 共 50 筆 [10▼] 筆/頁 [< 1 2 3 >]│
└─────────────────────────────────────────────────┘
手機版(卡片模式)
┌─────────────────────┐
│ [搜尋框.........] 🔍 │
│ [過濾 ▼] │
├─────────────────────┤
│ ┌─────────────────┐ │
│ │ ABC-001 [VIP]│ │
│ │ 王小明 │ │
│ │ 0912-345-678 │ │
│ │ 累計: $5,000 │ │
│ │ 最後: 2025/10/31│ │
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ ABC-002 [VVIP]│ │
│ │ XX 科技公司 │ │
│ │ 02-1234-5678 │ │
│ │ 累計: $25,000 │ │
│ │ 最後: 2025/11/01│ │
│ └─────────────────┘ │
├─────────────────────┤
│ [< 1 2 3 >] │
└─────────────────────┘
互動行為
-
搜尋框
- 左側搜尋圖示
- 右側清空按鈕(有輸入內容時顯示)
- 防抖 500ms
- Placeholder: 「搜尋客戶姓名、電話、地址...」
-
過濾面板
- 點擊「過濾」按鈕展開過濾面板(Dropdown)
- 面板包含所有過濾條件(客戶類型、等級、狀態、標籤、日期、金額)
- 「清空所有」按鈕(重置所有過濾條件)
- 「套用」按鈕(套用並關閉面板)
-
過濾條件標籤
- 顯示在搜尋框下方
- 每個標籤可單獨移除(點擊 × 圖示)
- 標籤顏色: DaisyUI
badge-primary
-
列表表格
- 點擊欄位標題可排序(升序/降序)
- 點擊客戶編號或姓名進入詳情頁
- Hover 時列顯示背景色
- 停用客戶以灰色標示
-
分頁導航
- 頁碼快速跳轉(1 ... 5 6 7 ... 10)
- 上一頁/下一頁按鈕
- 每頁筆數下拉選單
- 顯示當前範圍與總筆數(「顯示 1-20 / 共 50 筆」)
錯誤訊息
- API 錯誤: Toast 通知「載入失敗,請稍後再試」
- 空搜尋結果: 「查無符合條件的客戶」+ 插圖
載入狀態
- 列表載入中: Skeleton 載入動畫(表格行)
- 搜尋中: 搜尋框右側顯示 Spinner
技術規格 (Technical Specifications)
API 端點
1. 獲取客戶列表
- 端點:
GET /api/v1/customers - 權限要求:
ROLE_STAFF或更高 - 多租戶隔離: 自動過濾當前租戶
查詢參數:
{
// 搜尋
search?: string, // 搜尋關鍵字(姓名/電話/地址)
// 過濾
type?: 'individual' | 'corporate', // 客戶類型
tier?: ('regular' | 'vip' | 'vvip')[], // 客戶等級(可多選)
status?: 'active' | 'inactive', // 客戶狀態
tags?: string[], // 客戶標籤(可多選)
registeredDateFrom?: string, // 註冊日期起始(ISO 8601)
registeredDateTo?: string, // 註冊日期結束(ISO 8601)
lastOrderDateFrom?: string, // 最後消費日期起始(ISO 8601)
lastOrderDateTo?: string, // 最後消費日期結束(ISO 8601)
totalSpentMin?: number, // 累計消費最低金額
totalSpentMax?: number, // 累計消費最高金額
// 分頁
page?: number, // 頁碼(預設: 1)
limit?: number, // 每頁筆數(預設: 20)
// 排序
sortBy?: 'createdAt' | 'lastOrderDate' | 'totalSpent' | 'name', // 排序欄位
sortOrder?: 'asc' | 'desc', // 排序順序(預設: desc)
}
響應 Payload:
{
"customers": [
{
"id": "abc123",
"customerNumber": "ABC-CUST-000001",
"type": "individual", // 'individual' | 'corporate'
"name": "王小明",
"companyName": null,
"phone": "0912345678",
"email": "wang@example.com",
"status": "active", // 'active' | 'inactive'
"tier": "vip", // 'regular' | 'vip' | 'vvip'
"tags": ["常客", "喜歡玫瑰"],
"totalSpent": 5000,
"totalOrders": 8,
"lastOrderDate": "2025-10-31T10:00:00Z",
"createdAt": "2025-01-15T10:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"totalPages": 3,
"totalItems": 50
}
}
前端狀態管理
// 搜尋與過濾狀態
interface CustomerListState {
// 搜尋
searchQuery: string;
// 過濾
filters: {
type?: 'individual' | 'corporate';
tier: ('regular' | 'vip' | 'vvip')[];
status?: 'active' | 'inactive';
tags: string[];
registeredDateFrom?: string;
registeredDateTo?: string;
lastOrderDateFrom?: string;
lastOrderDateTo?: string;
totalSpentMin?: number;
totalSpentMax?: number;
};
// 分頁
pagination: {
page: number;
limit: number;
};
// 排序
sort: {
sortBy: 'createdAt' | 'lastOrderDate' | 'totalSpent' | 'name';
sortOrder: 'asc' | 'desc';
};
// 數據
customers: Customer[];
totalPages: number;
totalItems: number;
// 載入狀態
isLoading: boolean;
error: string | null;
}
元件架構
src/applets/customer-applet/
├── CustomersPage.tsx # 客戶列表主頁面
├── components/
│ ├── CustomerSearchBar.tsx # 搜尋框組件
│ ├── CustomerFilterPanel.tsx # 過濾面板
│ ├── CustomerFilterTags.tsx # 過濾條件標籤
│ ├── CustomerTable.tsx # 客戶表格(桌面)
│ ├── CustomerCardList.tsx # 客戶卡片列表(手機)
│ ├── CustomerPagination.tsx # 分頁導航
│ └── CustomerTierBadge.tsx # 客戶等級徽章
├── hooks/
│ ├── useCustomerList.ts # 客戶列表邏輯
│ └── useCustomerFilters.ts # 過濾邏輯
└── types.ts # TypeScript 類型定義
測試需求 (Test Requirements)
單元測試
- 搜尋防抖邏輯
- 過濾條件構建邏輯
- 排序邏輯
- 分頁導航邏輯
整合測試
- 客戶列表 API 整合
- 搜尋功能整合
- 過濾功能整合
- 分頁功能整合
E2E 測試
- Scenario 1: 查看客戶列表
- Scenario 2: 搜尋客戶(姓名)
- Scenario 3: 搜尋客戶(電話)
- Scenario 4: 依客戶等級過濾
- Scenario 5: 組合搜尋與過濾
- Scenario 6: 清空過濾條件
- Scenario 7: 切換每頁筆數
- Scenario 8: 排序客戶列表
- Scenario 9: 點擊客戶進入詳情頁
- Scenario 10: 空搜尋結果
- Scenario 11: 響應式設計
驗收檢查清單 (Acceptance Checklist)
- 客戶列表正確顯示(預設排序)
- 搜尋功能正常(姓名/電話)
- 過濾功能正常(類型/等級/狀態/標籤/日期/金額)
- 組合搜尋與過濾功能正常
- 清空過濾條件功能正常
- 分頁導航功能正常
- 切換每頁筆數功能正常
- 排序功能正常(升序/降序)
- 點擊客戶進入詳情頁
- 空搜尋結果處理正確
- 響應式設計正常(桌面/手機)
- 載入狀態正確顯示
- 錯誤處理正確
Story Points
估算: 5 points
理由:
- 基本的列表、搜尋、過濾、分頁功能
- 需要處理多種過濾條件組合
- 需要響應式設計(表格/卡片模式)
- 類似 US-203(訂單搜尋與過濾)
相關文檔
最後更新: 2025-11-04 撰寫者: Product Team