Files
17168ERP/web/admin/pivot
2025-10-20 11:54:40 +08:00
..
2025-10-19 21:59:22 +08:00
2025-10-19 21:59:22 +08:00
2025-10-20 11:54:40 +08:00
2025-10-19 21:59:22 +08:00
2025-10-20 11:54:40 +08:00
2025-10-19 21:59:22 +08:00
2025-10-19 21:59:22 +08:00
2025-10-19 21:59:22 +08:00

Pivot Module 執行計劃

概述

建立多頁籤數據透視查詢模組,參考 transfer 模組的設計架構,使用相同的技術棧與 UI/UX 模式,實現法會報名資料的多維度分析與展示。


技術架構

前端技術

  • 框架: Vue.js 2.x + Vuetify 2.x與 transfer 模組一致)
  • 表格元件: v-data-tableVuetify 內建表格元件)
  • 頁籤元件: v-tabs / v-tab / v-tab-itemVuetify 頁籤)
  • UI 框架: Bootstrap 5 + Bootstrap IconsMasterPage
  • 樣式: 與 transfer 模組保持一致的視覺風格

後端 API

  • 框架: ASP.NET Web APIC#
  • ORM: Entity Framework + LINQ
  • 控制器: App_Code/api/pivotController.cs(已建立)
  • 資料庫視圖: 報名明細查詢SQL View

資料流架構(重要)★

查詢流程:
┌─────────────┐
│   Tab 1     │ → 選擇法會 → API 查詢一次 → 
│  查詢條件   │              (完整資料集)
└─────────────┘
       ↓
┌─────────────────────────────────────────┐
│  Vue Data (存於前端 this.rawData)        │
│  - 完整報名明細                           │
│  - 一次性載入,不重複查詢                 │
└─────────────────────────────────────────┘
       ↓
┌──────┬──────┬──────┬──────┬──────┐
│Tab 2 │Tab 3 │Tab 4 │Tab 5 │Tab 6 │
│明細  │信眾  │收入  │趨勢  │對比  │
│      │      │      │      │      │
│ 純前端計算 / 過濾 / 分組 / 統計       │
│ 使用 computed / methods / filters    │
└──────────────────────────────────────┘

優點:

  1. 效能優化: API 只查詢一次,減少伺服器負載
  2. 即時響應: 切換頁籤無延遲,使用者體驗佳
  3. 離線分析: 資料載入後可離線操作(過濾、排序、統計)
  4. 減少流量: 不重複傳輸相同資料
  5. 一致性: 所有頁籤基於同一份資料,確保一致性

技術實現:

  • this.rawData: 原始完整資料Tab 1 查詢後存入)
  • computed properties: 各頁籤的資料來源(動態計算)
  • methods: 過濾、分組、統計邏輯
  • watch: 監聽過濾條件變化

元件規格

  1. 日期選擇器: v-date-picker<input type="date">
  2. 下拉選單: v-select(年份、月份、法會選擇)
  3. 資料表格: v-data-table(分頁、排序、過濾)
  4. 頁籤切換: v-tabsTab 1~N
  5. 按鈕群組: v-btn(查詢、匯出、重設)
  6. 載入狀態: :loading="loading"
  7. 視覺標記: Bootstrap Badge橙、藍、綠、紫色標籤

頁籤設計

Tab 1: 查詢條件設定

功能目標: 提供查詢條件,篩選法會並選擇目標法會

UI 布局

+--------------------------------------------------------------+
| [查詢條件]                                                    |
|--------------------------------------------------------------|
| 時間範圍: [年份 ▼] [月份 ▼] [查詢法會]                        |
|--------------------------------------------------------------|
| 法會清單:                                                     |
| +----------------------------------------------------------+ |
| | 序號 | 法會名稱 | 開始日期 | 結束日期 | 報名人數 | 操作 |   |
| +----------------------------------------------------------+ |
| | 1 | 2025春季法會 | 2025-03-01 | 2025-03-15 | 120 | [選擇] | |
| | 2 | 2025夏季法會 | 2025-06-01 | 2025-06-20 | 85  | [選擇] | |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+

