iT邦幫忙

2025 iThome 鐵人賽

DAY 3
4
Modern Web

Angular 進階實務 30天系列 第 3

Day 3:常見UX實作 - 表格佈局設計策略

  • 分享至 

  • xImage
  •  

前言

在上一篇文章中,我們解決了表格內文字過長的問題。
但實務上還有另一個更大的挑戰:比時薪還多欄位數

當你的表格從10個欄位增加到25個、50個,甚至破百個欄位時,再好的文字處理技巧也救不了破版的災難。這時候就需要從整體佈局的角度來思考解決方案。

今天要分享的兩種方法:

  • 固定欄滑動:讓重要欄位永遠可見,其他欄位可以滑動查看
  • 明細彈窗:將複雜資訊分層展示,主表格保持簡潔

這些都是我在企業專案中實際使用過的方案,適合客服、交易、保單管理等需要處理大量資料的系統。
網頁展示:Day3


表格設計

  • 固定欄滑動

    • 適用情境

      • 用在欄位很多,但有固定資料需要顯示跟查找的情況,可能25個左右可以考慮
    • 舉例情境

      • 員工資料管理系統
      固定欄: 員工姓名、部門
      滑動欄: 電話、Email、地址、入職日期、薪資、績效、考勤記錄...
      
    • 程式碼,nzTableLayout記得設成fixed,也需要設定寬度

      • sample-table.component.ts
      <nz-table
        [nzTableLayout]="'fixed'"
        [nzData]="employees"
        nzBordered
        style="width: 800px;"
        [nzScroll]="{ x: '1200px' }"
      >
        <thead>
          <tr>
            <!-- 固定左側欄位 -->
            <th nzLeft nzWidth="120px">員工姓名</th>
            <th nzLeft nzWidth="100px">部門</th>
      
            <!-- 可滑動欄位 -->
            <th nzWidth="130px">電話</th>
            <th nzWidth="200px">Email</th>
            <th nzWidth="250px">地址</th>
            <th nzWidth="120px">入職日期</th>
            <th nzWidth="100px">薪資</th>
            <th nzWidth="80px">績效</th>
            <th nzWidth="100px">考勤記錄</th>
          </tr>
        </thead>
      
        <tbody>
          <tr *ngFor="let employee of employees">
            <!-- 固定左側欄位 -->
            <td nzLeft><strong>{{ employee.name }}</strong></td>
            <td nzLeft>{{ employee.department }}</td>
      
            <!-- 可滑動欄位 -->
            <td>{{ employee.phone }}</td>
            <td>{{ employee.email }}</td>
            <td>{{ employee.address }}</td>
            <td>{{ employee.hireDate }}</td>
            <td>NT$ {{ employee.salary | number }}</td>
            <td>{{ employee.performance }}</td>
            <td>{{ employee.attendance }}</td>
          </tr>
        </tbody>
      </nz-table>
      
      • sample.component.ts
       export interface Employee {
        name: string;
        department: string;
        phone: string;
        email: string;
        address: string;
        hireDate: string;
        salary: number;
        performance: string;
        attendance: string;
      }
       ...
        employees: Employee[] = [
          {
            name: '張小明',
            department: '資訊部',
            phone: '0912-000-000',
            email: 'zhang.xiaoming@company.com',
            address: '台北市信義區信義路X段7號101室',
            hireDate: '2022-03-15',
            salary: 65000,
            performance: '優秀',
            attendance: '95%',
          },
          {
            name: '李美麗',
            department: '人資部',
            phone: '0923-000-000',
            email: 'li.meili@company.com',
            address: '新北市板橋區文化路X段188號3樓',
            hireDate: '2021-08-20',
            salary: 58000,
            performance: '良好',
            attendance: '92%'
          },
          {
            name: '王大偉',
            department: '業務部',
            phone: '0934-000-000',
            email: 'wang.dawei@company.com',
            address: '台中市西屯區台灣大道X段99號12樓',
            hireDate: '2020-11-10',
            salary: 72000,
            performance: '優秀',
            attendance: '89%'
          },
          {
            name: '陳怡君',
            department: '財務部',
            phone: '0900-000-000',
            email: 'chen.yijun@company.com',
            address: '高雄市前金區中正X路211號8樓A室',
            hireDate: '2023-01-05',
            salary: 62000,
            performance: '良好',
            attendance: '97%'
          }
        ];
      
  • 示意圖

    • 滑動前
    ┌─────────────────────────────────────────────────────────────────┐
    │ 員工姓名 │ 部門   │ 電話        │ Email              │ 地址    │
    ├─────────────────────────────────────────────────────────────────┤
    │ 張小明   │ 資訊部 │ 0912-345-678│ zhang@company.com  │ 台北... │
    │ 李美麗   │ 人資部 │ 0923-456-789│ li@company.com     │ 新北... │
    │ 王大偉   │ 業務部 │ 0934-567-890│ wang@company.com   │ 台中... │
    └─────────────────────────────────────────────────────────────────┘
      ↑固定欄位↑                    ↑─── 可滑動區域 ────↑
    
    • 滑動後
    ┌─────────────────────────────────────────────────────────────────┐
    │ 員工姓名 │ 部門   │ 入職日期    │ 薪資    │ 績效 │ 考勤記錄 │
    ├─────────────────────────────────────────────────────────────────┤
    │ 張小明   │ 資訊部 │ 2022-03-15  │ 65,000  │ 優秀 │ 95%     │
    │ 李美麗   │ 人資部 │ 2021-08-20  │ 58,000  │ 良好 │ 92%     │
    │ 王大偉   │ 業務部 │ 2020-11-10  │ 72,000  │ 優秀 │ 89%     │
    └─────────────────────────────────────────────────────────────────┘
      ↑固定欄位↑                    ↑─── 滑動後顯示 ────↑
    
    • 原理
    完整表格結構:
    ┌──固定──┬─────────────── 滑動區域 ────────────────┐
    │姓名│部門│電話│Email│地址│入職日期│薪資│績效│考勤│
    ├────┼────┼────┼─────┼────┼────────┼────┼────┼────┤
    │張小明│資訊│...│...  │... │2022-03 │65K │優秀│95% │
    └────┴────┴────┴─────┴────┴────────┴────┴────┴────┘
    
    瀏覽器視窗:
    ┌─────────────────────────────┐ ← 視窗邊界
    │ 固定 │    可見的滑動部分    │
    │姓名部門│ 電話 Email 地址... │ ← 初始位置
    └─────────────────────────────┘
    
    滑動後:
    ┌─────────────────────────────┐
    │ 固定 │    可見的滑動部分    │
    │姓名部門│ 入職日期 薪資 績效 │ ← 滑動後位置
    └─────────────────────────────┘
    
  • 注意事項

    • 手機板建議改用卡片形式或是明細彈窗展開
  • 明細

    • 適用情境:

      • 使用彈窗的情況,通常是需要進一步閱讀資料的情況,但是第一眼不需要看到全部,這種的通常資料也很多,破百的欄位是常見情況
    • 舉例情境

      • 保險業務系統
        • 主表格顯示:保單號碼、要保人姓名、商品名稱、保險金額、保單狀態、生效日期、操作按鈕
        • 點擊「查看明細」彈窗顯示:要保人完整資料、被保險人資訊、受益人清單、保障內容明細、繳費記錄、理賠歷史、健康告知、保單異動記錄、相關文件等
      • 客戶保單管理
        • 主表格顯示:客戶編號、客戶姓名、有效保單數、總保額、最後異動日、操作按鈕
        • 點擊「查看詳情」彈窗顯示:客戶基本資料、聯絡資訊、職業資料、財務狀況、風險評估、所有保單清單、家庭成員、理賠統計、業務員記錄等
    • 程式碼

      • sample.component.html
      <nz-table
        #policyTable
        [nzData]="listOfPolicies"
        nzBordered
        [nzLoading]="loading"
        [nzPageSize]="20"
        [nzShowPagination]="true"
      >
        <thead>
          <tr>
            <th>保單號碼</th>
            <th>要保人姓名</th>
            <th>商品名稱</th>
            <th nzAlign="right">保險金額</th>
            <th nzAlign="center">保單狀態</th>
            <th>生效日期</th>
            <th nzAlign="center" nzWidth="120px">操作</th>
          </tr>
        </thead>
      
        <tbody>
          <tr *ngFor="let policy of policyTable.data">
            <td>{{ policy.policyNo }}</td>
            <td>{{ policy.applicantName }}</td>
            <td>{{ policy.productName }}</td>
            <td nzAlign="right">
              {{ policy.sumInsured | currency:'TWD':'symbol':'1.0-0' }}
            </td>
            <td nzAlign="center">
              <nz-tag [nzColor]="getPolicyStatusColor(policy.status)"
                >{{ policy.status }}</nz-tag
              >
            </td>
            <td>{{ policy.effectiveDate | date:'yyyy-MM-dd' }}</td>
            <td nzAlign="center">
              <button
                nz-button
                nzType="link"
                nzSize="small"
                (click)="showPolicyDetail(policy)"
              >
                <i nz-icon nzType="eye"></i> 查看明細
              </button>
            </td>
          </tr>
        </tbody>
      </nz-table>
      
      • sample.component.ts
        showPolicyDetail(policy: PolicyData): void {
          const modal = this.modal.create({
            nzTitle: `保單明細 - ${policy.policyNo}`,
            nzContent: DetailModalComponent,
            nzWidth: '95%',
            nzStyle: { top: '20px' },
            nzBodyStyle: { padding: '24px' },
            nzMaskClosable: false,
      
            nzData: {
              policyData: policy
            },
          });
        }
      
    • 示意圖

      可以參考網頁 Day3

    • 不適用情境

      • 如果是需要多筆比對的情況,就不適合用彈窗,可能要變成一部分收納跟展開的方式,或是配上滾動卷軸

設計原則回顧

  1. 使用者優先:不是所有資訊都需要第一眼看到
  2. 情境導向:根據實際使用情境選擇方案
  3. 響應式考量:手機版建議都改用卡片或明細設計
  4. 效能平衡:避免一次載入過多不必要的資料,這部分會在Day5介紹

上一篇
Day 2:常見UX實作 - 表格文字處理技巧
下一篇
Day 4:常見UX實作 - 表格互動功能設計
系列文
Angular 進階實務 30天18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言