查詢範例
This commit is contained in:
@@ -1,73 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Data;
|
||||
using System.Configuration;
|
||||
using System.Collections;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Web.UI;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net.Mail;
|
||||
using System.Configuration;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Data.OleDb;
|
||||
using Microsoft.VisualBasic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Web.UI;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.Design;
|
||||
using System.ComponentModel.Design.Serialization;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Data.OleDb;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using System.Web.ModelBinding;
|
||||
using System.Web.Routing;
|
||||
using System.Web.Security;
|
||||
using System.Web.SessionState;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.Adapters;
|
||||
using System.Web.UI.HtmlControls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Collections;
|
||||
using System.Data;
|
||||
using System.Data.OleDb;
|
||||
using System.Configuration;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
|
||||
namespace Model.ViewModel
|
||||
|
||||
246
web/App_Code/api/pivot01Controller.cs
Normal file
246
web/App_Code/api/pivot01Controller.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Web.Http;
|
||||
using System.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// pivot01Controller - 法會報名統計分析 API
|
||||
/// 設計理念:直接查詢 SQL VIEW,保持彈性與簡潔
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class pivot01Controller : ApiController
|
||||
{
|
||||
// 連線字串
|
||||
private readonly string _connectionString;
|
||||
|
||||
public pivot01Controller()
|
||||
{
|
||||
// 優先使用 shopConn 連線字串(純 SQL Server 連線字串)
|
||||
var shopConnectionString = ConfigurationManager.ConnectionStrings["shopConn"]?.ConnectionString;
|
||||
if (!string.IsNullOrEmpty(shopConnectionString))
|
||||
{
|
||||
// 移除不相容的 Provider 參數
|
||||
_connectionString = shopConnectionString.Replace("Provider=SQLOLEDB;", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 備用方案:從 Entity Framework 連線字串中提取 SQL Server 連線字串
|
||||
var efConnectionString = ConfigurationManager.ConnectionStrings["ezEntities"]?.ConnectionString;
|
||||
if (!string.IsNullOrEmpty(efConnectionString))
|
||||
{
|
||||
// 解析 EF 連線字串,提取 provider connection string 部分
|
||||
var startIndex = efConnectionString.IndexOf("provider connection string="") + "provider connection string="".Length;
|
||||
var endIndex = efConnectionString.LastIndexOf(""");
|
||||
if (startIndex > 0 && endIndex > startIndex)
|
||||
{
|
||||
_connectionString = efConnectionString.Substring(startIndex, endIndex - startIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("無法解析 Entity Framework 連線字串");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("找不到可用的資料庫連線字串");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region 通用 VIEW 查詢方法
|
||||
|
||||
/// <summary>
|
||||
/// 執行 SQL 查詢並回傳 DataTable
|
||||
/// </summary>
|
||||
/// <param name="sql">SQL 查詢語句</param>
|
||||
/// <param name="parameters">參數陣列</param>
|
||||
/// <returns>查詢結果 DataTable</returns>
|
||||
private DataTable ExecuteSqlQuery(string sql, SqlParameter[] parameters = null)
|
||||
{
|
||||
var dataTable = new DataTable();
|
||||
|
||||
try
|
||||
{
|
||||
using (var connection = new SqlConnection(_connectionString))
|
||||
using (var command = new SqlCommand(sql, connection))
|
||||
{
|
||||
// 設定逾時時間為 60 秒
|
||||
command.CommandTimeout = 60;
|
||||
|
||||
// 加入參數
|
||||
if (parameters != null && parameters.Length > 0)
|
||||
{
|
||||
command.Parameters.AddRange(parameters);
|
||||
}
|
||||
|
||||
// 開啟連線並執行查詢
|
||||
connection.Open();
|
||||
using (var adapter = new SqlDataAdapter(command))
|
||||
{
|
||||
adapter.Fill(dataTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SqlException ex)
|
||||
{
|
||||
throw new Exception($"SQL 查詢錯誤: {ex.Message}", ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"執行查詢時發生錯誤: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
return dataTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DataTable 轉換為動態物件列表(保留中文欄位名)
|
||||
/// </summary>
|
||||
/// <param name="dt">DataTable</param>
|
||||
/// <returns>Dictionary 列表</returns>
|
||||
private List<Dictionary<string, object>> DataTableToDictionary(DataTable dt)
|
||||
{
|
||||
var list = new List<Dictionary<string, object>>();
|
||||
|
||||
foreach (DataRow row in dt.Rows)
|
||||
{
|
||||
var dict = new Dictionary<string, object>();
|
||||
foreach (DataColumn col in dt.Columns)
|
||||
{
|
||||
// 保留原始欄位名稱(包含中文)
|
||||
dict[col.ColumnName] = row[col] == DBNull.Value ? null : row[col];
|
||||
}
|
||||
list.Add(dict);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region API 端點
|
||||
|
||||
/// <summary>
|
||||
/// GET /api/pivot01/activity_stats
|
||||
/// 查詢法會統計(對應「法會統計」VIEW)
|
||||
/// </summary>
|
||||
/// <param name="year">查詢年份</param>
|
||||
/// <returns>法會統計資料</returns>
|
||||
[HttpGet]
|
||||
[Route("api/pivot01/activity_stats")]
|
||||
public IHttpActionResult GetActivityStats(int year)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 驗證年份參數
|
||||
if (year < 1900 || year > 2100)
|
||||
{
|
||||
return BadRequest("年份參數不正確,請輸入 1900 ~ 2100 之間的年份");
|
||||
}
|
||||
|
||||
// 建立 SQL 查詢(包含當年度及無日期的法會)
|
||||
string sql = @"
|
||||
SELECT * FROM [法會統計]
|
||||
WHERE (YEAR(開始日期) = @year OR 開始日期 IS NULL)
|
||||
ORDER BY
|
||||
CASE WHEN 開始日期 IS NULL THEN 1 ELSE 0 END,
|
||||
開始日期 DESC,
|
||||
結束日期 DESC
|
||||
";
|
||||
|
||||
// 建立參數
|
||||
var parameters = new[]
|
||||
{
|
||||
new SqlParameter("@year", SqlDbType.Int) { Value = year }
|
||||
};
|
||||
|
||||
// 執行查詢
|
||||
var dataTable = ExecuteSqlQuery(sql, parameters);
|
||||
var data = DataTableToDictionary(dataTable);
|
||||
|
||||
// 回應結果
|
||||
var result = new
|
||||
{
|
||||
success = true,
|
||||
data = data,
|
||||
message = "查詢成功",
|
||||
rowCount = data.Count
|
||||
};
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorResponse = new
|
||||
{
|
||||
success = false,
|
||||
message = $"查詢失敗:{ex.Message}",
|
||||
error = ex.ToString()
|
||||
};
|
||||
return Content(System.Net.HttpStatusCode.BadRequest, errorResponse);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET /api/pivot01/registration_details
|
||||
/// 查詢報名明細(對應「報名明細查詢」VIEW)
|
||||
/// </summary>
|
||||
/// <param name="activityNum">法會編號(必填)</param>
|
||||
/// <returns>報名明細資料</returns>
|
||||
[HttpGet]
|
||||
[Route("api/pivot01/registration_details")]
|
||||
public IHttpActionResult GetRegistrationDetails(int? activityNum = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 驗證參數
|
||||
if (!activityNum.HasValue || activityNum.Value <= 0)
|
||||
{
|
||||
return BadRequest("請提供有效的法會編號(activityNum)");
|
||||
}
|
||||
|
||||
// 建立查詢 SQL
|
||||
string sql = @"
|
||||
SELECT * FROM [報名明細查詢]
|
||||
WHERE 法會ID = @activityNum
|
||||
ORDER BY 報名日期 DESC, 報名編號 DESC
|
||||
";
|
||||
|
||||
// 建立參數
|
||||
var parameters = new[]
|
||||
{
|
||||
new SqlParameter("@activityNum", SqlDbType.Int) { Value = activityNum.Value }
|
||||
};
|
||||
|
||||
// 執行查詢
|
||||
var dataTable = ExecuteSqlQuery(sql, parameters);
|
||||
var data = DataTableToDictionary(dataTable);
|
||||
|
||||
// 回應結果
|
||||
var result = new
|
||||
{
|
||||
success = true,
|
||||
data = data,
|
||||
message = "查詢成功",
|
||||
rowCount = data.Count
|
||||
};
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorResponse = new
|
||||
{
|
||||
success = false,
|
||||
message = $"查詢失敗:{ex.Message}",
|
||||
error = ex.ToString()
|
||||
};
|
||||
return Content(System.Net.HttpStatusCode.BadRequest, errorResponse);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
1142
web/App_Code/api/pivotController.cs
Normal file
1142
web/App_Code/api/pivotController.cs
Normal file
File diff suppressed because it is too large
Load Diff
1401
web/admin/pivot/README-pivot-01.md
Normal file
1401
web/admin/pivot/README-pivot-01.md
Normal file
File diff suppressed because it is too large
Load Diff
768
web/admin/pivot/README.md
Normal file
768
web/admin/pivot/README.md
Normal file
@@ -0,0 +1,768 @@
|
||||
# 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` 或 `<input type="date">`
|
||||
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
|
||||
<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 或背景色區隔:
|
||||
```html
|
||||
<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>
|
||||
```
|
||||
|
||||
#### 資料來源(前端計算)
|
||||
```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
|
||||
<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 筆
|
||||
|
||||
#### 資料來源(前端計算)
|
||||
```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
|
||||
<!-- 橙色:法會資料 -->
|
||||
<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>
|
||||
```
|
||||
|
||||
### 表格樣式
|
||||
```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(建立查詢頁面骨架)。**
|
||||
185
web/admin/pivot/index.aspx
Normal file
185
web/admin/pivot/index.aspx
Normal file
@@ -0,0 +1,185 @@
|
||||
<%@ Page Title="數據透視管理" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" EnableEventValidation="false" CodeFile="index.aspx.cs" Inherits="admin_pivot_index" %>
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
|
||||
<link rel="stylesheet" href="../../js/_bootstrap-icons-1.8.1/bootstrap-icons.css">
|
||||
<style>
|
||||
.function-icon {
|
||||
font-size: 2em;
|
||||
line-height: 1;
|
||||
align-content: center;
|
||||
}
|
||||
.external-link-icon {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
</style>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="page_nav" runat="Server">
|
||||
<h2 class="mb-3">數據透視管理</h2>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
|
||||
<div id="content" class="container py-4">
|
||||
<div class="row">
|
||||
<!-- 第一欄:報表查詢 -->
|
||||
<div class="col-lg-4 mb-4">
|
||||
<h5 class="text-primary mb-3">
|
||||
<i class="bi bi-graph-up"></i> 報表查詢
|
||||
</h5>
|
||||
<div class="list-group">
|
||||
<a href="activity_report.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-bar-chart text-success me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>法會報名統計</div>
|
||||
<small class="text-muted">各法會報名人數與金額統計</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-primary">管理員</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="follower_report.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-people text-info me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>信眾參與分析</div>
|
||||
<small class="text-muted">信眾參與法會的詳細分析</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-info">管理員</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="income_report.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-currency-dollar text-warning me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>收入統計報表</div>
|
||||
<small class="text-muted">各項功德金收入統計分析</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-warning text-dark">財務</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二欄:數據分析 -->
|
||||
<div class="col-lg-4 mb-4">
|
||||
<h5 class="text-primary mb-3">
|
||||
<i class="bi bi-pie-chart"></i> 數據分析
|
||||
</h5>
|
||||
<div class="list-group">
|
||||
<a href="query.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-diagram-3 text-primary me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>數據透視查詢</div>
|
||||
<small class="text-muted">多維度數據透視分析(完整版)</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-success">NEW</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="pivot_analysis.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-diagram-3 text-secondary me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>樞紐分析(舊版)</div>
|
||||
<small class="text-muted">多維度數據透視分析</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-secondary">分析師</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="trend_analysis.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-graph-up-arrow text-success me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>趨勢分析</div>
|
||||
<small class="text-muted">時間序列趨勢變化分析</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-success">分析師</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="comparative_analysis.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-graph-down text-danger me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>對比分析</div>
|
||||
<small class="text-muted">不同時期、法會的對比分析</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-danger">分析師</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第三欄:報表管理 -->
|
||||
<div class="col-lg-4 mb-4">
|
||||
<h5 class="text-primary mb-3">
|
||||
<i class="bi bi-file-earmark-text"></i> 報表管理
|
||||
</h5>
|
||||
<div class="list-group">
|
||||
<a href="custom_report.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-file-plus text-info me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>自訂報表</div>
|
||||
<small class="text-muted">建立自訂的報表範本</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-info">管理員</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="report_schedule.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-clock text-secondary me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>定期報表</div>
|
||||
<small class="text-muted">設定定期自動產生報表</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-secondary">管理員</span>
|
||||
</div>
|
||||
</a>
|
||||
<a href="export_center.aspx" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="d-flex">
|
||||
<i class="bi bi-download text-success me-3 function-icon"></i>
|
||||
<div>
|
||||
<div>匯出中心</div>
|
||||
<small class="text-muted">報表匯出與下載管理</small>
|
||||
</div>
|
||||
</div>
|
||||
<span class="badge bg-success">使用者</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 統計資訊 -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<h6 class="alert-heading">
|
||||
<i class="bi bi-info-circle"></i> 系統說明
|
||||
</h6>
|
||||
<ul class="mb-0">
|
||||
<li><strong>報表查詢</strong>:提供各種預設報表的查詢與統計功能</li>
|
||||
<li><strong>數據分析</strong>:多維度的數據透視與趨勢分析工具</li>
|
||||
<li><strong>報表管理</strong>:自訂報表建立、定期報表設定與匯出管理</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</asp:Content>
|
||||
10
web/admin/pivot/index.aspx.cs
Normal file
10
web/admin/pivot/index.aspx.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Web.UI;
|
||||
|
||||
public partial class admin_pivot_index : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
// 頁面初始化(暫無邏輯)
|
||||
}
|
||||
}
|
||||
1374
web/admin/pivot/pivot-01.aspx
Normal file
1374
web/admin/pivot/pivot-01.aspx
Normal file
File diff suppressed because it is too large
Load Diff
11
web/admin/pivot/pivot-01.aspx.cs
Normal file
11
web/admin/pivot/pivot-01.aspx.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Web.UI;
|
||||
|
||||
public partial class admin_pivot_pivot01 : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
// 頁面初始化(暫無邏輯)
|
||||
// 前端直接呼叫 API
|
||||
}
|
||||
}
|
||||
1128
web/admin/pivot/query.aspx
Normal file
1128
web/admin/pivot/query.aspx
Normal file
File diff suppressed because it is too large
Load Diff
10
web/admin/pivot/query.aspx.cs
Normal file
10
web/admin/pivot/query.aspx.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Web.UI;
|
||||
|
||||
public partial class admin_pivot_query : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
// 頁面初始化(暫無邏輯)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user