Files
thinkyu/報到系統客戶端/MainForm.cs
T
sryang 577060bc78 chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore
- 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
2026-09-10 09:42:37 +08:00

1718 lines
62 KiB
C#

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using 報到系統客戶端.Data;
using 報到系統客戶端.Data.Models;
using 報到系統客戶端.Data.Repositories;
using 報到系統客戶端.Models;
using 報到系統客戶端.Services;
namespace 報到系統客戶端
{
public partial class MainForm : Form
{
private string systemName = "新譽管理課程報到系統";
/// <summary>
/// 是否作業中
/// </summary>
private bool checkinOperating = false;
private Color enlabedColor = Color.White;
private Color disabledColor = Color.LightGray;
private Color focusedColor = Color.FromArgb(255, 255, 200);
private ProcessingForm processingForm = new ProcessingForm();
public MainForm()
{
InitializeComponent();
Text = string.Format("{0} ({1})", systemName, ClientSession.Version);
cbCheckMode.SelectedIndex = 0;
}
private void SetupTextBoxFocusEffects()
{
// 定義輸入框列表
Control[] controls = { cbCurrentClass, cbCheckMode, txtQRCode, txtName, txtMobile, txtApiBaseUrl, txtIDNumber, txtEMail, cbIDType, cbMealType, cbGender, txtLossJobTimes };
Control[] menualCheckInControls = { txtName, txtMobile, txtIDNumber, txtEMail, cbIDType, cbMealType, cbGender, txtLossJobTimes };
foreach (Control control in controls)
{
control.Enter += (sender, e) => Control_Enter(sender, e);
control.Leave += (sender, e) => Control_Leave(sender, e);
if (control is System.Windows.Forms.ComboBox)
{
(control as System.Windows.Forms.ComboBox).DrawItem += comboBox_DrawItem;
(control as System.Windows.Forms.ComboBox).MouseDown += comboBox_MouseDown;
}
}
foreach (Control control in menualCheckInControls)
{
control.KeyPress += (sender, e) =>
{
if (e.KeyChar == '\r' && checkinOperating)
{
ProcessManualCheckIn();
}
};
}
}
private void Control_Enter(object sender, EventArgs e)
{
Control control = sender as Control;
if (control != null)
{
// Focus 時:淺黃色背景
control.BackColor = focusedColor;
control.ForeColor = Color.Black;
// 展開 ComboBox
if (control is System.Windows.Forms.ComboBox)
{
(control as System.Windows.Forms.ComboBox).DroppedDown = true;
}
}
}
private void Control_Leave(object sender, EventArgs e)
{
Control control = sender as Control;
if (control != null)
{
// 失焦時:白色背景
control.BackColor = enlabedColor;
control.ForeColor = Color.Black;
}
}
private void comboBox_MouseDown(object sender, EventArgs e)
{
System.Windows.Forms.ComboBox cb = (System.Windows.Forms.ComboBox)sender;
cb.DroppedDown = true;
}
private void comboBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
var combobox = (sender as System.Windows.Forms.ComboBox);
bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
bool disabled = (e.State & DrawItemState.Disabled) == DrawItemState.Disabled;
Color backColor;
Color foreColor;
if (disabled)
{
backColor = disabledColor;
foreColor = SystemColors.GrayText;
}
else if (selected)
{
backColor = SystemColors.Highlight;
foreColor = SystemColors.HighlightText;
}
else
{
backColor = focusedColor;
foreColor = SystemColors.ControlText;
}
using (SolidBrush b = new SolidBrush(backColor))
e.Graphics.FillRectangle(b, e.Bounds);
string text = combobox.Items[e.Index].ToString();
if (combobox.DisplayMember != null)
{
var displayMemberProp = combobox.Items[e.Index].GetType().GetProperty(combobox.DisplayMember);
if (displayMemberProp != null)
{
text = displayMemberProp.GetValue(combobox.Items[e.Index]).ToString();
}
}
TextRenderer.DrawText(
e.Graphics,
text,
e.Font,
e.Bounds,
foreColor,
TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
e.DrawFocusRectangle();
}
private void UpdateSessionDisplay()
{
lbLoginUser.Text = string.Format("{0} {1}", ClientSession.UserID, ClientSession.UserName);
lbOnlineMode1.Text = ClientSession.OnlineMode ? "連線" : "離線";
lbOnlineMode2.Text = ClientSession.OnlineMode ? "連線" : "離線";
lbLocalDB.Text = DatabaseService.Instance.DatabasePath;
string apiUrl = ConfigService.Instance.GetApiBaseUrl();
txtApiBaseUrl.Text = apiUrl;
lbApiBaseUrl.Text = apiUrl;
btnDownload.Enabled = ClientSession.OnlineMode;
btnUpload.Enabled = ClientSession.OnlineMode;
btnRefreshClasses.Enabled = ClientSession.OnlineMode;
}
private void ClientSession_OnModeChanged(object sender, ModeChangeEventArgs e)
{
// 更新畫面
UpdateSessionDisplay();
// 連線模式時,定時上傳數據
if (e.OnlineMode)
{
// 啟動同步 Timer
Processing = true;
Application.DoEvents();
downloadWorker.RunWorkerAsync();
uploadTimer.Enabled = true;
}
else
{
LoadClassesData();
uploadTimer.Enabled = false;
}
}
private void MainForm_Load(object sender, EventArgs e)
{
ClientSession.OnModeChange += ClientSession_OnModeChanged;
ClientSession.ShowLoginForm();
// 偵測是否需要修復資料庫
if (!CheckLocalDB(false))
{
if (MessageBox.Show("檢查到本地資料庫需要修復,是否現在修復?", "警告",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button1) == DialogResult.Yes)
{
var repairResult = DatabaseService.Instance.AutoRepairDatabase();
if (repairResult.IsSuccessful)
{
MessageBox.Show("資料庫修復完成");
}
else
{
MessageBox.Show("資料庫修復失敗,請至「系統設定」頁籤執行重建資料庫");
}
}
}
// 設定 tabControl1 的樣式
tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
tabControl1.DrawItem += TabControl1_DrawItem;
// 綁定 cbClasses 的選擇變更事件
cbClasses.SelectedIndexChanged += CbClasses_SelectedIndexChanged;
// 綁定下載和上傳按鈕事件
btnDownload.Click += BtnDownload_Click;
btnUpload.Click += BtnUpload_Click;
// 綁定 txtQRCode KeyDown 事件(預約報名簽到簽退)
txtQRCode.KeyDown += TxtQRCode_KeyDown;
// 設定輸入框 focus 效果
SetupTextBoxFocusEffects();
// 清空結果顯示
ClearCheckResultDisplay();
}
private void TabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
TabPage page = tabControl1.TabPages[e.Index];
// 設定背景色 (根據分頁索引選擇相應的顏色)
Color tabBackColor;
if (e.Index == tabControl1.SelectedIndex)
{
// 選中時使用比分頁底色深一點的顏色
switch (e.Index)
{
case 0: // tabPage1 - 淺橘色 -> 深橘色
tabBackColor = Color.FromArgb(255, 200, 124);
break;
case 1: // tabPage2 - 淺青色 -> 深青色
tabBackColor = Color.FromArgb(150, 220, 220);
break;
case 2: // tabPage3 - 淺灰色 -> 深灰色
tabBackColor = Color.FromArgb(190, 190, 190);
break;
default:
tabBackColor = Color.FromArgb(255, 220, 150);
break;
}
}
else
{
tabBackColor = SystemColors.Control;
}
e.Graphics.FillRectangle(new SolidBrush(tabBackColor), e.Bounds);
// 設定字體 (粗體)
Font tabFont;
if (e.Index == tabControl1.SelectedIndex)
{
tabFont = new Font(tabControl1.Font, FontStyle.Bold);
}
else
{
tabFont = new Font(tabControl1.Font, FontStyle.Regular);
}
// 繪製文本
TextRenderer.DrawText(
e.Graphics,
page.Text,
tabFont,
e.Bounds,
Color.Black,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPrefix
);
}
private void btnSaveApiBaseUrl_Click(object sender, EventArgs e)
{
bool success = ConfigService.Instance.SetApiBaseUrl(txtApiBaseUrl.Text.Trim());
if (success)
{
Console.WriteLine("API URL 已更新");
// 更新 ApiService 的 BaseUrl
ApiService.Instance.BaseUrl = txtApiBaseUrl.Text.Trim();
// 更新畫面顯示
UpdateSessionDisplay();
}
else
{
Console.WriteLine("API URL 更新失敗");
}
}
private void btnTestAPIConnection_Click(object sender, EventArgs e)
{
if (ClientSession.Ping())
{
MessageBox.Show("連線測試成功");
}
else
{
MessageBox.Show("連線測試失敗");
}
}
/// <summary>
/// 載入活動清單資料
/// </summary>
private void LoadClassesData()
{
try
{
ClassesRepository classes = new ClassesRepository();
var classList1 = classes.GetAll();
var classList2 = classes.GetAll();
if (classList1 != null && classList1.Count > 0)
{
// 綁定到 ComboBox
cbClasses.DisplayMember = "DisplayText"; // 顯示課程名稱
cbClasses.ValueMember = "ID"; // 值為課程 ID
cbClasses.DataSource = classList1;
CbClasses_SelectedIndexChanged(cbClasses, new EventArgs());
cbCurrentClass.DisplayMember = "DisplayText"; // 顯示課程名稱
cbCurrentClass.ValueMember = "ID"; // 值為課程 ID
cbCurrentClass.DataSource = classList2;
}
else
{
cbClasses.DataSource = null;
cbClasses.Items.Clear();
cbCurrentClass.DataSource = null;
cbCurrentClass.Items.Clear();
}
}
catch (Exception ex)
{
MessageBox.Show("載入課程清單時發生錯誤: " + ex.Message);
cbClasses.DataSource = null;
cbClasses.Items.Clear();
}
}
/// <summary>
/// 當選擇活動時,取得相應統計資料
/// </summary>
private void CbClasses_SelectedIndexChanged(object sender, EventArgs e)
{
if (cbClasses.SelectedValue == null)
{
// 清空統計資訊
lbServerCount.Text = "0";
lbLocalCount.Text = "0";
lbWaitingUploadCount.Text = "0";
lbCheckInCount.Text = "0";
lbCheckOutCount.Text = "0";
return;
}
int courseID = (int)cbClasses.SelectedValue;
try
{
// 1. 取得後端報名人數
if (ClientSession.OnlineMode)
{
int serverCount = GetServerSignUpCount(courseID);
lbServerCount.Text = serverCount.ToString();
}
else
{
lbServerCount.Text = "(離線)";
}
// 2. 取得本地已下載人數
int localCount = GetLocalSignUpCount(courseID);
lbLocalCount.Text = localCount.ToString();
// 3. 取得資料未上傳筆數(本地有簽到簽退記錄但後端沒有更新的筆數)
int waitingUploadCount = GetWaitingUploadCount(courseID);
lbWaitingUploadCount.Text = waitingUploadCount.ToString();
// 4. 取得本地資料顯示出來
SignUpsRepository signUps = new SignUpsRepository();
var data = signUps.GetByCourseID(courseID);
dgSignUp.DataSource = null;
bsSignUp.DataSource = null;
bsSignUp.DataSource = data;
dgSignUp.DataSource = bsSignUp;
// 5.統計人數
lbCheckInCount.Text = string.Format("{0}", data.Count(p => !string.IsNullOrWhiteSpace(p.CheckInTime)));
lbCheckOutCount.Text = string.Format("{0}", data.Count(p => !string.IsNullOrWhiteSpace(p.CheckOutTime)));
}
catch (Exception ex)
{
MessageBox.Show("取得統計資料時發生錯誤: " + ex.Message);
}
}
/// <summary>
/// 取得後端報名人數
/// </summary>
private int GetServerSignUpCount(int courseID)
{
try
{
// 呼叫後端 API 取得該活動的報名人數
BaseResponse response = SignUpService.SearchSignUps(
pageIndex: 1,
pageSize: 1,
courseID: courseID,
name: "",
mobile: ""
);
if (response.Success)
{
return response.TotalCount;
}
else
{
return 0;
}
}
catch (Exception)
{
return 0;
}
}
/// <summary>
/// 取得本地已下載人數
/// </summary>
private int GetLocalSignUpCount(int courseID)
{
try
{
// 從本地資料庫取得該活動的參加人員數
SignUpsRepository repo = new SignUpsRepository();
return repo.GetCount(courseID: courseID);
}
catch (Exception)
{
return 0;
}
}
/// <summary>
/// 取得資料未上傳筆數
/// </summary>
private int GetWaitingUploadCount(int courseID)
{
try
{
CheckInRepository repo = new CheckInRepository();
return repo.GetByCourseID(courseID).Where(p => !p.UploadTime.HasValue).Count();
}
catch (Exception)
{
return 0;
}
}
/// <summary>
/// 下載活動參加人員資料
/// </summary>
private void BtnDownload_Click(object sender, EventArgs e)
{
if (cbClasses.SelectedValue == null)
{
MessageBox.Show("請先選擇課程", "提示");
return;
}
int courseID = (int)cbClasses.SelectedValue;
try
{
CheckInRepository checkinRepo = new CheckInRepository();
SignUpsRepository signupRepo = new SignUpsRepository();
// 檢查是否有未上傳報到紀錄
if (checkinRepo.GetByCourseID(courseID).Where(p => !p.UploadTime.HasValue).Count() > 0)
{
MessageBox.Show("有未上傳報到紀錄,請先上傳", "提示");
return;
}
// 禁用按鈕防止重複點擊
btnDownload.Enabled = false;
btnDownload.Text = "下載中...";
Processing = true;
Application.DoEvents();
// 清除本地該活動所有參加人員以及報到紀錄
signupRepo.DeleteByCourseID(courseID);
checkinRepo.DeleteByCourseID(courseID);
// 呼叫 API 取得該活動的所有參加人員(分頁取得)
int downloadedCount = 0;
int pageIndex = 1;
const int pageSize = 100;
while (true)
{
BaseResponse response = SignUpService.SearchSignUps(
pageIndex: pageIndex,
pageSize: pageSize,
courseID: courseID,
name: "",
mobile: ""
);
if (!response.Success)
{
MessageBox.Show("下載失敗: " + response.Message, "錯誤");
return;
}
if (response.Data == null || response.Data.ToString() == "[]")
{
break;
}
// 轉換資料並保存到本地資料庫
SignUpList signUpList = ApiService.DeserializeObject<SignUpList>(response.Data.ToString());
foreach (var item in signUpList)
{
// 轉換為本地模型
LocalSignUpItem localItem = LocalSignUpItem.ConvertFromApiModel(item);
// 檢查是否已存在,存在則更新,不存在則新增
var existingItem = signupRepo.GetByCourseNameMobile(courseID, item.Name, item.Mobile);
if (existingItem != null)
{
localItem.ID = existingItem.ID;
signupRepo.Update(localItem);
}
else
{
signupRepo.Insert(localItem);
}
downloadedCount++;
}
// 如果取回的筆數小於 pageSize,表示已取完所有資料
if (signUpList.Count < pageSize)
{
break;
}
pageIndex++;
}
// 重新更新統計資訊
CbClasses_SelectedIndexChanged(null, null);
MessageBox.Show($"下載成功,共下載 {downloadedCount} 筆資料", "成功");
}
catch (Exception ex)
{
MessageBox.Show("下載時發生錯誤: " + ex.Message, "錯誤");
}
finally
{
Processing = false;
btnDownload.Enabled = true;
btnDownload.Text = "下載課程人員";
}
}
/// <summary>
/// 上傳本地簽到簽退資料
/// </summary>
private void BtnUpload_Click(object sender, EventArgs e)
{
if (cbClasses.SelectedValue == null)
{
MessageBox.Show("請先選擇課程", "提示");
return;
}
int courseID = (int)cbClasses.SelectedValue;
try
{
// 禁用按鈕防止重複點擊
btnUpload.Enabled = false;
btnUpload.Text = "上傳中...";
Processing = true;
Application.DoEvents();
// 呼叫上傳
ProcessUpload(courseID, true);
}
catch (Exception ex)
{
MessageBox.Show("上傳時發生錯誤: " + ex.Message, "錯誤");
}
finally
{
Processing = false;
btnUpload.Enabled = true;
btnUpload.Text = "上傳報到資料";
}
}
/// <summary>
/// 重新下載員工資料表
/// </summary>
private void DownloadBPSNData()
{
try
{
// 呼叫 BPSNService 下載員工資料
BaseResponse response = BPSNService.Download();
if (response.Success && response.Data != null)
{
// 轉換資料為 BPSNList
BPSNList bpsnList = ApiService.DeserializeObject<BPSNList>(response.Data.ToString());
if (bpsnList != null && bpsnList.Count > 0)
{
// 建立本地資料庫 Repository
BPSNRepository repo = new BPSNRepository();
// 清除現有資料
repo.DeleteAll();
// 新增下載的員工資料
int addedCount = 0;
foreach (var item in bpsnList)
{
repo.Insert(new LocalBPSNItem()
{
BPENO = item.BPENO,
BPNME = item.BPNME,
BPAWD = item.BPAWD,
Is系統管理群組 = item.Is系統管理群組 == 1,
Is客戶管理群組 = item.Is客戶管理群組 == 1,
Is現場工作群組 = item.Is現場工作群組 == 1,
Is查詢群組 = item.Is查詢群組 == 1
});
addedCount++;
}
Console.WriteLine($"員工資料重新載入成功,共 {addedCount} 筆");
}
else
{
MessageBox.Show("未取得任何員工資料", "提示");
}
}
else
{
MessageBox.Show("重新載入員工資料失敗: " + (response.Message ?? "未知錯誤"), "錯誤");
}
}
catch (Exception ex)
{
MessageBox.Show("重新載入員工資料時發生錯誤: " + ex.Message, "錯誤");
}
}
private void btnBeginEnd_Click(object sender, EventArgs e)
{
if (cbCurrentClass.Enabled)
{
checkinOperating = true;
btnBeginEnd.Text = "停止作業";
}
else
{
checkinOperating = false;
btnBeginEnd.Text = "開始作業";
}
SetInputControlEnabled();
txtQRCode.Focus();
}
private void SetInputControlEnabled()
{
string checkMode = cbCheckMode.SelectedItem?.ToString() ?? "簽到";
bool isCheckIn = checkMode == "簽到";
cbCurrentClass.Enabled = !checkinOperating;
cbCheckMode.Enabled = !checkinOperating;
txtQRCode.Enabled = checkinOperating;
btnMenualCheckIn.Enabled = checkinOperating;
txtName.Enabled = checkinOperating;
txtMobile.Enabled = checkinOperating;
txtIDNumber.Enabled = checkinOperating && isCheckIn;
txtEMail.Enabled = checkinOperating && isCheckIn;
cbIDType.Enabled = checkinOperating && isCheckIn;
cbMealType.Enabled = checkinOperating && isCheckIn;
cbGender.Enabled = checkinOperating && isCheckIn;
txtLossJobTimes.Enabled = checkinOperating && isCheckIn;
cbCurrentClass.BackColor = cbCurrentClass.Enabled ? enlabedColor : disabledColor;
cbCheckMode.BackColor = cbCheckMode.Enabled ? enlabedColor : disabledColor;
txtQRCode.BackColor = txtQRCode.Enabled ? enlabedColor : disabledColor;
txtName.BackColor = txtName.Enabled ? enlabedColor : disabledColor;
txtMobile.BackColor = txtMobile.Enabled ? enlabedColor : disabledColor;
txtIDNumber.BackColor = txtIDNumber.Enabled ? enlabedColor : disabledColor;
txtEMail.BackColor = txtEMail.Enabled ? enlabedColor : disabledColor;
cbIDType.BackColor = cbIDType.Enabled ? enlabedColor : disabledColor;
cbMealType.BackColor = cbMealType.Enabled ? enlabedColor : disabledColor;
cbGender.BackColor = cbGender.Enabled ? enlabedColor : disabledColor;
txtLossJobTimes.BackColor = txtLossJobTimes.Enabled ? enlabedColor : disabledColor;
}
/// <summary>
/// 預約報名 - QRCode 簽到簽退事件處理器
/// 當掃描器掃入 QRCode 時觸發
/// </summary>
private void TxtQRCode_KeyDown(object sender, KeyEventArgs e)
{
// Enter 鍵觸發簽到簽退
if (e.KeyCode == Keys.Return)
{
e.Handled = true;
ProcessScanCheckIn();
}
}
/// <summary>
/// 處理 QRCode 簽到簽退邏輯
/// </summary>
private void ProcessScanCheckIn()
{
try
{
// 先清空結果顯示
ClearCheckResultDisplay();
string qrCode = txtQRCode.Text.Trim();
// 驗證 QRCode 不為空
if (string.IsNullOrWhiteSpace(qrCode))
{
DisplayScanCheckResult(false, "請掃碼");
return;
}
// 取得當前課程 ID
if (cbCurrentClass.SelectedValue == null)
{
DisplayScanCheckResult(false, "請先選擇課程");
return;
}
int courseID = (int)cbCurrentClass.SelectedValue;
string courseAgreedTerms = (cbCurrentClass.SelectedItem as LocalClassesItem)?.AgreedTerms;
// 取得簽到簽退模式
string checkMode = cbCheckMode.SelectedItem?.ToString() ?? "簽到";
bool isCheckIn = checkMode == "簽到";
// 查詢本地資料庫
SignUpsRepository repo = new SignUpsRepository();
LocalSignUpItem signUpItem = repo.GetByQRCode(qrCode);
if (signUpItem == null)
{
// 未查到
DisplayScanCheckResult(false, "無此筆資料");
txtQRCode.Text = string.Empty;
return;
}
// 查到記錄,核對是否為本場次
if (signUpItem.CourseID != courseID)
{
DisplayScanCheckResult(false, "非本課程報到碼");
txtQRCode.Text = string.Empty;
return;
}
var now = DateTime.Now;
var nowStr = now.ToString("yyyy-MM-dd HH:mm:ss");
bool needUpdate = false;
// 根據簽到簽退模式更新資料
if (isCheckIn)
{
// 簽到:只在 CheckInTime 為 NULL 時才寫入
if (string.IsNullOrWhiteSpace(signUpItem.CheckInTime))
{
// TODO:此處檢查是否有同意確認事項,若有則要跳出確認視窗
if (!string.IsNullOrWhiteSpace(courseAgreedTerms))
{
signUpItem.AgreementStatus = ShowAgreementConfirmDialog(courseAgreedTerms);
}
signUpItem.CheckInTime = nowStr;
needUpdate = true;
}
}
else
{
// 檢查是否沒有簽到而要簽退
if (string.IsNullOrWhiteSpace(signUpItem.CheckInTime))
{
using (var checkoutOnlyForm = new CheckoutOnlyConfirmForm())
{
checkoutOnlyForm.姓名 = signUpItem.Name;
checkoutOnlyForm.手機號 = signUpItem.Mobile;
checkoutOnlyForm.Top = 0;
checkoutOnlyForm.Left = (this.Width - checkoutOnlyForm.Width) / 2;
if (checkoutOnlyForm.ShowDialog() == DialogResult.No)
{
ClearInputsAndFocus();
return;
}
}
}
// 簽退:只在 CheckOutTime 為 NULL 時才寫入
if (string.IsNullOrWhiteSpace(signUpItem.CheckOutTime))
{
signUpItem.CheckOutTime = nowStr;
needUpdate = true;
}
}
// 更新 SignUps 表
// 新增 CheckIn 紀錄
if (needUpdate)
{
repo.Update(signUpItem);
CheckInRepository checkInRepo = new CheckInRepository();
LocalCheckInItem checkInItem = new LocalCheckInItem
{
CourseID = signUpItem.CourseID,
Name = signUpItem.Name,
Mobile = signUpItem.Mobile,
SignUpType = signUpItem.SignUpType,
AgreementStatus = signUpItem.AgreementStatus,
CheckInType = isCheckIn ? "CHECKIN" : "CHECKOUT",
CheckInTime = nowStr,
CreatedAt = now,
UpdatedAt = now
};
checkInRepo.Insert(checkInItem);
}
// 顯示成功結果
DisplayScanCheckResult(true, needUpdate ? $"{cbCheckMode.Text}成功" : $"已{cbCheckMode.Text}");
DisplaySignUpInfo(signUpItem);
// 播放音效
if (isCheckIn)
Services.NotificationSoundService.CheckinOK();
else
Services.NotificationSoundService.CheckoutOK();
// 清空 QRCode 輸入框,準備下一次掃碼
txtQRCode.Clear();
txtQRCode.Focus();
}
catch (Exception ex)
{
// 異常處理:顯示錯誤訊息,不清空 UI
DisplayScanCheckResult(false, $"處理簽到簽退時發生錯誤:{ex.Message}");
Console.WriteLine($"QRCode 簽到簽退異常:{ex.Message}");
}
}
/// <summary>
/// 顯示簽到簽退結果(成功/失敗)
/// </summary>
private void DisplayScanCheckResult(bool success, string message)
{
if (success)
{
// 綠底白字 - 成功
lbCheckResult.BackColor = Color.Green;
lbCheckResult.ForeColor = Color.White;
}
else
{
// 紅底白字 - 失敗
lbCheckResult.BackColor = Color.Red;
lbCheckResult.ForeColor = Color.White;
}
lbCheckResult.Text = message;
}
/// <summary>
/// 顯示報名人員資訊
/// </summary>
private void DisplaySignUpInfo(LocalSignUpItem item)
{
if (item == null)
{
ClearCheckResultDisplay();
return;
}
lbSeqNo.Text = item.SeqNo.ToString();
lbName.Text = item.Name ?? string.Empty;
lbMobile.Text = Utils.HideMobile(item.Mobile ?? string.Empty);
lbCheckInTime.Text = item.CheckInTime;
lbCheckOutTime.Text = item.CheckOutTime;
}
/// <summary>
/// 清空簽到簽退顯示資訊
/// 這裡不處理輸入控制項的清空
/// </summary>
private void ClearCheckResultDisplay()
{
lbCheckResult.Text = string.Empty;
lbSeqNo.Text = string.Empty;
lbName.Text = string.Empty;
lbMobile.Text = string.Empty;
lbCheckInTime.Text = string.Empty;
lbCheckOutTime.Text = string.Empty;
lbName2.Text = string.Empty;
lbMobile2.Text = string.Empty;
lbMenualCheckResult.Text = string.Empty;
lbMenualCheckInTime.Text = string.Empty;
lbMenualCheckOutTime.Text = string.Empty;
}
private void downloadWorker_DoWork(object sender, DoWorkEventArgs e)
{
downloadWorker.ReportProgress(0, "下載員工資料...");
DownloadBPSNData();
downloadWorker.ReportProgress(0, "下載課程資料...");
DownloadClassesData();
}
private void downloadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lbSyncJob.Text = e.UserState.ToString();
Application.DoEvents();
}
private void downloadWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lbSyncJob.Text = "(無)";
LoadClassesData();
Processing = false;
}
private void btnRefreshSignUp_Click(object sender, EventArgs e)
{
CbClasses_SelectedIndexChanged(sender, e);
}
private void btnMenualCheckIn_Click(object sender, EventArgs e)
{
ProcessManualCheckIn();
}
/// <summary>
/// 顯示簽到簽退結果(成功/失敗)
/// </summary>
private void DisplayMenualCheckResult(bool success, string message, LocalSignUpItem signUpItem)
{
if (success)
{
// 綠底白字 - 成功
lbMenualCheckResult.BackColor = Color.Green;
lbMenualCheckResult.ForeColor = Color.White;
}
else
{
// 紅底白字 - 失敗
lbMenualCheckResult.BackColor = Color.Red;
lbMenualCheckResult.ForeColor = Color.White;
}
lbMenualCheckResult.Text = message;
lbName2.Text = signUpItem?.Name;
lbMobile2.Text = Utils.HideMobile(signUpItem?.Mobile);
txtMobile.Text = lbMobile2.Text;
lbMenualCheckInTime.Text = !string.IsNullOrWhiteSpace(signUpItem?.CheckInTime)
? ExtractTimeFromDateTime(signUpItem?.CheckInTime)
: string.Empty;
lbMenualCheckOutTime.Text = !string.IsNullOrWhiteSpace(signUpItem?.CheckOutTime)
? ExtractTimeFromDateTime(signUpItem?.CheckOutTime)
: string.Empty;
}
/// <summary>
/// 處理現場報到邏輯
/// 支持簽到和簽退
/// </summary>
private void ProcessManualCheckIn()
{
try
{
// 先清空結果顯示
ClearCheckResultDisplay();
// 驗證課程選擇
if (cbCurrentClass.SelectedValue == null)
{
DisplayMenualCheckResult(false, "請先選擇課程", null);
cbCurrentClass.Focus();
return;
}
int courseID = (int)cbCurrentClass.SelectedValue;
string courseAgreedTerms = (cbCurrentClass.SelectedItem as LocalClassesItem)?.AgreedTerms;
// 驗證輸入資料
if (!ValidateInputs(out string name, out string mobile, out string idNumber, out string email, out int lossJobTimes))
{
return;
}
// 取得簽到簽退模式
bool isCheckIn = cbCheckMode.SelectedItem?.ToString() == "簽到";
DateTime now = DateTime.Now;
var nowStr = now.ToString("yyyy-MM-dd HH:mm:ss");
// 查詢或建立簽到記錄
SignUpsRepository repo = new SignUpsRepository();
LocalSignUpItem signUpItem = repo.GetByCourseNameMobile(courseID, name, mobile);
bool isNewRecord = signUpItem == null;
bool foundByNameOrMobile = false;
// 簽到作業時,若姓名 + 手機號查不到,用姓名查再用手機號查
if (isNewRecord)
{
signUpItem = repo.GetByCourseNameMobile(courseID, name, null);
foundByNameOrMobile = signUpItem != null;
if (!foundByNameOrMobile)
{
signUpItem = repo.GetByCourseNameMobile(courseID, null, mobile);
foundByNameOrMobile = signUpItem != null;
}
}
if (foundByNameOrMobile && signUpItem.SignUpType == "預約")
{
using (var confirmForm = new ExistConfirmForm())
{
confirmForm.姓名 = signUpItem.Name;
confirmForm.手機號 = signUpItem.Mobile;
confirmForm.Top = 0;
confirmForm.Left = (this.Width - confirmForm.Width) / 2;
switch (confirmForm.ShowDialog())
{
case DialogResult.Yes: isNewRecord = false; break;
case DialogResult.No: isNewRecord = true; break;
case DialogResult.Cancel: return;
}
}
}
// 此處檢查是否有同意確認事項,若有則要跳出確認視窗
string agreementStatus = string.Empty;
if (!string.IsNullOrWhiteSpace(courseAgreedTerms))
{
agreementStatus = ShowAgreementConfirmDialog(courseAgreedTerms);
}
if (isNewRecord)
{
// 檢查是否沒有簽到而要簽退
if (!isCheckIn && signUpItem == null)
{
using (var checkoutOnlyForm = new CheckoutOnlyConfirmForm())
{
checkoutOnlyForm.姓名 = name;
checkoutOnlyForm.手機號 = mobile;
checkoutOnlyForm.Top = 0;
checkoutOnlyForm.Left = (this.Width - checkoutOnlyForm.Width) / 2;
if (checkoutOnlyForm.ShowDialog() == DialogResult.No)
{
ClearInputsAndFocus();
return;
}
}
}
signUpItem = new LocalSignUpItem
{
CourseID = courseID,
Name = name,
Mobile = mobile,
SignUpType = "現場",
IDNumber = idNumber,
Email = email,
IDType = cbIDType.Text,
MealType = cbMealType.Text,
LossJobTimes = lossJobTimes,
Gender = cbGender.Text,
AgreementStatus = agreementStatus,
CheckInTime = isCheckIn ? nowStr : string.Empty,
CheckOutTime = isCheckIn ? string.Empty : nowStr,
CreatedAt = now,
UpdatedAt = now
};
repo.Insert(signUpItem);
AddCheckInRecord(signUpItem, isCheckIn, now);
}
else
{
// 更新現有記錄
signUpItem.AgreementStatus = agreementStatus;
bool updated = UpdateCheckInTime(signUpItem, isCheckIn, now);
if (updated)
{
repo.Update(signUpItem);
AddCheckInRecord(signUpItem, isCheckIn, now);
}
}
// 更新時間顯示
// DisplayCheckInTimes(signUpItem);
// 顯示結果
string resultMessage = isNewRecord || !IsAlreadyChecked(signUpItem, isCheckIn)
? $"{cbCheckMode.Text}成功"
: $"已{cbCheckMode.Text}";
DisplayMenualCheckResult(true, resultMessage, signUpItem);
// 播放音效
if (isCheckIn)
Services.NotificationSoundService.CheckinOK();
else
Services.NotificationSoundService.CheckoutOK();
// 清空輸入框並設定焦點
ClearInputsAndFocus();
}
catch (Exception ex)
{
MessageBox.Show($"現場報到時發生錯誤:{ex.Message}", "錯誤");
Console.WriteLine($"現場報到異常:{ex}");
}
}
/// <summary>
/// 驗證輸入資料
/// </summary>
private bool ValidateInputs(out string name, out string mobile, out string idNumber, out string email, out int lossJobTimes)
{
name = txtName.Text.Trim();
mobile = txtMobile.Text.Trim();
idNumber = txtIDNumber.Text.Trim().ToUpper();
email = txtEMail.Text.Trim();
lossJobTimes = 0;
var str_lossJobTimes = txtLossJobTimes.Text.Trim();
if (string.IsNullOrWhiteSpace(name))
{
DisplayMenualCheckResult(false, "請輸入姓名", null);
txtName.Focus();
return false;
}
if (string.IsNullOrWhiteSpace(mobile))
{
DisplayMenualCheckResult(false, "請輸入手機號", null);
txtMobile.Focus();
return false;
}
if (!System.Text.RegularExpressions.Regex.IsMatch(mobile, @"^09\d{8}$"))
{
DisplayMenualCheckResult(false, "手機號格式錯誤", null);
txtMobile.Focus();
return false;
}
// 驗證身分證字號(非必填,但如果輸入則驗證格式)
if (!string.IsNullOrWhiteSpace(idNumber))
{
if (!ValidateROCIDNumber(idNumber))
{
DisplayMenualCheckResult(false, "身分證字號格式錯誤", null);
txtIDNumber.Focus();
return false;
}
}
// 驗證電子郵件(非必填,但如果輸入則驗證格式)
if (!string.IsNullOrWhiteSpace(email))
{
if (!ValidateEmail(email))
{
DisplayMenualCheckResult(false, "電子郵件格式錯誤", null);
txtEMail.Focus();
return false;
}
}
// 驗證失業給付認定次數(非必填,但如果輸入則驗證)
if (!string.IsNullOrWhiteSpace(str_lossJobTimes))
{
if (!int.TryParse(str_lossJobTimes, out lossJobTimes))
{
DisplayMenualCheckResult(false, "失業給付認定次數必須輸入數字", null);
txtLossJobTimes.Focus();
return false;
}
else if (lossJobTimes < 0)
{
DisplayMenualCheckResult(false, "失業給付認定次數必須大於或等於 0", null);
txtLossJobTimes.Focus();
return false;
}
}
return true;
}
/// <summary>
/// 驗證中華民國身分證字號
/// 規則:第一碼為英文字母(A-Z),後接九位數字
/// 使用檢查碼算法驗證
/// </summary>
private bool ValidateROCIDNumber(string idNumber)
{
if (string.IsNullOrWhiteSpace(idNumber))
return false;
idNumber = idNumber.Trim().ToUpper();
// 檢查長度
if (idNumber.Length != 10)
return false;
// 第一碼必須是英文字母
if (idNumber[0] < 'A' || idNumber[0] > 'Z')
return false;
// 後九碼必須是數字
for (int i = 1; i < 10; i++)
{
if (idNumber[i] < '0' || idNumber[i] > '9')
return false;
}
// 驗證檢查碼
// 字母對應的數字
int[] letterValues = {
10, 11, 12, 13, 14, 15, 16, 17, 34, 18, 19, 20, 21,
22, 35, 23, 24, 25, 26, 27, 28, 29, 32, 30, 31, 33
};
int code = letterValues[idNumber[0] - 'A'];
int sum = code / 10 + (code % 10) * 9;
int[] weights = { 8, 7, 6, 5, 4, 3, 2, 1 };
for (int i = 1; i <= 8; i++)
{
sum += (idNumber[i] - '0') * weights[i - 1];
}
sum += idNumber[9] - '0';
return sum % 10 == 0;
}
/// <summary>
/// 驗證電子郵件格式
/// </summary>
private bool ValidateEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
return false;
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
/// <summary>
/// 更新簽到簽退時間
/// </summary>
private bool UpdateCheckInTime(LocalSignUpItem signUpItem, bool isCheckIn, DateTime now)
{
if (isCheckIn && string.IsNullOrWhiteSpace(signUpItem.CheckInTime))
{
signUpItem.CheckInTime = now.ToString("yyyy-MM-dd HH:mm:ss");
return true;
}
else if (!isCheckIn && string.IsNullOrWhiteSpace(signUpItem.CheckOutTime))
{
signUpItem.CheckOutTime = now.ToString("yyyy-MM-dd HH:mm:ss");
return true;
}
return false;
}
/// <summary>
/// 檢查是否已經簽到或簽退
/// </summary>
private bool IsAlreadyChecked(LocalSignUpItem signUpItem, bool isCheckIn)
{
return isCheckIn ? !string.IsNullOrWhiteSpace(signUpItem.CheckInTime) : !string.IsNullOrWhiteSpace(signUpItem.CheckOutTime);
}
/// <summary>
/// 新增簽到檢查紀錄
/// </summary>
private void AddCheckInRecord(LocalSignUpItem signUpItem, bool isCheckIn, DateTime now)
{
CheckInRepository checkInRepo = new CheckInRepository();
LocalCheckInItem checkInItem = new LocalCheckInItem
{
CourseID = signUpItem.CourseID,
Name = signUpItem.Name,
Mobile = signUpItem.Mobile,
SignUpType = signUpItem.SignUpType,
IDNumber = signUpItem.IDNumber,
IDType = signUpItem.IDType,
MealType = signUpItem.MealType,
Email = signUpItem.Email,
LossJobTimes = signUpItem.LossJobTimes,
Gender = signUpItem.Gender,
AgreementStatus = signUpItem.AgreementStatus,
CheckInType = isCheckIn ? "CHECKIN" : "CHECKOUT",
CheckInTime = now.ToString("yyyy-MM-dd HH:mm:ss"),
CreatedAt = now,
UpdatedAt = now
};
checkInRepo.Insert(checkInItem);
}
/// <summary>
/// 顯示簽到簽退時間
/// </summary>
//private void DisplayCheckInTimes(LocalSignUpItem signUpItem)
//{
// lbMenualCheckInTime.Text = !string.IsNullOrWhiteSpace(signUpItem.CheckInTime)
// ? ExtractTimeFromDateTime(signUpItem.CheckInTime)
// : string.Empty;
// lbMenualCheckOutTime.Text = !string.IsNullOrWhiteSpace(signUpItem.CheckOutTime)
// ? ExtractTimeFromDateTime(signUpItem.CheckOutTime)
// : string.Empty;
//}
/// <summary>
/// 從日期時間字符串中提取時間部分(HH:mm:ss)
/// </summary>
private string ExtractTimeFromDateTime(string dateTimeStr)
{
if (string.IsNullOrWhiteSpace(dateTimeStr))
{
return string.Empty;
}
// 日期時間格式為 "yyyy-MM-dd HH:mm:ss",時間部分從索引 11 開始
if (dateTimeStr.Length >= 19)
{
return dateTimeStr.Substring(11, 8); // 提取 "HH:mm:ss"
}
return dateTimeStr;
}
/// <summary>
/// 清空輸入框並設定焦點
/// </summary>
private void ClearInputsAndFocus()
{
txtQRCode.Clear();
txtName.Clear();
txtMobile.Clear();
txtIDNumber.Clear();
txtEMail.Clear();
txtLossJobTimes.Clear();
cbIDType.SelectedIndex = -1;
cbMealType.SelectedIndex = -1;
cbGender.SelectedIndex = -1;
// 根據作業狀態設定焦點
if (checkinOperating)
{
txtQRCode.Focus();
}
}
/// <summary>
/// 從後端服務下載 Classes 資料並儲存到本地
/// </summary>
private void DownloadClassesData()
{
try
{
// 呼叫 API 取得所有課程資料(分頁取得)
int downloadedCount = 0;
ClassesRepository classesRepo = new ClassesRepository();
// 清除本地現有課程資料
classesRepo.DeleteAll();
BaseResponse response = ClassesService.SearchClasses();
if (!response.Success)
{
return;
}
// 轉換資料並保存到本地資料庫
ClassesList classesList = ApiService.DeserializeObject<ClassesList>(response.Data.ToString());
foreach (var item in classesList)
{
// 轉換為本地模型
LocalClassesItem localItem = LocalClassesItem.ConvertFromApiModel(item);
classesRepo.Insert(localItem);
downloadedCount++;
}
}
catch
{
}
}
private void cbCheckMode_SelectedIndexChanged(object sender, EventArgs e)
{
btnMenualCheckIn.Text = $"現場{cbCheckMode.Text}";
}
/// <summary>
/// 執行簽到紀錄上傳
/// </summary>
/// <param name="courseID">課程ID</param>
/// <param name="showMessage">是否要顯示訊息</param>
private void ProcessUpload(int courseID, bool showMessage)
{
// 取得本地有簽到簽退記錄的資料
CheckInRepository checkinRepo = new CheckInRepository();
var localItems = checkinRepo.GetByCourseID(courseID).Where(p => !p.UploadTime.HasValue).OrderBy(p => p.ID);
if (localItems.Count() == 0)
{
if (showMessage)
{
MessageBox.Show("沒有需要上傳的資料", "提示");
btnUpload.Enabled = true;
btnUpload.Text = "上傳";
}
return;
}
// 篩選出有簽到或簽退記錄的項目
var itemsToUpload = new List<CheckInItem>();
foreach (var item in localItems)
{
itemsToUpload.Add(
new CheckInItem()
{
ID = item.ID,
CourseID = item.CourseID,
Name = item.Name,
Mobile = item.Mobile,
SignUpType = item.SignUpType,
IDNumber = item.IDNumber,
IDType = item.IDType,
MealType = item.MealType,
Email = item.Email,
AgreementStatus = item.AgreementStatus,
CheckInType = item.CheckInType,
CheckInTime = item.CheckInTime,
});
}
// 呼叫 API 上傳資料
BaseResponse response = SignUpService.UploadCheckIns(courseID, itemsToUpload);
if (response.Success)
{
var createdCount = response.Data.CreatedCount;
var updatedCount = response.Data.UpdatedCount;
var failedCount = response.Data.FailedCount;
var SkippedCount = response.Data.SkippedCount;
var successIDs = response.Data.SuccessIDs.ToString().Split(',');
// 更新上傳時間
checkinRepo.UpdateUploadTime(successIDs);
// 重新更新統計資訊
if (showMessage)
{
CbClasses_SelectedIndexChanged(null, null);
MessageBox.Show($"上傳成功,共上傳 {itemsToUpload.Count} 筆資料", "成功");
}
}
else
{
if (showMessage)
{
MessageBox.Show("上傳失敗: " + response.Message, "錯誤");
}
}
}
private void uploadWorker_DoWork(object sender, DoWorkEventArgs e)
{
var courseID = Convert.ToInt32(e.Argument);
uploadWorker.ReportProgress(0, $"{courseID} 上傳報到資料...");
ProcessUpload(courseID, false);
}
private void uploadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lbSyncJob.Text = e.UserState.ToString();
Application.DoEvents();
}
private void uploadWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lbSyncJob.Text = "(無)";
}
private void uploadTimer_Tick(object sender, EventArgs e)
{
if (cbCurrentClass.SelectedValue == null)
{
return;
}
if (!uploadWorker.IsBusy)
{
int courseID = (int)cbCurrentClass.SelectedValue;
uploadWorker.RunWorkerAsync(courseID);
}
}
private void MainForm_Activated(object sender, EventArgs e)
{
WindowState = FormWindowState.Maximized;
SetInputControlEnabled();
}
private bool CheckLocalDB(bool showLog = true)
{
if (showLog) txtLocalDBStatus.AppendText("檢查資料庫...\r\n");
var healthCheck = DatabaseService.Instance.PerformHealthCheck();
if (healthCheck.IsHealthy)
{
if (showLog) txtLocalDBStatus.AppendText(" >> 資料庫健康狀態良好\r\n\r\n");
}
else
{
if (showLog) txtLocalDBStatus.AppendText(" >> 資料庫需要修復\r\n\r\n");
}
return healthCheck.IsHealthy;
}
private void btnCheckLocalDB_Click(object sender, EventArgs e)
{
CheckLocalDB();
}
private void btnRepareLocalDB_Click(object sender, EventArgs e)
{
txtLocalDBStatus.AppendText("修復資料庫...\r\n");
var repairResult = DatabaseService.Instance.AutoRepairDatabase();
foreach (var tableRepair in repairResult.TableRepairResults)
{
txtLocalDBStatus.AppendText($" >> {tableRepair.TableName}: {tableRepair.Message}\r\n");
}
if (CheckLocalDB() && ClientSession.OnlineMode)
{
txtLocalDBStatus.AppendText("下載員工資料...\r\n");
DownloadBPSNData();
txtLocalDBStatus.AppendText("下載課程資料...\r\n");
DownloadClassesData();
txtLocalDBStatus.AppendText(" >> 課程資料下載完畢,請依需要下載課程人員資料\r\n\r\n");
}
}
private void btnRebuildLocalDB_Click(object sender, EventArgs e)
{
txtLocalDBStatus.AppendText("重建資料庫...\r\n");
DatabaseService.Instance.DropAndCreateAllTables();
if (CheckLocalDB() && ClientSession.OnlineMode)
{
txtLocalDBStatus.AppendText("下載員工資料...\r\n");
DownloadBPSNData();
txtLocalDBStatus.AppendText("下載課程資料...\r\n");
DownloadClassesData();
txtLocalDBStatus.AppendText(" >> 課程資料下載完畢,請依需要下載課程人員資料\r\n\r\n");
}
}
private void btnFilter_Click(object sender, EventArgs e)
{
txtFilterName.Text = txtFilterName.Text.Trim();
txtFilterMobile.Text = txtFilterMobile.Text.Trim();
if (string.IsNullOrWhiteSpace(txtFilterName.Text) && string.IsNullOrWhiteSpace(txtFilterMobile.Text))
{
MessageBox.Show("請輸入姓名或手機號來篩選");
return;
}
if (cbClasses.SelectedValue == null)
{
MessageBox.Show("請先選擇課程");
return;
}
int courseID = (int)cbClasses.SelectedValue;
SignUpsRepository signUps = new SignUpsRepository();
var data = signUps.GetByCourseID(courseID);
if (!string.IsNullOrWhiteSpace(txtFilterName.Text))
data = data.Where(x => x.Name.Contains(txtFilterName.Text)).ToList();
if (!string.IsNullOrWhiteSpace(txtFilterMobile.Text))
data = data.Where(x => x.Mobile.Contains(txtFilterMobile.Text)).ToList();
dgSignUp.DataSource = null;
bsSignUp.DataSource = null;
bsSignUp.DataSource = data;
dgSignUp.DataSource = bsSignUp;
}
private void btnClearFilter_Click(object sender, EventArgs e)
{
txtFilterName.Text = string.Empty;
txtFilterMobile.Text = string.Empty;
int courseID = (int)cbClasses.SelectedValue;
SignUpsRepository signUps = new SignUpsRepository();
dgSignUp.DataSource = null;
bsSignUp.DataSource = null;
bsSignUp.DataSource = signUps.GetByCourseID(courseID);
dgSignUp.DataSource = bsSignUp;
}
private void btnRefreshClasses_Click(object sender, EventArgs e)
{
// 連線模式時,定時上傳數據
if (ClientSession.OnlineMode)
{
try
{
btnRefreshClasses.Text = "下載中";
btnRefreshClasses.Enabled = false;
Processing = true;
Application.DoEvents();
// 下載課程資料
DownloadClassesData();
}
finally
{
btnRefreshClasses.Text = "重新下載課程資料";
btnRefreshClasses.Enabled = true;
Processing = false;
}
}
}
private bool Processing
{
set
{
if (value)
{
processingForm.TopMost = true;
processingForm.Show();
}
else
{
processingForm.Hide();
}
}
}
private string ShowAgreementConfirmDialog(string agreementText)
{
using (var confirmForm = new AgreementConfirmForm())
{
confirmForm.同意事項 = agreementText;
confirmForm.Top = 0;
confirmForm.Left = (this.Width - confirmForm.Width) / 2;
return confirmForm.ShowDialog() == DialogResult.Yes ? "同意" : "不同意";
}
}
}
}