using NPOI.SS.UserModel; using NPOI.Util; using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; using System.Web.Script.Services; using System.Web.Services; using WebLib; namespace 報到系統 { [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] [ScriptService] public class ClassesService : System.Web.Services.WebService { /// /// 搜尋活動 /// [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public BaseResponse SearchClasses(int pageIndex, int pageSize, string courseCode, string courseName, DateTime? courseDate1, DateTime? courseDate2, string courseLocation, bool? closed, string projectID) { try { // 計算起始索引 (pageIndex 從 1 開始) int startRowIndex = (pageIndex - 1) * pageSize; // 判斷是否所有條件都為空 bool noInput = string.IsNullOrEmpty(courseCode) && string.IsNullOrEmpty(courseName) && !courseDate1.HasValue && !courseDate2.HasValue && string.IsNullOrEmpty(courseLocation); using (var conn = DAC.NewConnection()) { DAC_Classes daoClasses = new DAC_Classes(conn); //DAC_SignUp daoSignUp = new DAC_SignUp(conn); DAC_BPAA daoBpaa = new DAC_BPAA(conn); // 查詢資料 ClassesList result = daoClasses.SelectPage( noInputReturnAll: true, startRowIndex: startRowIndex, maximumRows: pageSize, CourseCode: courseCode, CourseName: courseName, CourseDate_1: courseDate1, CourseDate_2: courseDate2, CourseLocation: courseLocation, Closed: closed, ProjectID: projectID, orderBy: "CourseDate DESC, CourseCode ASC" ); // 取得總筆數 int totalCount = daoClasses.SelectCount( noInputReturnAll: true, CourseCode: courseCode, CourseName: courseName, CourseDate_1: courseDate1, CourseDate_2: courseDate2, CourseLocation: courseLocation, Closed: closed, ProjectID: projectID, orderBy: "" ); foreach (var item in result) { // 填入關聯欄位與統計欄位 item.ProjectName = daoBpaa.SelectProjectName(item.ProjectID); // TODO: 這些等用戶提出再加上 //var thisCourdeSignUps = daoSignUp.Select(false, item.ID, null, null, null, null); //item.Actual = thisCourdeSignUps.Count(x => x.IsPresent); //item.Expected = thisCourdeSignUps.Count(x => !x.IsOnSite); //item.Absent = thisCourdeSignUps.Count(x => !x.IsOnSite && x.IsPresent); //item.Exception = thisCourdeSignUps.Count(x => x.IsException); //item.OnSite = thisCourdeSignUps.Count(x => x.IsOnSite); } return new BaseResponse { Success = true, Message = "查詢成功", Data = result?.ToDict() ?? new ClassesList().ToDict(), TotalCount = totalCount }; } } catch (Exception ex) { return new BaseResponse { Success = false, Message = "查詢活動發生錯誤:" + ex.Message, Data = new ClassesList(), TotalCount = 0 }; } } /// /// 儲存活動 (新增或更正) /// [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public BaseResponse SaveClasses(bool isNew, ClassesItem item) { try { if (item == null) { return new BaseResponse { Success = false, Message = "參數不完整" }; } // 驗證必填欄位 if (string.IsNullOrEmpty(item.CourseName)) { return new BaseResponse { Success = false, Message = "課程名稱為必填欄位" }; } if (item.CourseName.Length > 100) { return new BaseResponse { Success = false, Message = "課程名稱最長 100 個字元" }; } if (item.CourseDate == DateTime.MinValue) { return new BaseResponse { Success = false, Message = "課程日期為必填欄位" }; } if (!string.IsNullOrEmpty(item.NotificationTitle) && item.NotificationTitle.Length > 100) { return new BaseResponse { Success = false, Message = "通知標題最長 100 個字元" }; } if (!string.IsNullOrEmpty(item.NotificationTemplate) && item.NotificationTemplate.Length > 500) { return new BaseResponse { Success = false, Message = "通知模板最長 500 個字元" }; } DAC_Classes dac = new DAC_Classes(); if (isNew) { // 新增 ClassesItem newItem = dac.InsertOne(item); return new BaseResponse { Success = true, Message = "新增成功", Data = newItem }; } else { // 更正 ClassesItem updatedItem = dac.UpdateOne(item); return new BaseResponse { Success = true, Message = "更新成功", Data = updatedItem }; } } catch (Exception ex) { return new BaseResponse { Success = false, Message = "儲存活動發生錯誤:" + ex.Message }; } } /// /// 刪除活動 /// [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public BaseResponse DeleteClasses(int id, int dbAppNo) { try { if (id <= 0) { return new BaseResponse { Success = false, Message = "參數不完整" }; } DAC_Classes dac = new DAC_Classes(); int rowsAffected = dac.DeleteOne(id, dbAppNo); if (rowsAffected > 0) { return new BaseResponse { Success = true, Message = "刪除成功" }; } else { return new BaseResponse { Success = false, Message = "找不到要刪除的資料或資料已被修改" }; } } catch (Exception ex) { return new BaseResponse { Success = false, Message = "刪除活動發生錯誤:" + ex.Message }; } } /// /// 搜尋專案 - 用於自動完成 /// [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public BaseResponse SearchProjects(string keyword) { try { DAC_BPAA dac = new DAC_BPAA(); var result = dac.Select(keyword); return new BaseResponse { Success = true, Message = "搜尋成功", Data = result ?? new NameValueList() }; } catch (Exception ex) { return new BaseResponse { Success = false, Message = "搜尋專案發生錯誤:" + ex.Message, Data = new NameValueList() }; } } /// /// 匯出學員簽到表 - 使用 NPOI 生成 Excel 檔案並回傳 Base64 /// [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public BaseResponse ExportClassesReport(int classesID, string timeFormat = "datetime") { try { if (classesID <= 0) { return new BaseResponse { Success = false, Message = "課程編號為必填欄位" }; } using (var conn = DAC.NewConnection()) { DAC_Classes dacClasses = new DAC_Classes(conn); DAC_SignUp dacSignUp = new DAC_SignUp(conn); DAC_BPAA dacBpaa = new DAC_BPAA(conn); // 查詢課程資訊 ClassesList classes = dacClasses.SelectOne(classesID); if (classes.Count == 0) { return new BaseResponse { Success = false, Message = "找不到課程資訊" }; } ClassesItem classItem = classes[0]; classItem.ProjectName = dacBpaa.SelectProjectName(classItem.ProjectID); // 查詢該課程的參加人員 SignUpList signups = dacSignUp.Select( noInputReturnAll: true, CourseID: classesID, SeqNo: null, Name: null, Mobile: null, QRCode: null, orderBy: "ID ASC" ); var 實到人數 = signups.Count(x => x.IsPresent); var 應到人數 = signups.Count(x => !x.IsOnSite ); var 缺席人數 = signups.Count(x => !x.IsOnSite && !x.IsPresent); var 異常人數 = signups.Count(x => x.IsException); var 現場報名 = signups.Count(x => x.IsOnSite); // 使用 NPOI 建立 Excel 檔案 Excel excel = new Excel(); IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("學員簽到表"); excel.WorkBook = workbook; excel.Sheet = sheet; // 建立字體和樣式 IFont titleFont = excel.NewFont(null, 12, true); IFont headerFont = excel.NewFont(null, 10, true); IFont bodyFont = excel.NewFont(null, 10, false); ICellStyle titleStyle = excel.NewCellStyle(titleFont, HorizontalAlignment.Center, VerticalAlignment.Center, true); ICellStyle headerStyleCenter = excel.NewCellStyle(headerFont, HorizontalAlignment.Center, VerticalAlignment.Center, true); ICellStyle headerStyleLeft = excel.NewCellStyle(headerFont, HorizontalAlignment.Left, VerticalAlignment.Center, true); ICellStyle dataStyle = excel.NewCellStyle(bodyFont, HorizontalAlignment.Left, VerticalAlignment.Center, true); ICellStyle dataStyleCenter = excel.NewCellStyle(bodyFont, HorizontalAlignment.Center, VerticalAlignment.Center, true); ICellStyle dataStyleRight = excel.NewCellStyle(bodyFont, HorizontalAlignment.Right, VerticalAlignment.Center, true); int rowIndex = 0; // 表頭 excel.MergeCell(rowIndex, 0, rowIndex, 10); excel.SetCellValue(rowIndex, 0, classItem.ProjectName, titleStyle); excel.SetRowHeightInPoints(rowIndex, 15); rowIndex++; excel.MergeCell(rowIndex, 0, rowIndex, 10); excel.SetCellValue(rowIndex, 0, "學員簽到表", headerStyleCenter); excel.SetRowHeightInPoints(rowIndex, 15); rowIndex++; // 課程時間 excel.MergeCell(rowIndex, 0, rowIndex, 1); excel.SetCellValue(rowIndex, 0, "課程時間", headerStyleCenter); excel.MergeCell(rowIndex, 2, rowIndex, 10); excel.SetCellValue(rowIndex, 2, string.Format(PublicVariable.TaiwanCulture, "{0:yyyy年MM月dd日} {1}-{2}", classItem.CourseDate, classItem.StartTime, classItem.EndTime), headerStyleLeft); rowIndex++; // 授課講師 excel.MergeCell(rowIndex, 0, rowIndex, 1); excel.SetCellValue(rowIndex, 0, "授課講師", headerStyleCenter); excel.MergeCell(rowIndex, 2, rowIndex, 10); excel.SetCellValue(rowIndex, 2, classItem.Teacher, headerStyleLeft); rowIndex++; // 課程地點 excel.MergeCell(rowIndex, 0, rowIndex, 1); excel.SetCellValue(rowIndex, 0, "課程地點", headerStyleCenter); excel.MergeCell(rowIndex, 2, rowIndex, 10); excel.SetCellValue(rowIndex, 2, classItem.CourseLocation, headerStyleLeft); rowIndex++; // 課程名稱 excel.MergeCell(rowIndex, 0, rowIndex, 1); excel.SetCellValue(rowIndex, 0, "課程名稱", headerStyleCenter); excel.MergeCell(rowIndex, 2, rowIndex, 10); excel.SetCellValue(rowIndex, 2, classItem.CourseName, headerStyleLeft); rowIndex++; // 到課人數 excel.MergeCell(rowIndex, 0, rowIndex, 1); excel.SetCellValue(rowIndex, 0, "到課人數", headerStyleCenter); excel.MergeCell(rowIndex, 2, rowIndex, 10); excel.SetCellValue(rowIndex, 2, string.Format("實到人數:{0} 人 = 應到人數:{1} 人 - 缺席人數:{2} 人 + 現場報名:{3} 人 / (異常人數:{4} 人)", 實到人數, 應到人數, 缺席人數, 現場報名, 異常人數), headerStyleLeft); rowIndex++; // 參加人員表頭 excel.SetCellValue(rowIndex, 0, "序號", headerStyleCenter); excel.SetCellValue(rowIndex, 1, "學員姓名", headerStyleCenter); excel.SetCellValue(rowIndex, 2, "手機號碼", headerStyleCenter); excel.SetCellValue(rowIndex, 3, "身分證字號", headerStyleCenter); excel.SetCellValue(rowIndex, 4, "性別", headerStyleCenter); excel.SetCellValue(rowIndex, 5, "認定次數", headerStyleCenter); excel.SetCellValue(rowIndex, 6, "用餐別", headerStyleCenter); excel.SetCellValue(rowIndex, 7, "報名方式", headerStyleCenter); excel.SetCellValue(rowIndex, 8, "簽到時間", headerStyleCenter); excel.SetCellValue(rowIndex, 9, "簽退時間", headerStyleCenter); excel.SetCellValue(rowIndex, 10, "出席狀況", headerStyleCenter); rowIndex++; // 參加人員資料 foreach (var signup in signups) { excel.SetCellValue(rowIndex, 0, signup.SeqNo, dataStyleCenter); excel.SetCellValue(rowIndex, 1, signup.Name, dataStyleCenter); excel.SetCellValue(rowIndex, 2, signup.Mobile, dataStyleCenter); excel.SetCellValue(rowIndex, 3, string.IsNullOrEmpty(signup.IDNumber) ? "-未填寫-" : signup.IDNumber, dataStyleCenter); excel.SetCellValue(rowIndex, 4, string.IsNullOrEmpty(signup.Gender) ? "-未填寫-" : signup.Gender, dataStyleCenter); excel.SetCellValue(rowIndex, 5, signup.LossJobTimes.HasValue ? signup.LossJobTimes.ToString() : "-未填寫-", dataStyleCenter); excel.SetCellValue(rowIndex, 6, string.IsNullOrEmpty(signup.MealType) ? "-未填寫-" : signup.MealType, dataStyleCenter); excel.SetCellValue(rowIndex, 7, string.IsNullOrEmpty(signup.SignUpType) ? "預約" : signup.SignUpType, dataStyleCenter); excel.SetCellValue(rowIndex, 8, Utils.FormatTimeValue(signup.CheckInTime, timeFormat), dataStyleCenter); excel.SetCellValue(rowIndex, 9, Utils.FormatTimeValue(signup.CheckOutTime, timeFormat), dataStyleCenter); excel.SetCellValue(rowIndex, 10, signup.IsException ? "異常" : (signup.IsPresent ? "" : "缺席"), dataStyleCenter); rowIndex++; } // 設定欄寬 excel.SetColumnWidthInChars(0, 6); excel.SetColumnWidthInChars(1, 15); excel.SetColumnWidthInChars(2, 15); excel.SetColumnWidthInChars(3, 15); excel.SetColumnWidthInChars(4, 8); excel.SetColumnWidthInChars(5, 8); excel.SetColumnWidthInChars(6, 8); excel.SetColumnWidthInChars(7, 10); excel.SetColumnWidthInChars(8, timeFormat == "datetime" ? 20 : 10); excel.SetColumnWidthInChars(9, timeFormat == "datetime" ? 20 : 10); excel.SetColumnWidthInChars(10, 8); // 儲存到記憶體流並轉換為 Base64 MemoryStream ms = new MemoryStream(); workbook.Write(ms); byte[] fileBytes = ms.ToArray(); string fileBase64 = Convert.ToBase64String(fileBytes); string fileName = $"{classItem.CourseName}_{DateTime.Now:yyyyMMdd}.xlsx"; return new BaseResponse { Success = true, Message = "匯出成功", Data = new { FileName = fileName, FileBase64 = fileBase64, FileSize = fileBytes.Length, DataCount = signups.Count } }; } } catch (Exception ex) { return new BaseResponse { Success = false, Message = "匯出課程報表發生錯誤:" + ex.Message }; } } /// /// 匯出課程清單 - 使用 NPOI 生成 Excel 檔案並回傳 Base64 /// [WebMethod(EnableSession = true)] [ScriptMethod(ResponseFormat = ResponseFormat.Json)] public BaseResponse ExportClassesList(DateTime? courseDate1, DateTime? courseDate2, string projectID) { try { // 判斷是否所有條件都為空 bool noInput = !courseDate1.HasValue && !courseDate2.HasValue && string.IsNullOrEmpty(projectID); using (var conn = DAC.NewConnection()) { DAC_Classes dac = new DAC_Classes(conn); DAC_BPAA dacBpaa = new DAC_BPAA(conn); DAC_SignUp dacSignUp = new DAC_SignUp(conn); var projectName = !string.IsNullOrEmpty(projectID) ? dacBpaa.SelectProjectName(projectID) : "全部"; // 查詢課程資料 ClassesList result = dac.SelectPage( noInputReturnAll: true, startRowIndex: 0, maximumRows: Int32.MaxValue, CourseCode: null, CourseName: null, CourseDate_1: courseDate1, CourseDate_2: courseDate2, CourseLocation: null, Closed: null, ProjectID: projectID, orderBy: "ProjectID ASC, CourseDate DESC" ); if (result.Count == 0) { return new BaseResponse { Success = false, Message = "沒有符合條件的資料", Data = null }; } var reportData = new List(); foreach (var item in result) { // 查詢課程的參加人員 SignUpList signups = dacSignUp.Select( noInputReturnAll: true, CourseID: item.ID, SeqNo: null, Name: null, Mobile: null, QRCode: null ); reportData.Add(new ClassesReportItem { CourseName = item.CourseName, CourseLocation = item.CourseLocation, Teacher = item.Teacher, 實到人數 = signups.Count(x => x.IsPresent), 應到人數 = signups.Count(x => x.SignUpType != "現場"), 缺席人數 = signups.Count(x => x.SignUpType != "現場" && !x.IsPresent), 現場報名 = signups.Count(x => x.SignUpType == "現場") }); } // 使用 NPOI 建立 Excel 檔案 Excel excel = new Excel(); IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("課程匯出"); excel.WorkBook = workbook; excel.Sheet = sheet; // 建立字體和樣式 IFont titleFont = excel.NewFont(null, 12, true); IFont headerFont = excel.NewFont(null, 10, true); IFont bodyFont = excel.NewFont(null, 10, false); ICellStyle titleStyle = excel.NewCellStyle(titleFont, HorizontalAlignment.Center, VerticalAlignment.Center, true); ICellStyle headerStyleCenter = excel.NewCellStyle(headerFont, HorizontalAlignment.Center, VerticalAlignment.Center, true); ICellStyle headerStyleLeft = excel.NewCellStyle(headerFont, HorizontalAlignment.Left, VerticalAlignment.Center, true); ICellStyle dataStyle = excel.NewCellStyle(bodyFont, HorizontalAlignment.Left, VerticalAlignment.Center, true); ICellStyle dataStyleCenter = excel.NewCellStyle(bodyFont, HorizontalAlignment.Center, VerticalAlignment.Center, true); ICellStyle dataStyleRight = excel.NewCellStyle(bodyFont, HorizontalAlignment.Right, VerticalAlignment.Center, true); int rowIndex = 0; // 表頭 excel.MergeCell(rowIndex, 0, rowIndex, 6); excel.SetCellValue(rowIndex, 0, "課程匯出", headerStyleCenter); excel.SetRowHeightInPoints(rowIndex, 15); rowIndex++; // 計畫 excel.SetCellValue(rowIndex, 0, "計畫", headerStyleCenter); excel.MergeCell(rowIndex, 1, rowIndex, 6); excel.SetCellValue(rowIndex, 1, projectName, headerStyleLeft); rowIndex++; // 課程日期 excel.SetCellValue(rowIndex, 0, "課程日期", headerStyleCenter); excel.MergeCell(rowIndex, 1, rowIndex, 6); excel.SetCellValue(rowIndex, 1, string.Format("{0:yyyy-MM-dd} - {1:yyyy-MM-dd}", courseDate1, courseDate2), headerStyleLeft); rowIndex++; // 參加人員表頭 excel.SetCellValue(rowIndex, 0, "課程名稱", headerStyleCenter); excel.SetCellValue(rowIndex, 1, "課程地點", headerStyleCenter); excel.SetCellValue(rowIndex, 2, "授課講師", headerStyleCenter); excel.SetCellValue(rowIndex, 3, "實到人數", headerStyleCenter); excel.SetCellValue(rowIndex, 4, "應到人數", headerStyleCenter); excel.SetCellValue(rowIndex, 5, "缺席人數", headerStyleCenter); excel.SetCellValue(rowIndex, 6, "現場報名", headerStyleCenter); rowIndex++; // 參加人員資料 foreach (var reportItem in reportData) { excel.SetCellValue(rowIndex, 0, reportItem.CourseName, dataStyleCenter); excel.SetCellValue(rowIndex, 1, reportItem.CourseLocation, dataStyleCenter); excel.SetCellValue(rowIndex, 2, reportItem.Teacher, dataStyleCenter); excel.SetCellValue(rowIndex, 3, reportItem.實到人數, dataStyleCenter); excel.SetCellValue(rowIndex, 4, reportItem.應到人數, dataStyleCenter); excel.SetCellValue(rowIndex, 5, reportItem.缺席人數, dataStyleCenter); excel.SetCellValue(rowIndex, 6, reportItem.現場報名, dataStyleCenter); rowIndex++; } // 設定欄寬 excel.SetColumnWidthInChars(0, 40); excel.SetColumnWidthInChars(1, 40); excel.SetColumnWidthInChars(2, 20); excel.SetColumnWidthInChars(3, 12); excel.SetColumnWidthInChars(4, 12); excel.SetColumnWidthInChars(5, 12); excel.SetColumnWidthInChars(6, 12); // 儲存到記憶體流並轉換為 Base64 MemoryStream ms = new MemoryStream(); workbook.Write(ms); byte[] fileBytes = ms.ToArray(); string fileBase64 = Convert.ToBase64String(fileBytes); string fileName = $"課程匯出_{DateTime.Now:yyyyMMdd}.xlsx"; return new BaseResponse { Success = true, Message = "匯出成功", Data = new { FileName = fileName, FileBase64 = fileBase64, FileSize = fileBytes.Length } }; } } catch (Exception ex) { return new BaseResponse { Success = false, Message = "匯出課程報表發生錯誤:" + ex.Message }; } } /// /// 課程報表項目 /// private class ClassesReportItem { public string CourseName { get; set; } public string CourseLocation { get; set; } public string Teacher { get; set; } public int 實到人數 { get; set; } public int 應到人數 { get; set; } public int 缺席人數 { get; set; } public int 現場報名 { get; set; } } } }