查詢流程

  1. 選擇年份20202025或月份112
  2. 點擊「查詢法會」按鈕
  3. API 回傳該期間的法會清單(api/pivot/activity_stats
  4. 表格呈現法會清單(法會名稱、日期、統計)
  5. 點擊「選擇」按鈕,載入該法會的詳細資料
  6. 自動切換到 Tab 2詳細資料頁籤

API 整合

  • 端點: GET api/pivot/activity_stats?startDate={start}&endDate={end}
  • 回傳: 法會清單(含報名統計)

資料查詢策略

// 選擇法會後,一次性載入完整資料
selectActivity(item) {
  this.loading = true;
  this.selectedActivity = item;
  
  // 一次性查詢完整報名明細(不分頁)
  axios.get('/api/pivot/registration_details', {
    params: {
      activityNum: item.法會ID,
      pageSize: 9999  // 取得全部資料
    }
  }).then(response => {
    // 存入原始資料(供所有頁籤使用)
    this.rawData = response.data.data.list;
    
    // 自動切換到 Tab 2
    this.activeTab = 1;
    this.loading = false;
  });
}

元件範例

<v-select
  :items="yearOptions"
  v-model="selectedYear"
  label="年份"
  dense
  outlined
></v-select>
<v-select
  :items="monthOptions"
  v-model="selectedMonth"
  label="月份"
  dense
  outlined
  clearable
></v-select>
<v-btn color="primary" @click="loadActivities">查詢法會</v-btn>

<v-data-table
  :headers="activityHeaders"
  :items="activities"
  :loading="loading"
  item-key="法會ID"
>
  <template v-slot:item.actions="{ item }">
    <v-btn small color="success" @click="selectActivity(item)">選擇</v-btn>
  </template>
</v-data-table>

Tab 2: 報名明細資料

功能目標: 完整呈現該場法會的所有報名明細

UI 布局

+--------------------------------------------------------------+
| [報名明細] 法會: 2025春季法會 (2025-03-01 ~ 2025-03-15)       |
|--------------------------------------------------------------|
| 過濾: [信眾姓名] [功德類型 ▼] [狀態 ▼] [查詢] [匯出Excel]      |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 報名編號 | 報名日期 | 信眾姓名 | 功德名稱 | 數量 | 金額 |   |
| +----------------------------------------------------------+ |
| | 20250301001 | 2025-03-01 | 張三 | 點燈 | 1 | 500 |      |
| | 20250301002 | 2025-03-01 | 李四 | 安太歲 | 1 | 300 |    |
| +----------------------------------------------------------+ |
| 第 1 頁,共 10 頁(共 200 筆)                                 |
+--------------------------------------------------------------+

功能特性

  1. 欄位顯示: 報名編號、報名日期、信眾姓名、功德名稱、數量、金額、已收、未收
  2. 過濾條件: 信眾姓名(模糊搜尋)、功德類型(下拉選單)、功德主(是/否)
  3. 排序: 可依任意欄位排序(升序/降序)
  4. 分頁: 預設每頁 50 筆可調整10/20/50/100
  5. 匯出: 匯出 Excel/CSV

欄位色彩標記(參考 Excel 視圖)

  • 橙色(法會資料): 法會ID、法會名稱、開始日期、結束日期
  • 藍色(信眾資料): 信眾編號、信眾姓名
  • 綠色(功德資訊): 報名編號、報名日期、功德主、功德類型、功德名稱
  • 紫色(計算欄位): 數量、金額、已收、未收

使用 Bootstrap Badge 或背景色區隔:

<span class="badge bg-warning text-dark">法會</span>
<span class="badge bg-info">信眾</span>
<span class="badge bg-success">功德</span>
<span class="badge bg-secondary">計算</span>

資料來源(前端計算)

computed: {
  // Tab 2: 報名明細(前端分頁、過濾)
  filteredRegistrations() {
    let data = this.rawData;
    
    // 過濾:信眾姓名
    if (this.filter.followerName) {
      data = data.filter(x => x.信眾姓名.includes(this.filter.followerName));
    }
    
    // 過濾:功德類型
    if (this.filter.itemKind) {
      data = data.filter(x => x.功德類型 === this.filter.itemKind);
    }
    
    // 過濾:功德主
    if (this.filter.isParent !== null) {
      data = data.filter(x => x.功德主 === (this.filter.isParent ? '是' : '否'));
    }
    
    return data;
  },
  
  // 前端分頁
  paginatedRegistrations() {
    const start = (this.currentPage - 1) * this.pageSize;
    const end = start + this.pageSize;
    return this.filteredRegistrations.slice(start, end);
  }
}

元件範例

<v-data-table
  :headers="detailHeaders"
  :items="registrations"
  :loading="loading"
  :server-items-length="totalCount"
  :options.sync="options"
  item-key="報名編號"
  class="elevation-1"
>
  <template v-slot:item.信眾姓名="{ item }">
    <span class="badge bg-info me-1"></span>{{ item.信眾姓名 }}
  </template>
  <template v-slot:item.金額="{ item }">
    <span class="badge bg-secondary me-1"></span>{{ item.金額 | currency }}
  </template>
</v-data-table>

Tab 3: 信眾參與分析

功能目標: 統計信眾的參與情況(參與次數、金額、最近參與)

UI 布局

+--------------------------------------------------------------+
| [信眾參與分析]                                                |
|--------------------------------------------------------------|
| 過濾: [信眾編號] [參與次數 ≥] [總金額 ≥] [查詢]               |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 信眾編號 | 姓名 | 參與次數 | 總金額 | 最近參與日期 |      |
| +----------------------------------------------------------+ |
| | F001 | 張三 | 5 | 2500 | 2025-03-01 |                 |
| | F002 | 李四 | 3 | 1500 | 2025-02-15 |                 |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+

功能特性

  1. 統計欄位: 參與次數、總金額、平均金額、最近參與日期
  2. 過濾條件: 信眾編號、參與次數閾值、總金額閾值
  3. 排序: 預設依參與次數降序
  4. 分頁: 預設每頁 50 筆

資料來源(前端計算)

computed: {
  // Tab 3: 信眾參與分析(從 rawData 計算)
  followerAnalysis() {
    const followerMap = {};
    
    this.rawData.forEach(item => {
      const fNum = item.信眾編號;
      if (!followerMap[fNum]) {
        followerMap[fNum] = {
          信眾編號: fNum,
          姓名: item.信眾姓名,
          參與次數: 0,
          總金額: 0,
          最近參與日期: item.報名日期
        };
      }
      
      followerMap[fNum].參與次數++;
      followerMap[fNum].總金額 += (item.金額 * item.數量);
      
      // 更新最近參與日期
      if (new Date(item.報名日期) > new Date(followerMap[fNum].最近參與日期)) {
        followerMap[fNum].最近參與日期 = item.報名日期;
      }
    });
    
    // 轉換為陣列,並計算平均金額
    return Object.values(followerMap).map(f => ({
      ...f,
      平均金額: Math.round(f.總金額 / f.參與次數)
    })).sort((a, b) => b.參與次數 - a.參與次數); // 依參與次數降序
  }
}

Tab 4: 收入統計分析

功能目標: 依時間、法會、功德類型統計收入

UI 布局

+--------------------------------------------------------------+
| [收入統計分析]                                                |
|--------------------------------------------------------------|
| 分組方式: ( ) 月份 ( ) 年度 (•) 法會 ( ) 功德類型            |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 分組名稱 | 報名人數 | 總金額 | 已收 | 未收 | 收款率 |    |
| +----------------------------------------------------------+ |
| | 2025春季法會 | 120 | 60000 | 50000 | 10000 | 83.3% |   |
| | 2025夏季法會 | 85  | 45000 | 40000 | 5000  | 88.9% |   |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+

功能特性

  1. 分組方式: 月份、年度、法會、功德類型
  2. 統計欄位: 報名人數、總金額、已收、未收、收款率
  3. 圖表呈現: 可加入 CanvasJS 長條圖/圓餅圖(選配)
  4. 匯出: 支援 Excel/CSV

資料來源(前端計算)

computed: {
  // Tab 4: 收入統計分析(從 rawData 計算)
  incomeStats() {
    const statsMap = {};
    const groupBy = this.groupBy; // 'monthly', 'yearly', 'activity', 'itemKind'
    
    this.rawData.forEach(item => {
      let key;
      switch(groupBy) {
        case 'monthly':
          key = item.報名日期.substring(0, 7); // YYYY-MM
          break;
        case 'yearly':
          key = item.報名日期.substring(0, 4); // YYYY
          break;
        case 'activity':
          key = item.法會名稱;
          break;
        case 'itemKind':
          key = item.功德類型;
          break;
      }
      
      if (!statsMap[key]) {
        statsMap[key] = {
          分組名稱: key,
          報名人數: 0,
          總金額: 0,
          已收: 0,
          未收: 0
        };
      }
      
      statsMap[key].報名人數++;
      const amount = item.金額 * item.數量;
      statsMap[key].總金額 += amount;
      statsMap[key].已收 += item.已收 || 0;
      statsMap[key].未收 += item.未收 || amount;
    });
    
    // 計算收款率
    return Object.values(statsMap).map(s => ({
      ...s,
      收款率: s.總金額 > 0 ? ((s.已收 / s.總金額) * 100).toFixed(1) + '%' : '0%'
    }));
  }
}

Tab 5: 趨勢分析

功能目標: 顯示時間序列趨勢(收入、參與人數、法會數量)

UI 布局

+--------------------------------------------------------------+
| [趨勢分析]                                                    |
|--------------------------------------------------------------|
| 指標: ( ) 收入 (•) 參與人數 ( ) 法會數量                      |
| 時間間隔: ( ) 月份 (•) 季度 ( ) 年度                          |
|--------------------------------------------------------------|
| [折線圖]                                                      |
|                                                              |
| +----------------------------------------------------------+ |
| | 時間 | 數值 | 成長率 |                                    |
| +----------------------------------------------------------+ |
| | 2025-01 | 120 | +10% |                                  |
| | 2025-02 | 132 | +10% |                                  |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+

功能特性

  1. 指標選擇: 收入、參與人數、法會數量
  2. 時間間隔: 月份、季度、年度
  3. 成長率計算: 較前期成長率
  4. 圖表: CanvasJS 折線圖(選配)

資料來源(前端計算)

computed: {
  // Tab 5: 趨勢分析(從 rawData 計算)
  trendAnalysis() {
    const metric = this.trendMetric; // 'income', 'followers', 'count'
    const interval = this.trendInterval; // 'monthly', 'quarterly', 'yearly'
    const trendMap = {};
    
    this.rawData.forEach(item => {
      let key;
      switch(interval) {
        case 'monthly':
          key = item.報名日期.substring(0, 7); // YYYY-MM
          break;
        case 'quarterly':
          const month = parseInt(item.報名日期.substring(5, 7));
          const quarter = Math.ceil(month / 3);
          key = `${item.報名日期.substring(0, 4)}-Q${quarter}`;
          break;
        case 'yearly':
          key = item.報名日期.substring(0, 4); // YYYY
          break;
      }
      
      if (!trendMap[key]) {
        trendMap[key] = { 時間: key, 收入: 0, 人數: 0, 次數: 0 };
      }
      
      trendMap[key].收入 += (item.金額 * item.數量);
      trendMap[key].人數++; // 簡化計算,實際可用 Set 去重
      trendMap[key].次數++;
    });
    
    // 轉換為陣列並排序
    const result = Object.values(trendMap).sort((a, b) => a.時間.localeCompare(b.時間));
    
    // 計算成長率
    return result.map((item, index) => {
      if (index === 0) {
        return { ...item, 數值: item[metric === 'income' ? '收入' : metric === 'followers' ? '人數' : '次數'], 成長率: '-' };
      }
      const prev = result[index - 1];
      const currentValue = item[metric === 'income' ? '收入' : metric === 'followers' ? '人數' : '次數'];
      const prevValue = prev[metric === 'income' ? '收入' : metric === 'followers' ? '人數' : '次數'];
      const growthRate = prevValue > 0 ? (((currentValue - prevValue) / prevValue) * 100).toFixed(1) : '0';
      return { ...item, 數值: currentValue, 成長率: growthRate + '%' };
    });
  }
}

Tab 6: 對比分析(選配)

功能目標: 不同時期、法會的對比分析

UI 布局

+--------------------------------------------------------------+
| [對比分析]                                                    |
|--------------------------------------------------------------|
| 對比類型: (•) 法會對比 ( ) 年度對比                           |
| 期間1: [2025春季法會 ▼]  期間2: [2024春季法會 ▼]             |
|--------------------------------------------------------------|
| +----------------------------------------------------------+ |
| | 指標 | 期間1 | 期間2 | 差異 | 差異率 |                  |
| +----------------------------------------------------------+ |
| | 報名人數 | 120 | 100 | +20 | +20% |                   |
| | 總金額 | 60000 | 50000 | +10000 | +20% |             |
| +----------------------------------------------------------+ |
+--------------------------------------------------------------+

資料來源(前端計算)

computed: {
  // Tab 6: 對比分析(從 rawData 計算)
  // 注意:對比分析可能需要跨法會資料,如需要可額外查詢
  comparativeAnalysis() {
    // 如果只在單一法會內對比(如月份對比),可用 rawData
    // 如果需要跨法會對比,建議另外查詢或在 Tab 1 時一併載入多場法會資料
    const period1Data = this.rawData.filter(x => {
      // 依 period1 條件過濾
      return this.isPeriod1(x);
    });
    
    const period2Data = this.rawData.filter(x => {
      // 依 period2 條件過濾
      return this.isPeriod2(x);
    });
    
    const stats1 = this.calculateStats(period1Data);
    const stats2 = this.calculateStats(period2Data);
    
    return [
      { 指標: '報名人數', 期間1: stats1.count, 期間2: stats2.count, 差異: stats1.count - stats2.count },
      { 指標: '總金額', 期間1: stats1.amount, 期間2: stats2.amount, 差異: stats1.amount - stats2.amount },
      // ... 其他指標
    ];
  }
}

視覺設計規範

色彩系統(參考 transfer

  • 主色調: Bootstrap 5 預設配色
  • 成功/確認: bg-success / text-success
  • 警告: bg-warning / text-warning
  • 危險/錯誤: bg-danger / text-danger
  • 資訊: bg-info / text-info
  • 次要: bg-secondary / text-muted

欄位色彩標記(對應 Excel

<!-- 橙色:法會資料 -->
<span class="badge bg-warning text-dark"></span>

<!-- 藍色:信眾資料 -->
<span class="badge bg-info"></span>

<!-- 綠色:功德資訊 -->
<span class="badge bg-success">功德</span>

<!-- 紫色:計算欄位 -->
<span class="badge bg-secondary"></span>

表格樣式

/* 與 transfer 保持一致 */
.v-data-table th {
  background-color: #f5f5f5 !important;
  font-weight: bold;
}

.v-data-table tbody tr:hover {
  background-color: #f0f8ff !important;
}

/* 欄位色彩標記 */
.field-activity { border-left: 3px solid #ffc107; } /* 橙 */
.field-follower { border-left: 3px solid #17a2b8; } /* 藍 */
.field-merit { border-left: 3px solid #28a745; }    /* 綠 */
.field-calculated { border-left: 3px solid #6c757d; } /* 紫 */

API 端點總覽

端點 方法 功能 對應頁籤 使用時機
/api/pivot/activity_stats GET 查詢法會清單與統計 Tab 1 選擇查詢條件時
/api/pivot/registration_details GET 報名明細查詢(完整資料 Tab 1 選擇法會後一次性載入
/api/pivot/registration_details_export GET 報名明細匯出 Tab 2 匯出 Excel 時(選配)
/api/pivot/excel_data_structured GET Excel 數據連接 外部 Power Query/Power Pivot

重要說明:

  • Tab 2~6 不呼叫 API,全部使用前端 computed 計算
  • registration_details 查詢時使用 pageSize=9999 取得完整資料
  • follower_analysis, income_stats, trend_analysis, comparative_analysis 端點保留備用,但前端優先使用 computed
  • 如資料量過大(>5000 筆),可調整策略改用分頁查詢

檔案結構

admin/pivot/
├── index.aspx              # 首頁(已完成)
├── index.aspx.cs           # 首頁邏輯(已完成)
├── query.aspx              # 多頁籤查詢頁面(待建立)★
├── query.aspx.cs           # 查詢頁面邏輯(待建立)★
└── README.md               # 本文件

App_Code/api/
└── pivotController.cs      # API 控制器(已完成)

實作步驟

Phase 1: 建立查詢頁面骨架

  1. 建立 query.aspx(參考 transfer/verify.aspx
  2. 建立 query.aspx.cs(參考 transfer/verify.aspx.cs
  3. 引入 Vue.js + Vuetify
  4. 建立頁籤結構v-tabs

Phase 2: 實作 Tab 1查詢條件

  1. 年份/月份選擇器
  2. 查詢法會按鈕與 API 整合(activity_stats
  3. 法會清單表格v-data-table
  4. 選擇法會邏輯(一次性載入完整資料)
  5. this.rawData 資料結構設計

Phase 3: 實作 Tab 2報名明細

  1. 報名明細表格(前端分頁、排序、過濾)
  2. computed: filteredRegistrations 實作
  3. 欄位色彩標記badge
  4. 匯出功能(選配)

Phase 4: 實作 Tab 3~6分析頁籤

  1. 信眾參與分析Tab 3- computed: followerAnalysis
  2. 收入統計分析Tab 4- computed: incomeStats
  3. 趨勢分析Tab 5- computed: trendAnalysis
  4. 對比分析Tab 6選配- computed: comparativeAnalysis

Phase 5: 優化與測試

  1. UI/UX 調整(與 transfer 保持一致)
  2. 效能優化(分頁、快取)
  3. 瀏覽器相容性測試
  4. 權限控制ezAuthorize

參考範例

1. transfer/verify.aspx

  • 多階段流程設計程序1、程序2
  • v-data-table 表格元件使用
  • 信眾選擇對話框v-dialog
  • 狀態選擇v-select

2. transfer/verify1.aspx

  • 單階段流程設計
  • 簡潔的查詢與確認流程

3. transfer/index.aspx

  • 功能入口頁面設計
  • Bootstrap Icons + Badge
  • 三欄式布局

注意事項

  1. 元件一致性: 所有元件、樣式、命名均與 transfer 模組保持一致
  2. API 規範: 遵循 RESTful 設計,統一回傳格式
  3. 權限控制: 所有 API 加上 [ezAuthorize]
  4. 效能考量:
    • 前端計算策略: Tab 1 查詢一次Tab 2~6 使用 Vue computed 計算
    • 資料量控制: 單一法會報名明細預估 <5000 筆,適合前端處理
    • ⚠️ 大資料處理: 若單場法會 >5000 筆,可改用 API 分頁查詢
    • 記憶體管理: 切換法會時清除舊資料(this.rawData = []
  5. 視覺區隔: 使用 Badge、邊框色、背景色標記欄位類型
  6. 使用者體驗: 載入狀態loading、錯誤提示alert、空資料提示
  7. 前端效能優化:
    • 使用 computed 而非 methods(自動快取)
    • 大型陣列操作使用 Object.freeze() 凍結原始資料
    • v-data-table 啟用虛擬滾動(:virtual-scroll="true",資料量 >1000 時)

SQL View 參考

CREATE VIEW [dbo].[報名明細查詢]
AS
SELECT  dbo.activity.num AS 法會ID, dbo.activity.subject AS 法會名稱, dbo.activity.startDate_solar AS 開始日期, dbo.activity.endDate_solar AS 結束日期, 
               dbo.followers.f_number AS 信眾編號, dbo.followers.u_name AS 信眾姓名, dbo.pro_order.order_no AS 報名編號, dbo.pro_order.up_time AS 報名日期, 
               CASE WHEN parent_num IS NOT NULL THEN '是' ELSE '否' END AS 功德主, dbo.actItem_kind.kind AS 功德類型, dbo.actItem.subject AS 功德名稱, 
               dbo.pro_order_detail.qty AS 數量, dbo.pro_order_detail.price AS 金額, 0 AS 已收, dbo.pro_order_detail.price * dbo.pro_order_detail.qty - 0 AS 未收
FROM     dbo.pro_order_detail INNER JOIN
               dbo.pro_order ON dbo.pro_order_detail.order_no = dbo.pro_order.order_no INNER JOIN
               dbo.actItem ON dbo.pro_order_detail.actItem_num = dbo.actItem.num INNER JOIN
               dbo.activity ON dbo.pro_order.activity_num = dbo.activity.num INNER JOIN
               dbo.followers ON dbo.pro_order.f_num = dbo.followers.num INNER JOIN
               dbo.actItem_kind ON dbo.actItem.kind = dbo.actItem_kind.num
GO

資料結構範例

// Vue data 結構
data() {
  return {
    // 原始完整資料Tab 1 查詢後存入,供所有頁籤使用)
    rawData: [],  // Array<報名明細物件>
    
    // 選中的法會
    selectedActivity: null,
    
    // 當前頁籤索引
    activeTab: 0,  // 0=Tab1, 1=Tab2, 2=Tab3...
    
    // Tab 2 過濾條件
    filter: {
      followerName: '',
      itemKind: null,
      isParent: null
    },
    
    // Tab 4 分組方式
    groupBy: 'activity',  // 'monthly', 'yearly', 'activity', 'itemKind'
    
    // Tab 5 趨勢設定
    trendMetric: 'income',  // 'income', 'followers', 'count'
    trendInterval: 'monthly',  // 'monthly', 'quarterly', 'yearly'
    
    // 載入狀態
    loading: false
  }
}

Vue Computed 效能考量

computed: {
  // 使用 Object.freeze() 凍結大型陣列,提升效能
  frozenRawData() {
    return Object.freeze(this.rawData);
  },
  
  // 各頁籤 computed 基於 frozenRawData
  filteredRegistrations() {
    return this.frozenRawData.filter(/* 過濾邏輯 */);
  }
}

執行確認

請檢視以上計劃,確認以下事項:

  1. 頁籤設計Tab 1~6符合需求
  2. 查詢流程(年/月 → 法會清單 → 明細資料)清晰
  3. 視覺區隔色彩標記、Badge符合 Excel 概念
  4. 技術架構Vue + Vuetify + API與 transfer 一致
  5. 功能完整性(查詢、過濾、排序、匯出)
  6. **資料流架構(一次查詢 + 前端計算)**合理且高效 ★

確認後即開始實作 Phase 1建立查詢頁面骨架