# Pivot Module 執行計劃 ## 概述 建立多頁籤數據透視查詢模組,參考 transfer 模組的設計架構,使用相同的技術棧與 UI/UX 模式,實現法會報名資料的多維度分析與展示。 --- ## 技術架構 ### 前端技術 - **框架**: Vue.js 2.x + Vuetify 2.x(與 transfer 模組一致) - **表格元件**: v-data-table(Vuetify 內建表格元件) - **頁籤元件**: v-tabs / v-tab / v-tab-item(Vuetify 頁籤) - **UI 框架**: Bootstrap 5 + Bootstrap Icons(MasterPage) - **樣式**: 與 transfer 模組保持一致的視覺風格 ### 後端 API - **框架**: ASP.NET Web API(C#) - **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` 或 `` 2. **下拉選單**: `v-select`(年份、月份、法會選擇) 3. **資料表格**: `v-data-table`(分頁、排序、過濾) 4. **頁籤切換**: `v-tabs`(Tab 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. 選擇年份(2020~2025)或月份(1~12) 2. 點擊「查詢法會」按鈕 3. API 回傳該期間的法會清單(`api/pivot/activity_stats`) 4. 表格呈現法會清單(法會名稱、日期、統計) 5. 點擊「選擇」按鈕,載入該法會的詳細資料 6. 自動切換到 Tab 2(詳細資料頁籤) #### API 整合 - **端點**: `GET api/pivot/activity_stats?startDate={start}&endDate={end}` - **回傳**: 法會清單(含報名統計) #### 資料查詢策略 ```javascript // 選擇法會後,一次性載入完整資料 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; }); } ``` #### 元件範例 ```html 查詢法會 ``` --- ### 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 或背景色區隔: ```html 法會 信眾 功德 計算 ``` #### 資料來源(前端計算) ```javascript 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); } } ``` #### 元件範例 ```html ``` --- ### Tab 3: 信眾參與分析 **功能目標**: 統計信眾的參與情況(參與次數、金額、最近參與) #### UI 布局 ``` +--------------------------------------------------------------+ | [信眾參與分析] | |--------------------------------------------------------------| | 過濾: [信眾編號] [參與次數 ≥] [總金額 ≥] [查詢] | |--------------------------------------------------------------| | +----------------------------------------------------------+ | | | 信眾編號 | 姓名 | 參與次數 | 總金額 | 最近參與日期 | | | +----------------------------------------------------------+ | | | F001 | 張三 | 5 | 2500 | 2025-03-01 | | | | F002 | 李四 | 3 | 1500 | 2025-02-15 | | | +----------------------------------------------------------+ | +--------------------------------------------------------------+ ``` #### 功能特性 1. **統計欄位**: 參與次數、總金額、平均金額、最近參與日期 2. **過濾條件**: 信眾編號、參與次數閾值、總金額閾值 3. **排序**: 預設依參與次數降序 4. **分頁**: 預設每頁 50 筆 #### 資料來源(前端計算) ```javascript 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 #### 資料來源(前端計算) ```javascript 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 折線圖(選配) #### 資料來源(前端計算) ```javascript 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% | | | +----------------------------------------------------------+ | +--------------------------------------------------------------+ ``` #### 資料來源(前端計算) ```javascript 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) ```html 功德 ``` ### 表格樣式 ```css /* 與 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 參考 ```SQL 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 ``` --- ## 資料結構範例 ```javascript // 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 效能考量 ```javascript 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(建立查詢頁面骨架)。**