Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f5a2e65bd | |||
| 22d283dbe6 | |||
| 3b5d366863 | |||
| c0404b8e70 | |||
| 1a6731e4c6 | |||
| 05ef2e28f3 | |||
| 90ef949ca4 | |||
| 39c9dd29e1 | |||
| a6aa35176c | |||
| 6a43883d08 | |||
| 87a2c35300 | |||
| 7d57e292fe |
176
data/SQL/執行此文件.sql
Normal file
176
data/SQL/執行此文件.sql
Normal file
@@ -0,0 +1,176 @@
|
||||
USE [17168erp_t2]
|
||||
GO
|
||||
/****** Object: Table [dbo].[AncestralTabletArea] Script Date: 2025/10/29 下午 01:32:57 ******/
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
CREATE TABLE [dbo].[AncestralTabletArea](
|
||||
[AreaId] [int] IDENTITY(1,1) NOT NULL,
|
||||
[AreaName] [nvarchar](10) NOT NULL,
|
||||
[AreaCode] [nvarchar](20) NOT NULL,
|
||||
[ParentAreaId] [int] NULL,
|
||||
[AreaType] [nvarchar](10) NULL,
|
||||
[Price] [int] NULL,
|
||||
[SortOrder] [int] NULL,
|
||||
[IsDisabled] [bit] NOT NULL,
|
||||
[Description] [nvarchar](200) NULL,
|
||||
PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[AreaId] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
/****** Object: Table [dbo].[AncestralTabletPosition] Script Date: 2025/10/29 下午 01:32:57 ******/
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
CREATE TABLE [dbo].[AncestralTabletPosition](
|
||||
[PositionId] [int] IDENTITY(1,1) NOT NULL,
|
||||
[AreaId] [int] NOT NULL,
|
||||
[PositionCode] [nvarchar](20) NOT NULL,
|
||||
[PositionName] [nvarchar](50) NULL,
|
||||
[Price] [int] NULL,
|
||||
[StatusCode] [nvarchar](20) NULL,
|
||||
[Description] [nvarchar](200) NULL,
|
||||
[RowNo] [int] NULL,
|
||||
[ColumnNo] [int] NULL,
|
||||
PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[PositionId] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY],
|
||||
CONSTRAINT [UQ_Position_Area_Code] UNIQUE NONCLUSTERED
|
||||
(
|
||||
[AreaId] ASC,
|
||||
[PositionCode] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
/****** Object: Table [dbo].[AncestralTabletPositionRecord] Script Date: 2025/10/29 下午 01:32:57 ******/
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
CREATE TABLE [dbo].[AncestralTabletPositionRecord](
|
||||
[RecordId] [int] IDENTITY(1,1) NOT NULL,
|
||||
[RegistrantCode] [nvarchar](20) NOT NULL,
|
||||
[NPTitle] [nvarchar](30) NULL,
|
||||
[NPStandDate] [date] NOT NULL,
|
||||
[NPYangShang] [nvarchar](20) NULL,
|
||||
[WPContent] [nvarchar](1000) NULL,
|
||||
[CreatedAt] [datetime] NOT NULL,
|
||||
[UpdatedAt] [datetime] NULL,
|
||||
PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[RecordId] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
/****** Object: Table [dbo].[AncestralTabletRegistrant] Script Date: 2025/10/29 下午 01:32:57 ******/
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
CREATE TABLE [dbo].[AncestralTabletRegistrant](
|
||||
[RegistrantCode] [nvarchar](20) NOT NULL,
|
||||
[Name] [nvarchar](50) NOT NULL,
|
||||
[Phone] [nvarchar](50) NULL,
|
||||
[Address] [nvarchar](60) NULL,
|
||||
[RegisterDate] [date] NOT NULL,
|
||||
[Price] [int] NULL,
|
||||
[PositionId] [int] NULL,
|
||||
[StartDate] [date] NOT NULL,
|
||||
[EndDate] [date] NULL,
|
||||
[IsLongTerm] [bit] NOT NULL,
|
||||
[IsActive] [bit] NOT NULL,
|
||||
[CreatedAt] [datetime] NOT NULL,
|
||||
[UpdatedAt] [datetime] NULL,
|
||||
[IsEnd] [bit] NOT NULL,
|
||||
PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[RegistrantCode] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
/****** Object: Table [dbo].[AncestralTabletStatus] Script Date: 2025/10/29 下午 01:32:57 ******/
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
CREATE TABLE [dbo].[AncestralTabletStatus](
|
||||
[StatusCode] [nvarchar](20) NOT NULL,
|
||||
[StatusName] [nvarchar](20) NOT NULL,
|
||||
[StatusType] [nvarchar](20) NOT NULL,
|
||||
PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[StatusCode] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletArea] ADD DEFAULT ((0)) FOR [IsDisabled]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletPositionRecord] ADD DEFAULT (getdate()) FOR [CreatedAt]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT ((0)) FOR [IsLongTerm]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT ((1)) FOR [IsActive]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT (getdate()) FOR [CreatedAt]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletRegistrant] ADD DEFAULT ((0)) FOR [IsEnd]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletArea] WITH CHECK ADD CONSTRAINT [FK_AncestralTabletArea_Parent] FOREIGN KEY([ParentAreaId])
|
||||
REFERENCES [dbo].[AncestralTabletArea] ([AreaId])
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletArea] CHECK CONSTRAINT [FK_AncestralTabletArea_Parent]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletPosition] WITH CHECK ADD CONSTRAINT [FK_Position_Area] FOREIGN KEY([AreaId])
|
||||
REFERENCES [dbo].[AncestralTabletArea] ([AreaId])
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletPosition] CHECK CONSTRAINT [FK_Position_Area]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletPosition] WITH CHECK ADD CONSTRAINT [FK_Position_Status] FOREIGN KEY([StatusCode])
|
||||
REFERENCES [dbo].[AncestralTabletStatus] ([StatusCode])
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletPosition] CHECK CONSTRAINT [FK_Position_Status]
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletPositionRecord] WITH CHECK ADD FOREIGN KEY([RegistrantCode])
|
||||
REFERENCES [dbo].[AncestralTabletRegistrant] ([RegistrantCode])
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletRegistrant] WITH CHECK ADD CONSTRAINT [FK_Registrant_Position] FOREIGN KEY([PositionId])
|
||||
REFERENCES [dbo].[AncestralTabletPosition] ([PositionId])
|
||||
GO
|
||||
ALTER TABLE [dbo].[AncestralTabletRegistrant] CHECK CONSTRAINT [FK_Registrant_Position]
|
||||
GO
|
||||
|
||||
INSERT [dbo].[AncestralTabletStatus] ([StatusCode], [StatusName], [StatusType]) VALUES (N'available', N'可用', N'Position')
|
||||
INSERT [dbo].[AncestralTabletStatus] ([StatusCode], [StatusName], [StatusType]) VALUES (N'maintenance', N'維護中', N'Position')
|
||||
INSERT [dbo].[AncestralTabletStatus] ([StatusCode], [StatusName], [StatusType]) VALUES (N'used', N'已使用', N'Position')
|
||||
GO
|
||||
-- 1. 如果不存在 OrderUuid 栏位,则新增
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM sys.columns
|
||||
WHERE Name = N'OrderUuid'
|
||||
AND Object_ID = Object_ID(N'dbo.GuaDanOrderGuest')
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[GuaDanOrderGuest]
|
||||
ADD [OrderUuid] UNIQUEIDENTIFIER NULL;
|
||||
END
|
||||
GO
|
||||
|
||||
-- 2. 如果不存在 FK_GuaDanOrderGuest_Order 外键,则新增
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM sys.foreign_keys
|
||||
WHERE Name = N'FK_GuaDanOrderGuest_Order'
|
||||
AND parent_object_id = OBJECT_ID(N'dbo.GuaDanOrderGuest')
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[GuaDanOrderGuest]
|
||||
ADD CONSTRAINT [FK_GuaDanOrderGuest_Order]
|
||||
FOREIGN KEY ([OrderUuid]) REFERENCES [dbo].[GuaDanOrder] ([Uuid]);
|
||||
END
|
||||
GO
|
||||
26
data/memo/report-view.md
Normal file
26
data/memo/report-view.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 報表系統規劃
|
||||
為每場活動(法會)建立報名到舉辦過程的報表,
|
||||
要涵蓋以下面向的統計分析資訊
|
||||
|
||||
- 以單一場法會為核心
|
||||
- 時間面向: 當前狀況, 指定期間狀況
|
||||
- 維度:
|
||||
- 信眾: 報名數量, 金額, 收款狀態
|
||||
- 牌位型態(活動品項表): 報名數量, 金額, 收款狀態
|
||||
- 收款狀態: 己收/未收 統計明細
|
||||
|
||||
## 法會報表系統查詢規劃**
|
||||
|
||||
### 1. 核心基礎 VIEW
|
||||
1. `vw_activity_registration_base` - 單一活動的完整報名基礎資料
|
||||
2. `vw_activity_payment_detail` - 單一活動的完整收款明細資料
|
||||
### 2. 統計分析 VIEW
|
||||
3. `vw_activity_follower_statistics` - 按信眾統計報名情況
|
||||
4. `vw_activity_item_statistics` - 按品項統計報名情況
|
||||
5. `vw_activity_payment_status` - 收款狀態統計分析
|
||||
### 3. 時間維度分析 VIEW
|
||||
6. `vw_activity_registration_trend` - 按日期統計報名趨勢
|
||||
7. `vw_activity_payment_trend` - 按收款日期統計收款趨勢
|
||||
### 4. 詳細查詢 VIEW
|
||||
8. `vw_activity_unpaid_detail` - 未收款明細清單
|
||||
9. `vw_activity_transfer_reconciliation` - 匯款對帳明細
|
||||
252
data/memo/report-view.sql
Normal file
252
data/memo/report-view.sql
Normal file
@@ -0,0 +1,252 @@
|
||||
drop view if exists vw_activity_registration_base;
|
||||
drop view if exists vw_activity_payment_detail;
|
||||
drop view if exists vw_activity_follower_statistics;
|
||||
drop view if exists vw_activity_item_statistics;
|
||||
drop view if exists vw_activity_payment_status;
|
||||
drop view if exists vw_activity_registration_trend;
|
||||
drop view if exists vw_activity_payment_trend;
|
||||
drop view if exists vw_activity_unpaid_detail;
|
||||
drop view if exists vw_activity_transfer_reconciliation;
|
||||
GO
|
||||
-- 1. 法會報名基礎資料 VIEW
|
||||
CREATE VIEW vw_activity_registration_base AS
|
||||
SELECT
|
||||
a.num AS 活動編號,
|
||||
a.subject AS 活動名稱,
|
||||
a.startDate_solar AS 活動開始日期,
|
||||
a.endDate_solar AS 活動結束日期,
|
||||
po.order_no AS 報名單號,
|
||||
po.up_time AS 報名日期,
|
||||
po.keyin1 AS 報名狀態,
|
||||
f.num AS 信眾編號,
|
||||
f.f_number AS 信眾代號,
|
||||
f.u_name AS 信眾姓名,
|
||||
f.phone AS 聯絡電話,
|
||||
f.identity_type AS 身分別,
|
||||
f.country AS 國籍,
|
||||
pod.num AS 報名明細編號,
|
||||
ai.num AS 品項編號,
|
||||
ai.subject AS 品項名稱,
|
||||
ai.category AS 品項分類,
|
||||
pod.price AS 單價,
|
||||
pod.qty AS 數量,
|
||||
pod.price * pod.qty AS 應繳金額,
|
||||
pod.pay AS 已收金額,
|
||||
(pod.price * pod.qty - ISNULL(pod.pay, 0)) AS 未收金額,
|
||||
pod.pay_date AS 付款期限,
|
||||
pod.start_date AS 開始日期,
|
||||
pod.due_date AS 到期日期,
|
||||
pod.keyin1 AS 明細狀態,
|
||||
pod.demo AS 備註
|
||||
FROM activity a
|
||||
INNER JOIN pro_order po ON a.num = po.activity_num
|
||||
INNER JOIN followers f ON po.f_num = f.num
|
||||
INNER JOIN pro_order_detail pod ON po.order_no = pod.order_no
|
||||
INNER JOIN actItem ai ON pod.actItem_num = ai.num;
|
||||
GO
|
||||
|
||||
-- 2. 收款明細基礎資料 VIEW
|
||||
CREATE VIEW vw_activity_payment_detail AS
|
||||
SELECT
|
||||
a.num AS 活動編號,
|
||||
a.subject AS 活動名稱,
|
||||
po.order_no AS 報名單號,
|
||||
f.u_name AS 信眾姓名,
|
||||
pod.num AS 報名明細編號,
|
||||
ai.subject AS 品項名稱,
|
||||
por.num AS 收款記錄編號,
|
||||
por.price AS 收款金額,
|
||||
por.payment AS 付款方式,
|
||||
por.pay_date AS 收款日期,
|
||||
por.organization AS 收款機構,
|
||||
por.bank_code AS 銀行代碼,
|
||||
por.transfer_id AS 匯款記錄ID,
|
||||
por.reconcile_memo AS 對帳備註,
|
||||
tr.name AS 匯款人姓名,
|
||||
tr.phone AS 匯款人電話,
|
||||
tr.amount AS 匯款金額,
|
||||
tr.check_date AS 入帳日期,
|
||||
tr.status AS 匯款狀態
|
||||
FROM activity a
|
||||
INNER JOIN pro_order po ON a.num = po.activity_num
|
||||
INNER JOIN followers f ON po.f_num = f.num
|
||||
INNER JOIN pro_order_detail pod ON po.order_no = pod.order_no
|
||||
INNER JOIN actItem ai ON pod.actItem_num = ai.num
|
||||
LEFT JOIN pro_order_record por ON pod.num = por.detail_num
|
||||
LEFT JOIN transfer_register tr ON por.transfer_id = tr.id;
|
||||
GO
|
||||
|
||||
-- 3. 信眾報名統計 VIEW
|
||||
CREATE VIEW vw_activity_follower_statistics AS
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
信眾編號,
|
||||
信眾代號,
|
||||
信眾姓名,
|
||||
聯絡電話,
|
||||
身分別,
|
||||
國籍,
|
||||
COUNT(DISTINCT 報名單號) AS 報名單數,
|
||||
COUNT(報名明細編號) AS 報名品項數,
|
||||
SUM(應繳金額) AS 應繳總金額,
|
||||
SUM(已收金額) AS 已收總金額,
|
||||
SUM(未收金額) AS 未收總金額,
|
||||
CASE
|
||||
WHEN SUM(未收金額) = 0 THEN '已繳清'
|
||||
WHEN SUM(未收金額) = SUM(應繳金額) THEN '未繳'
|
||||
ELSE '部分繳款'
|
||||
END AS 繳款狀態
|
||||
FROM vw_activity_registration_base
|
||||
GROUP BY 活動編號, 活動名稱, 信眾編號, 信眾代號, 信眾姓名, 聯絡電話, 身分別, 國籍;
|
||||
GO
|
||||
|
||||
-- 4. 品項報名統計 VIEW
|
||||
CREATE VIEW vw_activity_item_statistics AS
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
品項編號,
|
||||
品項名稱,
|
||||
品項分類,
|
||||
單價,
|
||||
COUNT(報名明細編號) AS 報名數量,
|
||||
SUM(數量) AS 總數量,
|
||||
SUM(應繳金額) AS 應繳總金額,
|
||||
SUM(已收金額) AS 已收總金額,
|
||||
SUM(未收金額) AS 未收總金額,
|
||||
CASE
|
||||
WHEN COUNT(報名明細編號) = 0 THEN 0
|
||||
ELSE AVG(已收金額)
|
||||
END AS 平均已收金額,
|
||||
CASE
|
||||
WHEN SUM(未收金額) = 0 THEN '已收齊'
|
||||
WHEN SUM(未收金額) = SUM(應繳金額) THEN '未收款'
|
||||
ELSE '部分收款'
|
||||
END AS 收款狀態
|
||||
FROM vw_activity_registration_base
|
||||
GROUP BY 活動編號, 活動名稱, 品項編號, 品項名稱, 品項分類, 單價;
|
||||
GO
|
||||
|
||||
-- 5. 收款狀態統計 VIEW
|
||||
CREATE VIEW vw_activity_payment_status AS
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
'總計' AS 統計項目,
|
||||
COUNT(DISTINCT 報名單號) AS 報名單數,
|
||||
COUNT(報名明細編號) AS 報名品項數,
|
||||
SUM(應繳金額) AS 應繳總金額,
|
||||
SUM(已收金額) AS 已收總金額,
|
||||
SUM(未收金額) AS 未收總金額,
|
||||
CASE
|
||||
WHEN SUM(應繳金額) = 0 THEN 0
|
||||
ELSE ROUND(SUM(已收金額) * 100.0 / SUM(應繳金額), 2)
|
||||
END AS 收款率
|
||||
FROM vw_activity_registration_base
|
||||
GROUP BY 活動編號, 活動名稱
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
CASE
|
||||
WHEN 未收金額 = 0 THEN '已繳清'
|
||||
WHEN 未收金額 = 應繳金額 THEN '未繳'
|
||||
ELSE '部分繳款'
|
||||
END AS 統計項目,
|
||||
COUNT(DISTINCT 報名單號) AS 報名單數,
|
||||
COUNT(報名明細編號) AS 報名品項數,
|
||||
SUM(應繳金額) AS 應繳總金額,
|
||||
SUM(已收金額) AS 已收總金額,
|
||||
SUM(未收金額) AS 未收總金額,
|
||||
CASE
|
||||
WHEN SUM(應繳金額) = 0 THEN 0
|
||||
ELSE ROUND(SUM(已收金額) * 100.0 / SUM(應繳金額), 2)
|
||||
END AS 收款率
|
||||
FROM vw_activity_registration_base
|
||||
GROUP BY 活動編號, 活動名稱,
|
||||
CASE
|
||||
WHEN 未收金額 = 0 THEN '已繳清'
|
||||
WHEN 未收金額 = 應繳金額 THEN '未繳'
|
||||
ELSE '部分繳款'
|
||||
END;
|
||||
GO
|
||||
|
||||
-- 6. 報名趨勢分析 VIEW
|
||||
CREATE VIEW vw_activity_registration_trend AS
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
CAST(報名日期 AS DATE) AS 報名日期,
|
||||
COUNT(DISTINCT 報名單號) AS 當日報名單數,
|
||||
COUNT(報名明細編號) AS 當日報名品項數,
|
||||
SUM(應繳金額) AS 當日應繳金額,
|
||||
SUM(已收金額) AS 當日已收金額,
|
||||
SUM(未收金額) AS 當日未收金額
|
||||
FROM vw_activity_registration_base
|
||||
GROUP BY 活動編號, 活動名稱, CAST(報名日期 AS DATE);
|
||||
GO
|
||||
|
||||
-- 7. 收款趨勢分析 VIEW
|
||||
CREATE VIEW vw_activity_payment_trend AS
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
CAST(收款日期 AS DATE) AS 收款日期,
|
||||
COUNT(收款記錄編號) AS 當日收款筆數,
|
||||
SUM(收款金額) AS 當日收款金額,
|
||||
COUNT(DISTINCT 報名單號) AS 當日收款單數,
|
||||
CASE
|
||||
WHEN COUNT(收款記錄編號) = 0 THEN 0
|
||||
ELSE AVG(收款金額)
|
||||
END AS 平均收款金額
|
||||
FROM vw_activity_payment_detail
|
||||
WHERE 收款日期 IS NOT NULL
|
||||
GROUP BY 活動編號, 活動名稱, CAST(收款日期 AS DATE);
|
||||
GO
|
||||
|
||||
-- 8. 未收款明細 VIEW
|
||||
CREATE VIEW vw_activity_unpaid_detail AS
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
報名單號,
|
||||
信眾姓名,
|
||||
聯絡電話,
|
||||
品項名稱,
|
||||
應繳金額,
|
||||
已收金額,
|
||||
未收金額,
|
||||
付款期限,
|
||||
CASE
|
||||
WHEN 付款期限 < GETDATE() THEN '已逾期'
|
||||
WHEN 付款期限 <= DATEADD(DAY, 3, GETDATE()) THEN '即將到期'
|
||||
ELSE '未到期'
|
||||
END AS 到期狀態
|
||||
FROM vw_activity_registration_base
|
||||
WHERE 未收金額 > 0;
|
||||
GO
|
||||
|
||||
-- 9. 匯款對帳明細 VIEW
|
||||
CREATE VIEW vw_activity_transfer_reconciliation AS
|
||||
SELECT
|
||||
活動編號,
|
||||
活動名稱,
|
||||
匯款記錄ID,
|
||||
匯款人姓名,
|
||||
匯款人電話,
|
||||
匯款金額,
|
||||
入帳日期,
|
||||
匯款狀態,
|
||||
COUNT(收款記錄編號) AS 關聯收款筆數,
|
||||
SUM(收款金額) AS 已對帳金額,
|
||||
CASE
|
||||
WHEN SUM(收款金額) IS NULL THEN 匯款金額
|
||||
ELSE 匯款金額 - SUM(收款金額)
|
||||
END AS 剩餘金額
|
||||
FROM vw_activity_payment_detail
|
||||
WHERE 匯款記錄ID IS NOT NULL
|
||||
GROUP BY 活動編號, 活動名稱, 匯款記錄ID, 匯款人姓名, 匯款人電話, 匯款金額, 入帳日期, 匯款狀態;
|
||||
GO
|
||||
484
data/memo/report.md
Normal file
484
data/memo/report.md
Normal file
@@ -0,0 +1,484 @@
|
||||
# 相關頁面
|
||||
## 基本功能
|
||||
admin/order/index.aspx
|
||||
admin/activity/index.aspx
|
||||
admin/follower/index.aspx
|
||||
admin/activity/index2.aspx
|
||||
admin/transfer/index.aspx
|
||||
|
||||
## 入帳沖帳
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\balance_reconcile_query.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\balance_reconcile.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\group_reconcile.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\index.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\personal_reconcile.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\register.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify_order_record_query.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify1.aspx
|
||||
D:\dev\ez\17168erp\git_17888\web\admin\transfer\verify2.aspx
|
||||
|
||||
# 資料結構
|
||||
|
||||
## 📊 17168ERP 系統使用的資料表架構
|
||||
|
||||
### 🎯 **核心業務資料表**
|
||||
|
||||
#### 1. **報名管理系統** (`order/index.aspx`)
|
||||
**主要資料表:**
|
||||
- **`pro_order`** - 報名主表
|
||||
- `order_no` (單號)、`up_time` (報名日期)、`keyin1` (單據狀態)
|
||||
- `f_num` (信眾編號)、`activity_num` (活動編號)、`phone` (聯絡電話)
|
||||
|
||||
- **`pro_order_detail`** - 報名明細表
|
||||
- `order_no` (關聯主表)、`actItem_num` (活動品項)、`f_num` (報名者)
|
||||
- `price` (金額)、`qty` (數量)、`pay` (已收金額)、`pay_date` (付款期限)
|
||||
|
||||
- **`activity`** - 活動主表
|
||||
- `num`、`subject` (活動名稱)、`start_date` (開始日期)、`end_date` (結束日期)
|
||||
|
||||
- **`actItem`** - 活動品項表
|
||||
- `num`、`subject` (品項名稱)、`category` (品項分類)
|
||||
|
||||
#### 2. **信眾管理系統** (`follower/index.aspx`)
|
||||
**主要資料表:**
|
||||
- **`followers`** - 信眾基本資料表
|
||||
- `num`、`f_number` (信眾編號)、`u_name` (姓名)、`sex` (性別)
|
||||
- `identity_type` (身分別)、`birthday` (生日)、`phone` (電話)
|
||||
- `address` (地址)、`country` (國籍)、`refugedate` (皈依日期)
|
||||
|
||||
- **`countries`** - 國籍資料表
|
||||
- `ID`、`name_zh` (中文名稱)、`name_en` (英文名稱)
|
||||
|
||||
#### 3. **活動管理系統** (`activity/index.aspx`)
|
||||
**主要資料表:**
|
||||
- **`activity`** - 活動主表
|
||||
- `num`、`subject` (活動名稱)、`startDate_solar` (國曆開始日期)
|
||||
- `startDate_lunar` (農曆開始日期)、`endDate_solar` (國曆結束日期)
|
||||
- `endDate_lunar` (農曆結束日期)、`dueDate` (報名截止日期)
|
||||
|
||||
- **`activity_kind`** - 活動分類表
|
||||
- `num`、`subject` (分類名稱)
|
||||
|
||||
#### 4. **匯款沖帳系統** (`transfer/index.aspx`)
|
||||
**主要資料表:**
|
||||
- **`transfer_register`** - 匯款登錄表
|
||||
- `id`、`name` (匯款人姓名)、`phone` (電話)、`amount` (匯款金額)
|
||||
- `pay_type` (付款方式)、`account_last5` (帳號後五碼)
|
||||
- `proof_img` (匯款證明圖片)、`status` (狀態)
|
||||
- `f_num_match` (配對信眾編號)、`check_amount` (核對金額)
|
||||
|
||||
- **`accounting`** - 會計帳務表
|
||||
- `num`、`category` (科目分類)、`kind` (收支類型)
|
||||
- `price` (金額)、`debtor` (債務人)、`activity_num` (關聯活動)
|
||||
|
||||
- **`pro_order_record`** - 報名收款記錄表
|
||||
- `num`、`detail_num` (關聯明細)、`price` (金額)、`payment` (付款方式)
|
||||
- `pay_date` (收款日期)、`transfer_id` (關聯匯款記錄)
|
||||
|
||||
### 🔗 **關聯關係**
|
||||
|
||||
#### **主要外鍵關聯:**
|
||||
```
|
||||
pro_order → followers (f_num)
|
||||
pro_order → activity (activity_num)
|
||||
pro_order_detail → pro_order (order_no)
|
||||
pro_order_detail → actItem (actItem_num)
|
||||
pro_order_detail → followers (f_num)
|
||||
transfer_register → followers (f_num)
|
||||
transfer_register → activity (activity_num)
|
||||
accounting → pro_order_detail (pro_order_detail_num)
|
||||
```
|
||||
|
||||
### 📈 **資料表統計**
|
||||
|
||||
| 功能模組 | 主要資料表數量 | 核心業務表 |
|
||||
|---------|-------------|-----------|
|
||||
| 報名管理 | 5個 | `pro_order`, `pro_order_detail` |
|
||||
| 信眾管理 | 2個 | `followers`, `countries` |
|
||||
| 活動管理 | 2個 | `activity`, `activity_kind` |
|
||||
| 匯款沖帳 | 3個 | `transfer_register`, `accounting`, `pro_order_record` |
|
||||
|
||||
### 🎯 **系統特色**
|
||||
|
||||
1. **模組化設計**:每個功能模組都有獨立的資料表群組
|
||||
2. **關聯完整**:透過外鍵建立完整的業務關聯關係
|
||||
3. **狀態管理**:多個狀態表支援複雜的業務流程
|
||||
4. **UUID設計**:新模組採用UUID作為主鍵,提升擴展性
|
||||
5. **軟刪除機制**:使用`IsDeleted`欄位而非物理刪除
|
||||
|
||||
這個資料表架構支援了一個完整的宗教活動管理ERP系統,涵蓋了從信眾管理、活動報名、床位安排到財務沖帳的全流程管理。
|
||||
|
||||
# 入帳沖帳
|
||||
```
|
||||
報名者匯款 → transfer_register (status: pending)
|
||||
↓
|
||||
出納核對身份 → transfer_register (status: verified, f_num_match)
|
||||
↓
|
||||
出納核對金額 → transfer_register (status: confirmed, check_amount)
|
||||
↓
|
||||
會計個人沖帳 → pro_order_record + accounting
|
||||
↓
|
||||
會計共同沖帳 → pro_order_record + accounting (多筆)
|
||||
↓
|
||||
會計餘額核銷 → transfer_register (remain_amount)
|
||||
↓
|
||||
查詢統計報表 → 各表關聯查詢
|
||||
```
|
||||
|
||||
# DB SCHEMA
|
||||
我來為您使用 Mermaid 語法繪製 17168ERP 系統的 ER 圖,展示各個資料表之間的關聯關係。
|
||||
|
||||
|
||||
[1 tool called]
|
||||
|
||||
我無法直接創建 Mermaid 圖表,但我可以為您提供完整的 Mermaid ER 圖代碼,您可以複製到支援 Mermaid 的工具中查看(如 GitHub、GitLab、或 Mermaid Live Editor)。
|
||||
|
||||
以下是 17168ERP 系統的 ER 圖 Mermaid 代碼:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
%% 信眾管理系統
|
||||
followers {
|
||||
int num PK
|
||||
string f_number
|
||||
string u_name
|
||||
string sex
|
||||
int identity_type
|
||||
datetime birthday
|
||||
string phone
|
||||
string email
|
||||
datetime refugedate
|
||||
string refuge_name
|
||||
string address
|
||||
string demo
|
||||
int leader
|
||||
string socialid1
|
||||
string socialid2
|
||||
string tab
|
||||
string contactor
|
||||
string contactor_phone
|
||||
string blood
|
||||
string customize_data
|
||||
}
|
||||
|
||||
countries {
|
||||
int ID PK
|
||||
string name_zh
|
||||
string name_en
|
||||
string range
|
||||
}
|
||||
|
||||
%% 活動管理系統
|
||||
activity {
|
||||
int num PK
|
||||
string subject
|
||||
datetime startDate_solar
|
||||
datetime startDate_lunar
|
||||
datetime endDate_solar
|
||||
datetime endDate_lunar
|
||||
datetime dueDate
|
||||
int kind
|
||||
string demo
|
||||
string customize_data
|
||||
}
|
||||
|
||||
activity_kind {
|
||||
int num PK
|
||||
string subject
|
||||
string demo
|
||||
}
|
||||
|
||||
actItem {
|
||||
int num PK
|
||||
int activity_num FK
|
||||
string subject
|
||||
int category
|
||||
string demo
|
||||
string customize_data
|
||||
}
|
||||
|
||||
%% 報名管理系統
|
||||
pro_order {
|
||||
string order_no PK
|
||||
datetime up_time
|
||||
datetime reg_time
|
||||
string keyin1
|
||||
int f_num FK
|
||||
string phone
|
||||
int activity_num FK
|
||||
string address
|
||||
string demo
|
||||
string customize_data
|
||||
int introducer FK
|
||||
boolean send_receipt
|
||||
string receipt_title
|
||||
}
|
||||
|
||||
pro_order_detail {
|
||||
int num PK
|
||||
string order_no FK
|
||||
int actItem_num FK
|
||||
int f_num FK
|
||||
string f_num_tablet
|
||||
string address
|
||||
int from_id FK
|
||||
string from_id_tablet
|
||||
datetime due_date
|
||||
int bed_type
|
||||
float price
|
||||
int qty
|
||||
datetime start_date
|
||||
datetime extend_date
|
||||
float pay
|
||||
datetime pay_date
|
||||
int keyin1
|
||||
string demo
|
||||
datetime UpdateTime
|
||||
}
|
||||
|
||||
%% 匯款沖帳系統
|
||||
transfer_register {
|
||||
int id PK
|
||||
int activity_num FK
|
||||
string name
|
||||
string phone
|
||||
string pay_type
|
||||
string account_last5
|
||||
decimal amount
|
||||
string pay_mode
|
||||
string note
|
||||
string proof_img
|
||||
string status
|
||||
datetime create_time
|
||||
int f_num_match FK
|
||||
int f_num FK
|
||||
int acc_num
|
||||
datetime check_date
|
||||
decimal check_amount
|
||||
string check_memo
|
||||
string check_status
|
||||
int acc_kind
|
||||
int member_num
|
||||
datetime verify_time
|
||||
string verify_note
|
||||
string draft
|
||||
decimal remain_amount
|
||||
int balance_act_item FK
|
||||
int balance_pro_order_detail FK
|
||||
}
|
||||
|
||||
accounting {
|
||||
int num PK
|
||||
datetime uptime
|
||||
int category
|
||||
int kind
|
||||
int kind2
|
||||
float price
|
||||
float tax
|
||||
string demo
|
||||
int mem_num
|
||||
string debtor
|
||||
int activity_num FK
|
||||
string excerpt
|
||||
datetime reg_time
|
||||
int pro_order_detail_num FK
|
||||
}
|
||||
|
||||
pro_order_record {
|
||||
int num PK
|
||||
int detail_num FK
|
||||
float price
|
||||
int payment
|
||||
datetime reg_time
|
||||
datetime pay_date
|
||||
string organization
|
||||
string bank_code
|
||||
int transfer_id FK
|
||||
string reconcile_memo
|
||||
}
|
||||
|
||||
%% 區域床位管理系統
|
||||
Region {
|
||||
Guid Uuid PK
|
||||
string Name
|
||||
boolean Gender
|
||||
boolean IsActive
|
||||
boolean IsDeleted
|
||||
}
|
||||
|
||||
Room {
|
||||
Guid Uuid PK
|
||||
string Name
|
||||
boolean Gender
|
||||
int BedCount
|
||||
boolean IsActive
|
||||
datetime CreatedAt
|
||||
datetime UpdatedAt
|
||||
boolean IsDeleted
|
||||
Guid RegionUuid FK
|
||||
}
|
||||
|
||||
RegionRoomBed {
|
||||
Guid Uuid PK
|
||||
string Name
|
||||
boolean IsActive
|
||||
boolean Gender
|
||||
boolean IsDeleted
|
||||
Guid RoomUuid FK
|
||||
string StatusCode FK
|
||||
}
|
||||
|
||||
RegionRoomBedStatus {
|
||||
string Code PK
|
||||
string Name
|
||||
string Description
|
||||
int Category
|
||||
boolean IsDeleted
|
||||
}
|
||||
|
||||
%% 掛單管理系統
|
||||
GuaDanOrder {
|
||||
Guid Uuid PK
|
||||
datetime StartDate
|
||||
datetime EndDate
|
||||
int CreateUser FK
|
||||
datetime CreatedAt
|
||||
datetime UpdatedAt
|
||||
string Notes
|
||||
string GuaDanOrderNo
|
||||
int BookerFollowerNum FK
|
||||
string BookerName
|
||||
string BookerPhone
|
||||
boolean IsDeleted
|
||||
int ActivityNum FK
|
||||
boolean IsCancel
|
||||
}
|
||||
|
||||
GuaDanOrderGuest {
|
||||
Guid Uuid PK
|
||||
string GuaDanOrderNo FK
|
||||
int FollowerNum FK
|
||||
boolean IsDeleted
|
||||
Guid RoomUuid FK
|
||||
Guid BedUuid FK
|
||||
datetime CheckInAt
|
||||
datetime CheckOutAt
|
||||
string StatusCode FK
|
||||
}
|
||||
|
||||
RegionAndRoomAndBedSchedule {
|
||||
Guid Uuid PK
|
||||
Guid TargetUuid FK
|
||||
string GuaDanOrderNo
|
||||
date ScheduleDate
|
||||
boolean IsCancel
|
||||
boolean IsDeleted
|
||||
int UseType
|
||||
string Title
|
||||
string Description
|
||||
string CreatedBy
|
||||
datetime CreatedAt
|
||||
Guid GuaDanOrderGuestUuid FK
|
||||
}
|
||||
|
||||
%% 關聯關係
|
||||
followers ||--o{ pro_order : "報名"
|
||||
followers ||--o{ pro_order_detail : "報名明細"
|
||||
followers ||--o{ transfer_register : "匯款人"
|
||||
followers ||--o{ GuaDanOrderGuest : "掛單客人"
|
||||
|
||||
activity ||--o{ pro_order : "活動報名"
|
||||
activity ||--o{ actItem : "活動品項"
|
||||
activity ||--o{ transfer_register : "活動匯款"
|
||||
activity ||--o{ GuaDanOrder : "掛單活動"
|
||||
|
||||
pro_order ||--o{ pro_order_detail : "報名明細"
|
||||
pro_order ||--o{ pro_order_record : "收款記錄"
|
||||
|
||||
pro_order_detail ||--o{ pro_order_record : "明細收款"
|
||||
pro_order_detail ||--o{ accounting : "會計分錄"
|
||||
pro_order_detail ||--o{ transfer_register : "沖帳明細"
|
||||
|
||||
transfer_register ||--o{ pro_order_record : "匯款記錄"
|
||||
transfer_register ||--o{ accounting : "會計記錄"
|
||||
|
||||
Region ||--o{ Room : "區域房間"
|
||||
Room ||--o{ RegionRoomBed : "房間床位"
|
||||
RegionRoomBedStatus ||--o{ RegionRoomBed : "床位狀態"
|
||||
RegionRoomBedStatus ||--o{ GuaDanOrderGuest : "客人狀態"
|
||||
|
||||
GuaDanOrder ||--o{ GuaDanOrderGuest : "掛單客人"
|
||||
GuaDanOrderGuest ||--o{ RegionAndRoomAndBedSchedule : "床位排程"
|
||||
|
||||
countries ||--o{ followers : "國籍"
|
||||
activity_kind ||--o{ activity : "活動分類"
|
||||
```
|
||||
|
||||
## 📊 **ER 圖說明**
|
||||
|
||||
### **主要實體群組:**
|
||||
|
||||
1. **信眾管理群組**
|
||||
- `followers` (信眾基本資料)
|
||||
- `countries` (國籍資料)
|
||||
|
||||
2. **活動管理群組**
|
||||
- `activity` (活動主表)
|
||||
- `activity_kind` (活動分類)
|
||||
- `actItem` (活動品項)
|
||||
|
||||
3. **報名管理群組**
|
||||
- `pro_order` (報名主表)
|
||||
- `pro_order_detail` (報名明細)
|
||||
|
||||
4. **財務管理群組**
|
||||
- `transfer_register` (匯款登錄)
|
||||
- `accounting` (會計帳務)
|
||||
- `pro_order_record` (收款記錄)
|
||||
|
||||
5. **床位管理群組**
|
||||
- `Region` (區域)
|
||||
- `Room` (房間)
|
||||
- `RegionRoomBed` (床位)
|
||||
- `RegionRoomBedStatus` (床位狀態)
|
||||
|
||||
6. **掛單管理群組**
|
||||
- `GuaDanOrder` (掛單主表)
|
||||
- `GuaDanOrderGuest` (掛單客人)
|
||||
- `RegionAndRoomAndBedSchedule` (床位排程)
|
||||
|
||||
### **關鍵關聯關係:**
|
||||
|
||||
- **一對多關係**:使用 `||--o{` 表示
|
||||
- **外鍵關聯**:FK 標記顯示主要的外鍵關聯
|
||||
- **業務流程**:從信眾報名 → 匯款 → 沖帳 → 床位安排的完整流程
|
||||
|
||||
您可以将這個 Mermaid 代碼複製到 [Mermaid Live Editor](https://mermaid.live/) 或其他支援 Mermaid 的工具中查看完整的 ER 圖表。
|
||||
|
||||
---
|
||||
# 報表系統規劃
|
||||
為每場活動(法會)建立報名到舉辦過程的報表,
|
||||
要涵蓋以下面向的統計分析資訊
|
||||
|
||||
- 以單一場法會為核心
|
||||
- 時間面向: 當前狀況, 指定期間狀況
|
||||
- 維度:
|
||||
- 信眾: 報名數量, 金額, 收款狀態
|
||||
- 牌位型態(活動品項表): 報名數量, 金額, 收款狀態
|
||||
- 收款狀態: 己收/未收 統計明細
|
||||
|
||||
## 執行方式:
|
||||
- 類似excel, 詳細資料->pivot table
|
||||
- 先建立一個(或數個)最核心的sql view, 包含各項:報名資料, 收款明細
|
||||
- 先以單一活動編號為固定FILTER : activity.num=59
|
||||
- 再依不同面向, 建立第二級的sql view
|
||||
- 再人工將以上:第一, 第二級的SQL VIEW, 以EXCEL查詢, 做資料分析/整理
|
||||
- 相關英文欄名, 在VIEW中以中文別名顯示
|
||||
|
||||
## 相關SQL VIEW
|
||||
- (查詢清單)
|
||||
### (查詢)
|
||||
(說明)
|
||||
```sql
|
||||
```
|
||||
BIN
data/查詢範例.xlsx
Normal file
BIN
data/查詢範例.xlsx
Normal file
Binary file not shown.
19
web/App_Code/Model/Partial/family_members.cs
Normal file
19
web/App_Code/Model/Partial/family_members.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
/// <summary>
|
||||
/// added
|
||||
/// </summary>
|
||||
namespace Model
|
||||
{
|
||||
[MetadataType(typeof(family_membersMetadata))]
|
||||
public partial class family_members
|
||||
{
|
||||
private class family_membersMetadata
|
||||
{
|
||||
[JsonIgnore]
|
||||
public virtual follower follower { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,19 @@ namespace Model
|
||||
public virtual ICollection<followers_tablet> followers_tablet { get; set; }
|
||||
[JsonIgnore]
|
||||
public virtual appellation appellation { get; set; }
|
||||
/// <summary>
|
||||
/// added
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<family_members> family_members { get; set; }
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<transfer_register> transfer_register { get; set; }
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<transfer_register> transfer_register1 { get; set; }
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<GuaDanOrder> GuaDanOrder { get; set; }
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<GuaDanOrderGuest> GuaDanOrderGuest { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -98,7 +98,17 @@ public class familyMembersController : ApiController
|
||||
[Route("api/familymembers/follower/{followerId}")]
|
||||
public IHttpActionResult GetByFollower(int followerId)
|
||||
{
|
||||
//var originalLazyLoading = _db.Configuration.LazyLoadingEnabled;
|
||||
//var originalProxyCreation = _db.Configuration.ProxyCreationEnabled;
|
||||
//
|
||||
//_db.Configuration.LazyLoadingEnabled = false;
|
||||
//_db.Configuration.ProxyCreationEnabled = false;
|
||||
|
||||
var familyMembers = _db.family_members.Where(fm => fm.follower_num == followerId).ToList();
|
||||
|
||||
//_db.Configuration.LazyLoadingEnabled = originalLazyLoading;
|
||||
//_db.Configuration.ProxyCreationEnabled = originalProxyCreation;
|
||||
|
||||
return Ok(familyMembers);
|
||||
}
|
||||
|
||||
|
||||
250
web/App_Code/api/pivot01Controller.cs
Normal file
250
web/App_Code/api/pivot01Controller.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
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 參數(包含 SQLOLEDB 和 SQLNCLI11)
|
||||
_connectionString = shopConnectionString
|
||||
.Replace("Provider=SQLOLEDB;", "")
|
||||
.Replace("Provider=SQLNCLI11;", "")
|
||||
.Replace(" Provider=SQLOLEDB", "")
|
||||
.Replace(" Provider=SQLNCLI11", "");
|
||||
}
|
||||
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
@@ -1393,7 +1393,7 @@
|
||||
message: `確定要將 ${guest.follower.u_name || ''} 退房嗎?`,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
const response = await axios.post(`/api/guadanorderguest/checkout`, null, {
|
||||
const response = await axios.post(HTTP_HOST + HTTP_HOST + `api/guadanorderguest/checkout`, null, {
|
||||
params: { uuid: guest.uuid }
|
||||
});
|
||||
|
||||
@@ -1594,7 +1594,7 @@
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
// 發送請求到後端 API
|
||||
const response = await axios.post(`/api/guadanorderguest/checkin`, null, {
|
||||
const response = await axios.post(HTTP_HOST + `api/guadanorderguest/checkin`, null, {
|
||||
params: { uuid: guest.uuid }
|
||||
});
|
||||
|
||||
|
||||
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