chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore - 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
<%@ Page Title="課程管理" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ClassesList.aspx.cs" Inherits="報到系統.Forms.ClassesList" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/ClassesList.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="classesApp" class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">課程管理</h1>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋面板 -->
|
||||
<div class="search-panel">
|
||||
<div class="search-row">
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程編號</label>
|
||||
<input type="text" v-model="search.courseCode" class="search-input" placeholder="請輸入課程編號">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程名稱</label>
|
||||
<input type="text" v-model="search.courseName" class="search-input" placeholder="請輸入課程名稱">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程日期(起)</label>
|
||||
<input type="date" v-model="search.courseDate1" class="search-input">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程日期(迄)</label>
|
||||
<input type="date" v-model="search.courseDate2" class="search-input">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程地點</label>
|
||||
<input type="text" v-model="search.courseLocation" class="search-input" placeholder="請輸入課程地點">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">結案</label>
|
||||
<select v-model="search.closed" class="search-input">
|
||||
<option value="">全部</option>
|
||||
<option value="true">是</option>
|
||||
<option value="false">否</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="search-field-inline autocomplete-field w-6">
|
||||
<label class="search-label-inline">計畫</label>
|
||||
<div class="autocomplete-container" style="flex: 1;">
|
||||
<input
|
||||
type="text"
|
||||
v-model="search.projectName"
|
||||
@input="searchProjects"
|
||||
@focus="showProjectSuggestions = true"
|
||||
@blur="hideProjectSuggestions"
|
||||
class="search-input autocomplete-input"
|
||||
placeholder="請輸入計畫編號或名稱">
|
||||
<div class="autocomplete-dropdown" v-if="showProjectSuggestions && projectSuggestions.length > 0">
|
||||
<div
|
||||
v-for="suggestion in projectSuggestions"
|
||||
:key="suggestion.Value"
|
||||
class="autocomplete-item"
|
||||
@click="selectProject(suggestion)">
|
||||
<span class="autocomplete-value">{{ suggestion.Value }}</span>
|
||||
<span class="autocomplete-name">{{ suggestion.Name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-actions">
|
||||
<span class="search-hint">💡 匯出時僅使用課程日期與計畫篩選條件</span>
|
||||
<button class="btn btn-secondary" @click="clearSearch">清除</button>
|
||||
<button class="btn btn-primary" @click="performSearch">搜尋</button>
|
||||
<button class="btn btn-primary" @click="performExport">匯出</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋結果 -->
|
||||
<div class="result-panel">
|
||||
<table class="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">課程編號</th>
|
||||
<th class="text-center">課程名稱</th>
|
||||
<th class="text-center">課程日期</th>
|
||||
<th class="text-center">開始時間</th>
|
||||
<th class="text-center">結束時間</th>
|
||||
<th class="text-center">講師</th>
|
||||
<th class="text-center">課程地點</th>
|
||||
<th class="text-center">計畫</th>
|
||||
<%-- <th class="text-center">實到</th>
|
||||
<th class="text-center">應到</th>
|
||||
<th class="text-center">缺席</th>
|
||||
<th class="text-center">異常</th>
|
||||
<th class="text-center">現場</th>--%>
|
||||
<th class="text-center">結案</th>
|
||||
<th class="text-center">
|
||||
操作
|
||||
<button class="btn btn-success btn-sm" style="margin-left: 10px;" @click="openAddModal">新增</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="loading">
|
||||
<td colspan="10" class="empty-message">載入中...</td>
|
||||
</tr>
|
||||
<tr v-else-if="classes.length === 0">
|
||||
<td colspan="10" class="empty-message">查無資料</td>
|
||||
</tr>
|
||||
<tr v-else v-for="item in classes" :key="item.ID">
|
||||
<td class="text-center">{{ item.CourseCode }}</td>
|
||||
<td>{{ item.CourseName }}</td>
|
||||
<td class="text-center">{{ $format.date(item.CourseDate) }}</td>
|
||||
<td class="text-center">{{ item.StartTime || '-' }}</td>
|
||||
<td class="text-center">{{ item.EndTime || '-' }}</td>
|
||||
<td>{{ item.Teacher || '-' }}</td>
|
||||
<td>{{ item.CourseLocation }}</td>
|
||||
<td class="project-cell">{{ formatProjectInfo(item) }}</td>
|
||||
<%-- <td class="text-center">{{ item.Actual }}</td>
|
||||
<td class="text-center">{{ item.Expected }}</td>
|
||||
<td class="text-center">{{ item.Absent }}</td>
|
||||
<td class="text-center">{{ item.Exception }}</td>
|
||||
<td class="text-center">{{ item.OnSite }}</td>--%>
|
||||
<td class="text-center">{{ item.Closed ? "是" : "否" }}</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-primary btn-sm" @click="openEditModal(item)">更正</button>
|
||||
<button class="btn btn-danger btn-sm" @click="deleteClasses(item)">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分頁 -->
|
||||
<div class="pagination" v-if="totalCount > 0">
|
||||
<button class="btn btn-secondary btn-sm" :disabled="currentPage === 1" @click="goToPage(currentPage - 1)">上一頁</button>
|
||||
<span class="pagination-info">第 {{ currentPage }} / {{ totalPages }} 頁,共 {{ totalCount }} 筆</span>
|
||||
<button class="btn btn-secondary btn-sm" :disabled="currentPage === totalPages" @click="goToPage(currentPage + 1)">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增/更正彈窗 -->
|
||||
<div class="modal" :class="{ show: showModal }">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">{{ isNewMode ? '新增課程' : '更正課程' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- 第一行:課程編號、結案 -->
|
||||
<div class="form-row">
|
||||
<div class="form-group-inline form-col-50">
|
||||
<label class="form-label-inline">課程編號</label>
|
||||
<input type="text" v-model="editItem.CourseCode" class="form-control" disabled>
|
||||
</div>
|
||||
<div class="form-group-inline form-col-50">
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" v-model="editItem.Closed" id="cbClosed">
|
||||
<label class="checkbox-label" for="cbClosed">結案 (在客戶端隱藏)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:課程名稱 -->
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline required">課程名稱</label>
|
||||
<input type="text" v-model="editItem.CourseName" class="form-control" maxlength="100" placeholder="請輸入課程名稱">
|
||||
<div class="error-message" v-if="validationErrors.CourseName">{{ validationErrors.CourseName }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 第三行:課程日期、開始時間、結束時間 -->
|
||||
<div class="form-row">
|
||||
<div class="form-group-inline form-col-33">
|
||||
<label class="form-label-inline required">課程日期</label>
|
||||
<input type="date" v-model="editItem.CourseDate" class="form-control">
|
||||
<div class="error-message" v-if="validationErrors.CourseDate">{{ validationErrors.CourseDate }}</div>
|
||||
</div>
|
||||
<div class="form-group-inline form-col-33">
|
||||
<label class="form-label-inline required">開始時間</label>
|
||||
<input type="time" v-model="editItem.StartTime" class="form-control">
|
||||
<div class="error-message" v-if="validationErrors.StartTime">{{ validationErrors.StartTime }}</div>
|
||||
</div>
|
||||
<div class="form-group-inline form-col-33">
|
||||
<label class="form-label-inline required">結束時間</label>
|
||||
<input type="time" v-model="editItem.EndTime" class="form-control">
|
||||
<div class="error-message" v-if="validationErrors.EndTime">{{ validationErrors.EndTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第四行:課程地點、講師 -->
|
||||
<div class="form-row">
|
||||
<div class="form-group-inline form-col-50">
|
||||
<label class="form-label-inline">課程地點</label>
|
||||
<input type="text" v-model="editItem.CourseLocation" class="form-control" placeholder="請輸入課程地點">
|
||||
<div class="error-message" v-if="validationErrors.CourseLocation">{{ validationErrors.CourseLocation }}</div>
|
||||
</div>
|
||||
<div class="form-group-inline form-col-50">
|
||||
<label class="form-label-inline required">講師</label>
|
||||
<input type="text" v-model="editItem.Teacher" class="form-control" placeholder="請輸入講師名稱">
|
||||
<div class="error-message" v-if="validationErrors.Teacher">{{ validationErrors.Teacher }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第五行:計畫 -->
|
||||
<div class="form-group-inline autocomplete-field">
|
||||
<label class="form-label-inline">計畫</label>
|
||||
<div class="autocomplete-container" style="flex: 1;">
|
||||
<input
|
||||
type="text"
|
||||
v-model="editItem.projectName"
|
||||
@input="searchEditProjectSuggestions"
|
||||
@focus="showEditProjectSuggestions = true"
|
||||
@blur="hideEditProjectSuggestions"
|
||||
class="form-control autocomplete-input"
|
||||
placeholder="請輸入計畫編號或名稱">
|
||||
<div class="autocomplete-dropdown" v-if="showEditProjectSuggestions && editProjectSuggestions.length > 0">
|
||||
<div
|
||||
v-for="suggestion in editProjectSuggestions"
|
||||
:key="suggestion.Value"
|
||||
class="autocomplete-item"
|
||||
@click="selectEditProject(suggestion)">
|
||||
<span class="autocomplete-value">{{ suggestion.Value }}</span>
|
||||
<span class="autocomplete-name">{{ suggestion.Name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第五點五行:同意確認事項 -->
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">同意確認事項</label>
|
||||
<input type="text" v-model="editItem.AgreedTerms" class="form-control" maxlength="100" placeholder="請輸入同意確認事項">
|
||||
</div>
|
||||
|
||||
<!-- 第六行:通知標題 -->
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">通知標題</label>
|
||||
<input type="text" v-model="editItem.NotificationTitle" class="form-control" maxlength="100" placeholder="請輸入通知標題">
|
||||
<div class="error-message" v-if="validationErrors.NotificationTitle">{{ validationErrors.NotificationTitle }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 第七行:通知模板、預覽 -->
|
||||
<div class="form-row">
|
||||
<div class="form-group form-col-50">
|
||||
<label class="form-label">通知模板</label>
|
||||
<textarea ref="templateTextarea" v-model="editItem.NotificationTemplate" class="form-control" maxlength="1000" placeholder="請輸入通知模板"></textarea>
|
||||
<div class="error-message" v-if="validationErrors.NotificationTemplate">{{ validationErrors.NotificationTemplate }}</div>
|
||||
<div class="template-buttons">
|
||||
<button class="btn-template" @click="insertTemplate('{課程日期}')">插入:課程日期</button>
|
||||
<button class="btn-template" @click="insertTemplate('{課程名稱}')">插入:課程名稱</button>
|
||||
<button class="btn-template" @click="insertTemplate('{課程地點}')">插入:課程地點</button>
|
||||
<button class="btn-template" @click="insertTemplate('{開始時間}')">插入:開始時間</button>
|
||||
<button class="btn-template" @click="insertTemplate('{結束時間}')">插入:結束時間</button>
|
||||
<button class="btn-template" @click="insertTemplate('{講師}')">插入:講師</button>
|
||||
<button class="btn-template" @click="insertTemplate('{學員姓名}')">插入:學員姓名</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group form-col-50">
|
||||
<label class="form-label">預覽</label>
|
||||
<div class="template-preview">
|
||||
<p v-if="!editItem.NotificationTemplate" class="preview-placeholder">(空白)</p>
|
||||
<p v-else class="preview-content">{{ generatePreview }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="closeModal">取消</button>
|
||||
<button class="btn btn-success" v-if="isNewMode" @click="saveAndContinue">存檔後繼續新增</button>
|
||||
<button class="btn btn-primary" @click="saveClasses">存檔</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
|
||||
<script src="<%= ResolveUrlWithSysVer("~/Scripts/ClassesList.js") %>"></script>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class ClassesList : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
// 可在此處進行權限檢查或初始化
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <自動產生>
|
||||
// 這段程式碼是由工具產生的。
|
||||
//
|
||||
// 變更這個檔案可能會導致不正確的行為,而且如果已重新產生
|
||||
// 程式碼,則會遺失變更。
|
||||
// </自動產生>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
|
||||
|
||||
public partial class ClassesList
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<%@ Page Title="課程報表" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ClassesReport.aspx.cs" Inherits="報到系統.Forms.ClassesReport" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/ClassesList.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="classesReportApp" class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">課程報表</h1>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋面板 -->
|
||||
<div class="search-panel">
|
||||
<div class="search-row">
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程編號</label>
|
||||
<input type="text" v-model="search.courseCode" class="search-input" placeholder="請輸入課程編號">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程名稱</label>
|
||||
<input type="text" v-model="search.courseName" class="search-input" placeholder="請輸入課程名稱">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程日期(起)</label>
|
||||
<input type="date" v-model="search.courseDate1" class="search-input">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程日期(迄)</label>
|
||||
<input type="date" v-model="search.courseDate2" class="search-input">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">課程地點</label>
|
||||
<input type="text" v-model="search.courseLocation" class="search-input" placeholder="請輸入課程地點">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">結案</label>
|
||||
<select v-model="search.closed" class="search-input">
|
||||
<option value="">全部</option>
|
||||
<option value="true">是</option>
|
||||
<option value="false">否</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">簽到退時間格式</label>
|
||||
<select v-model="search.timeFormat" class="search-input">
|
||||
<option value="datetime">年月日時分秒</option>
|
||||
<option value="time">時分</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="search-field-inline autocomplete-field w-6">
|
||||
<label class="search-label-inline">計畫</label>
|
||||
<div class="autocomplete-container" style="flex: 1;">
|
||||
<input
|
||||
type="text"
|
||||
v-model="search.projectName"
|
||||
@input="searchProjects"
|
||||
@focus="showProjectSuggestions = true"
|
||||
@blur="hideProjectSuggestions"
|
||||
class="search-input autocomplete-input"
|
||||
placeholder="請輸入計畫編號或名稱">
|
||||
<div class="autocomplete-dropdown" v-if="showProjectSuggestions && projectSuggestions.length > 0">
|
||||
<div
|
||||
v-for="suggestion in projectSuggestions"
|
||||
:key="suggestion.Value"
|
||||
class="autocomplete-item"
|
||||
@click="selectProject(suggestion)">
|
||||
<span class="autocomplete-value">{{ suggestion.Value }}</span>
|
||||
<span class="autocomplete-name">{{ suggestion.Name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-actions">
|
||||
<button class="btn btn-secondary" @click="clearSearch">清除</button>
|
||||
<button class="btn btn-primary" @click="performSearch">搜尋</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋結果 -->
|
||||
<div class="result-panel">
|
||||
<table class="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">課程編號</th>
|
||||
<th class="text-center">課程名稱</th>
|
||||
<th class="text-center">課程日期</th>
|
||||
<th class="text-center">開始時間</th>
|
||||
<th class="text-center">結束時間</th>
|
||||
<th class="text-center">講師</th>
|
||||
<th class="text-center">課程地點</th>
|
||||
<th class="text-center">計畫</th>
|
||||
<%-- <th class="text-center">實到</th>
|
||||
<th class="text-center">應到</th>
|
||||
<th class="text-center">缺席</th>
|
||||
<th class="text-center">異常</th>
|
||||
<th class="text-center">現場</th>--%>
|
||||
<th class="text-center">結案</th>
|
||||
<th class="text-center">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="loading">
|
||||
<td colspan="10" class="empty-message">載入中...</td>
|
||||
</tr>
|
||||
<tr v-else-if="classes.length === 0">
|
||||
<td colspan="10" class="empty-message">查無資料</td>
|
||||
</tr>
|
||||
<tr v-else v-for="item in classes" :key="item.ID">
|
||||
<td class="text-center">{{ item.CourseCode }}</td>
|
||||
<td class="text-center">{{ item.CourseName }}</td>
|
||||
<td class="text-center">{{ $format.date(item.CourseDate) }}</td>
|
||||
<td class="text-center">{{ item.StartTime }}</td>
|
||||
<td class="text-center">{{ item.EndTime }}</td>
|
||||
<td class="text-center">{{ item.Teacher }}</td>
|
||||
<td class="text-center">{{ item.CourseLocation }}</td>
|
||||
<td class="text-center">{{ item.ProjectValue }} {{ item.ProjectName }}</td>
|
||||
<%-- <td class="text-center">{{ item.Actual }}</td>
|
||||
<td class="text-center">{{ item.Expected }}</td>
|
||||
<td class="text-center">{{ item.Absent }}</td>
|
||||
<td class="text-center">{{ item.Exception }}</td>
|
||||
<td class="text-center">{{ item.OnSite }}</td>--%>
|
||||
<td class="text-center">{{ item.Closed ? '是' : '否' }}</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-primary btn-sm" @click="exportReport(item.ID)" :disabled="exporting">
|
||||
<span v-if="exporting && exportingId === item.ID">匯出中...</span>
|
||||
<span v-else>學員簽到表</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分頁 -->
|
||||
<div class="pagination" v-if="totalPages > 1">
|
||||
<button class="btn btn-secondary btn-sm" @click="previousPage" :disabled="currentPage === 1">上一頁</button>
|
||||
<span class="pagination-info">第 {{ currentPage }} / {{ totalPages }} 頁,共 {{ totalCount }} 筆</span>
|
||||
<button class="btn btn-secondary btn-sm" @click="nextPage" :disabled="currentPage === totalPages">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="<%= ResolveUrlWithSysVer("~/Scripts/ClassesReport.js") %>"></script>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class ClassesReport : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <自動產生>
|
||||
// 這段程式碼是由工具產生的。
|
||||
//
|
||||
// 變更這個檔案可能會導致不正確的行為,而且如果已重新產生
|
||||
// 程式碼,則會遺失變更。
|
||||
// </自動產生>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
|
||||
|
||||
public partial class ClassesReport
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Constructing.aspx.cs" Inherits="報到系統.Forms.Constructing" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="homeApp" class="page-container">
|
||||
<h1 class="page-title">功能建置中...</h1>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class Constructing : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <自動產生>
|
||||
// 這段程式碼是由工具產生的。
|
||||
//
|
||||
// 變更這個檔案可能會導致不正確的行為,而且如果已重新產生
|
||||
// 程式碼,則會遺失變更。
|
||||
// </自動產生>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
|
||||
|
||||
public partial class Constructing
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<%@ Page Title="首頁" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Home.aspx.cs" Inherits="報到系統.Forms.Home" %>
|
||||
|
||||
<%@ Import Namespace="報到系統" %>
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/Home.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="homeApp" class="home-container">
|
||||
|
||||
<!-- 客戶端安裝指南 -->
|
||||
<div class="content-section">
|
||||
<h3>📥 下載與安裝報到系統客戶端</h3>
|
||||
<ol class="steps-list">
|
||||
<li>
|
||||
<a href="<%= LatestClientPackage() %>" class="home-btn">點此下載安裝包壓縮檔</a>
|
||||
</li>
|
||||
<li>
|
||||
解壓縮下載的安裝包壓縮檔到任何資料夾
|
||||
</li>
|
||||
<li>
|
||||
找到並執行 <strong>setup.exe</strong> 文件
|
||||
</li>
|
||||
<li>
|
||||
依照安裝精靈進行安裝步驟
|
||||
</li>
|
||||
<li>
|
||||
安裝完畢後,您將在桌面看到 <strong>"報到系統客戶端"</strong> 捷徑
|
||||
</li>
|
||||
</ol>
|
||||
<div class="info-box">
|
||||
<div class="info-box-title">💡 提示</div>
|
||||
<p>建議使用最新版本的報到系統客戶端以獲得最佳體驗和安全性。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class Home : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string LatestClientPackage()
|
||||
{
|
||||
var latestClientPackage = GetLatestClientPackageFileName();
|
||||
if (!string.IsNullOrEmpty(latestClientPackage))
|
||||
{
|
||||
return ResolveUrlWithSysVer(string.Format("~/Assets/Downloads/{0}", latestClientPackage));
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得最新的客戶端安裝包檔案名稱
|
||||
/// 從 Assets/Downloads 目錄下查找 "報到系統客戶端_*.zip" 檔案
|
||||
/// 按檔名排序取最大的那一個
|
||||
/// </summary>
|
||||
private string GetLatestClientPackageFileName()
|
||||
{
|
||||
try
|
||||
{
|
||||
string downloadDir = Server.MapPath("~/Assets/Downloads/");
|
||||
|
||||
if (!Directory.Exists(downloadDir))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
DirectoryInfo dirInfo = new DirectoryInfo(downloadDir);
|
||||
FileInfo[] files = dirInfo.GetFiles("報到系統客戶端_*.zip");
|
||||
|
||||
if (files.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// 按檔名排序,取最後一個(最大的那個)
|
||||
string latestFileName = files.OrderBy(f => f.Name).LastOrDefault()?.Name;
|
||||
|
||||
return latestFileName ?? string.Empty;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <自動產生>
|
||||
// 這段程式碼是由工具產生的。
|
||||
//
|
||||
// 變更這個檔案可能會導致不正確的行為,而且如果已重新產生
|
||||
// 程式碼,則會遺失變更。
|
||||
// </自動產生>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
|
||||
|
||||
public partial class Home
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<%@ Page Title="簡訊帳號管理" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="MessageAccountManage.aspx.cs" Inherits="報到系統.Forms.MessageAccountManage" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/MessageAccountManage.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="messageAccountManageApp" class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">簡訊帳號管理</h1>
|
||||
<p class="page-description">管理計畫的簡訊發送帳號密碼</p>
|
||||
</div>
|
||||
|
||||
<!-- 目前預設平台區塊(含 inline 切換) -->
|
||||
<div style="margin-bottom:12px; border:1px solid #c8d8f8; border-radius:4px; background:#f0f4ff; overflow:hidden;">
|
||||
<div style="padding:10px 16px; display:flex; align-items:center; gap:16px;">
|
||||
<span>目前預設平台:<strong>{{ currentPlatform ? currentPlatform.PlatformName + '(' + currentPlatform.PlatformCode + ')' : '載入中...' }}</strong></span>
|
||||
<button type="button" class="btn btn-secondary btn-sm" @click="showSwitchPanel = !showSwitchPanel">
|
||||
{{ showSwitchPanel ? '收合' : '切換平台' }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="showSwitchPanel" style="padding:10px 16px 14px; border-top:1px solid #c8d8f8; background:#fff; display:flex; align-items:center; gap:12px; flex-wrap:wrap;">
|
||||
<label style="margin:0;">切換為:</label>
|
||||
<select v-model="switchPlatformCode" class="form-control" style="width:220px;">
|
||||
<option v-for="p in platforms" :key="p.PlatformCode" :value="p.PlatformCode">{{ p.PlatformName }}({{ p.PlatformCode }})</option>
|
||||
</select>
|
||||
<button type="button" class="btn btn-primary btn-sm" @click="setCurrentPlatform">確認切換</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" @click="showSwitchPanel = false">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 平台設定區塊 -->
|
||||
<div class="search-panel" style="margin-bottom:12px;">
|
||||
<div style="display:flex; align-items:center; cursor:pointer; user-select:none;" @click="showPlatformSettings = !showPlatformSettings">
|
||||
<strong>平台帳號設定</strong>
|
||||
<span style="margin-left:8px;">{{ showPlatformSettings ? '▲' : '▼' }}</span>
|
||||
</div>
|
||||
<div v-if="showPlatformSettings" style="margin-top:12px;">
|
||||
<div v-for="p in platforms" :key="p.PlatformCode" style="display:flex; align-items:center; gap:12px; margin-bottom:10px; flex-wrap:wrap;">
|
||||
<span style="min-width:80px; font-weight:bold;">{{ p.PlatformName }}</span>
|
||||
<input type="text" v-model="p.Account" class="form-control" style="width:200px;" placeholder="主帳號">
|
||||
<input :type="p.showPwd ? 'text' : 'password'" v-model="p.Password" class="form-control" style="width:200px;" placeholder="主密碼">
|
||||
<button type="button" class="btn-template" @click="p.showPwd = !p.showPwd">{{ p.showPwd ? '隱藏' : '顯示' }}</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" @click="savePlatformSetting(p)">儲存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋面板 -->
|
||||
<div class="search-panel">
|
||||
<div class="search-row">
|
||||
<div class="search-field-inline autocomplete-field w-6">
|
||||
<label class="search-label-inline">計畫</label>
|
||||
<div class="autocomplete-container" style="flex: 1;">
|
||||
<input
|
||||
type="text"
|
||||
v-model="search.projectName"
|
||||
@input="searchProjects"
|
||||
@focus="showProjectSuggestions = true"
|
||||
@blur="hideProjectSuggestions"
|
||||
class="search-input autocomplete-input"
|
||||
placeholder="請輸入計畫編號或名稱">
|
||||
<div class="autocomplete-dropdown" v-if="showProjectSuggestions && projectSuggestions.length > 0">
|
||||
<div
|
||||
v-for="suggestion in projectSuggestions"
|
||||
:key="suggestion.Value"
|
||||
class="autocomplete-item"
|
||||
@click="selectProject(suggestion)">
|
||||
<span class="autocomplete-value">{{ suggestion.Value }}</span>
|
||||
<span class="autocomplete-name">{{ suggestion.Name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-field-inline w-4">
|
||||
<label class="search-label-inline">平台</label>
|
||||
<select v-model="search.platformCode" class="search-input">
|
||||
<option value="">全部</option>
|
||||
<option v-for="p in platforms" :key="p.PlatformCode" :value="p.PlatformCode">{{ p.PlatformName }}({{ p.PlatformCode }})</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-actions">
|
||||
<button type="button" class="btn btn-secondary" @click="clearSearch">清除</button>
|
||||
<button type="button" class="btn btn-primary" @click="performSearch">搜尋</button>
|
||||
<button type="button" class="btn btn-success" style="margin-left: 10px;" @click="openAddModal">新增帳號</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 列表區域 -->
|
||||
<div class="result-panel">
|
||||
<table class="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">計畫代號</th>
|
||||
<th class="text-center">計畫名稱</th>
|
||||
<th class="text-center">平台</th>
|
||||
<th class="text-center">簡訊帳號</th>
|
||||
<th class="text-center">簡訊密碼</th>
|
||||
<th class="text-center">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="loading">
|
||||
<td colspan="6" class="empty-message">載入中...</td>
|
||||
</tr>
|
||||
<tr v-else-if="accountList.length === 0">
|
||||
<td colspan="6" class="empty-message">查無資料</td>
|
||||
</tr>
|
||||
<tr v-else v-for="item in accountList" :key="item.ID">
|
||||
<td class="text-center">{{ item.ProjectID }}</td>
|
||||
<td>{{ item.ProjectName || '-' }}</td>
|
||||
<td class="text-center">{{ getPlatformName(item.PlatformCode) }}</td>
|
||||
<td class="password-cell">
|
||||
<span v-if="!item.showPassword" class="password-mask">●●●●●●●●</span>
|
||||
<span v-else class="password-text">{{ item.MMSAccount }}</span>
|
||||
</td>
|
||||
<td class="password-cell">
|
||||
<span v-if="!item.showPassword" class="password-mask">●●●●●●●●</span>
|
||||
<span v-else class="password-text">{{ item.MMSPassword }}</span>
|
||||
<button type="button" class="btn-icon" @click.prevent="togglePasswordDisplay(item)">
|
||||
{{ item.showPassword ? '隱藏' : '顯示' }}
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="btn btn-primary btn-sm" @click="openEditModal(item)">編輯</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" @click="deleteAccount(item)">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分頁 -->
|
||||
<div class="pagination" v-if="totalCount > 0">
|
||||
<button type="button" class="btn btn-secondary btn-sm" :disabled="currentPage === 1" @click="previousPage">上一頁</button>
|
||||
<span class="pagination-info">第 {{ currentPage }} / {{ totalPages }} 頁,共 {{ totalCount }} 筆</span>
|
||||
<button type="button" class="btn btn-secondary btn-sm" :disabled="currentPage === totalPages" @click="nextPage">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增/編輯彈窗 -->
|
||||
<div class="modal" :class="{ show: showModal }">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">{{ isNewMode ? '新增簡訊帳號' : '編輯簡訊帳號' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- 計畫代號 - 自動完成 -->
|
||||
<div class="form-group-inline autocomplete-field">
|
||||
<label class="form-label-inline required">計畫代號</label>
|
||||
<div class="autocomplete-container" style="flex: 1;">
|
||||
<input
|
||||
type="text"
|
||||
v-model="editItem.projectName"
|
||||
@input="searchEditProjectSuggestions"
|
||||
@focus="showEditProjectSuggestions = true"
|
||||
@blur="hideEditProjectSuggestions"
|
||||
class="form-control autocomplete-input"
|
||||
:disabled="!isNewMode"
|
||||
placeholder="請輸入計畫編號或名稱">
|
||||
<div class="autocomplete-dropdown" v-if="showEditProjectSuggestions && editProjectSuggestions.length > 0">
|
||||
<div
|
||||
v-for="suggestion in editProjectSuggestions"
|
||||
:key="suggestion.Value"
|
||||
class="autocomplete-item"
|
||||
@click="selectEditProject(suggestion)">
|
||||
<span class="autocomplete-value">{{ suggestion.Value }}</span>
|
||||
<span class="autocomplete-name">{{ suggestion.Name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="error-message" v-if="validationErrors.ProjectID">{{ validationErrors.ProjectID }}</div>
|
||||
|
||||
<!-- 使用平台 -->
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">使用平台</label>
|
||||
<select v-model="editItem.PlatformCode" class="form-control">
|
||||
<option v-for="p in platforms" :key="p.PlatformCode" :value="p.PlatformCode">{{ p.PlatformName }}({{ p.PlatformCode }})</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 簡訊帳號 -->
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline required">簡訊帳號</label>
|
||||
<input type="text"
|
||||
v-model="editItem.MMSAccount"
|
||||
class="form-control"
|
||||
placeholder="請輸入簡訊帳號"
|
||||
maxlength="50">
|
||||
<div class="error-message" v-if="validationErrors.MMSAccount">{{ validationErrors.MMSAccount }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 簡訊密碼 -->
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline required">簡訊密碼</label>
|
||||
<input :type="showPassword ? 'text' : 'password'"
|
||||
v-model="editItem.MMSPassword"
|
||||
class="form-control"
|
||||
placeholder="請輸入簡訊密碼"
|
||||
maxlength="50">
|
||||
<button type="button" class="btn-template" @click.prevent="toggleEditPasswordDisplay">
|
||||
{{ showPassword ? '隱藏' : '顯示' }}
|
||||
</button>
|
||||
<div class="error-message" v-if="validationErrors.MMSPassword">{{ validationErrors.MMSPassword }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" @click="closeModal">取消</button>
|
||||
<button type="button" class="btn btn-primary" @click="saveAccount">存檔</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
|
||||
<script src="<%= ResolveUrlWithSysVer("~/Scripts/MessageAccountManage.js") %>"></script>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class MessageAccountManage : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
// 初始化頁面
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <自動產生>
|
||||
// 這段程式碼是由工具產生的。
|
||||
//
|
||||
// 變更這個檔案可能會導致不正確的行為,而且如果已重新產生
|
||||
// 程式碼,則會遺失變更。
|
||||
// </自動產生>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
|
||||
|
||||
public partial class MessageAccountManage
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<%@ Page Title="群組人員管理" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="RoleManage.aspx.cs" Inherits="報到系統.Forms.RoleManage" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/RoleManage.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="roleManageApp" class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">權限群組管理</h1>
|
||||
<p class="page-description">管理系統群組及其成員,支援批次新增或移除</p>
|
||||
</div>
|
||||
|
||||
<div class="role-manage-layout">
|
||||
<!-- 左側:群組清單 -->
|
||||
<div class="group-panel">
|
||||
<h2 class="section-title">群組清單</h2>
|
||||
<div class="group-item"
|
||||
v-for="group in groupList"
|
||||
:key="group.id"
|
||||
:class="{ active: selectedGroup && selectedGroup.id === group.id }"
|
||||
@click="selectGroup(group)">
|
||||
<div class="group-name">{{ group.name }}</div>
|
||||
<div class="group-description">{{ group.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右側:人員清單 -->
|
||||
<div class="member-panel">
|
||||
<!-- 群組成員列表 -->
|
||||
<div class="member-list-section">
|
||||
<h2 class="section-title">
|
||||
群組成員
|
||||
<span v-if="selectedGroup"> ({{ selectedGroup.name }})</span>
|
||||
</h2>
|
||||
|
||||
<div v-if="!selectedGroup" class="empty-message">
|
||||
請先選擇一個群組
|
||||
</div>
|
||||
|
||||
<div v-else-if="loadingMembers" class="loading-message">
|
||||
載入中...
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="member-list">
|
||||
<div v-if="groupMembers.length === 0" class="empty-message">
|
||||
此群組目前沒有成員
|
||||
</div>
|
||||
<div v-else
|
||||
v-for="member in groupMembers"
|
||||
:key="member.Value"
|
||||
class="member-item"
|
||||
:class="{ selected: isSelectedMember(member.Value) }"
|
||||
@click="toggleMemberSelection(member.Value)">
|
||||
<input type="checkbox"
|
||||
class="member-checkbox"
|
||||
:checked="isSelectedMember(member.Value)"
|
||||
@click.stop="toggleMemberSelection(member.Value)">
|
||||
<div class="member-info">
|
||||
<span class="member-name">{{ member.Name }}</span>
|
||||
<span class="member-id">({{ member.Value }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-secondary"
|
||||
:disabled="selectedMembers.length === 0"
|
||||
@click="removeMembersFromGroup">
|
||||
移除群組 ({{ selectedMembers.length }})
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 人員查詢 -->
|
||||
<div class="member-search-section">
|
||||
<h2 class="section-title">人員查詢</h2>
|
||||
|
||||
<div class="search-box">
|
||||
<input type="text"
|
||||
v-model="searchKeyword"
|
||||
class="search-input"
|
||||
placeholder="輸入員工編號或姓名搜尋..."
|
||||
@keyup.enter="searchEmployees">
|
||||
<button class="btn btn-primary"
|
||||
style="margin-top: 10px; width: 100%;"
|
||||
@click="searchEmployees">
|
||||
搜尋
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="loadingSearch" class="loading-message">
|
||||
搜尋中...
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="member-list">
|
||||
<div v-if="searchResults.length === 0" class="empty-message">
|
||||
{{ searchKeyword ? '查無結果' : '請輸入關鍵字進行搜尋' }}
|
||||
</div>
|
||||
<div v-else
|
||||
v-for="employee in searchResults"
|
||||
:key="employee.Value"
|
||||
class="member-item"
|
||||
:class="{ selected: isSelectedEmployee(employee.Value) }"
|
||||
@click="toggleEmployeeSelection(employee.Value)">
|
||||
<input type="checkbox"
|
||||
class="member-checkbox"
|
||||
:checked="isSelectedEmployee(employee.Value)"
|
||||
@click.stop="toggleEmployeeSelection(employee.Value)">
|
||||
<div class="member-info">
|
||||
<span class="member-name">{{ employee.Name }}</span>
|
||||
<span class="member-id">({{ employee.Value }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-primary"
|
||||
:disabled="!selectedGroup || selectedEmployees.length === 0"
|
||||
@click="addEmployeesToGroup">
|
||||
加入群組 ({{ selectedEmployees.length }})
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
|
||||
<script src="<%= ResolveUrlWithSysVer("~/Scripts/RoleManage.js") %>"></script>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Web.UI;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class RoleManage : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
// 檢查是否為系統管理員
|
||||
if (Session[PublicVariable.IsSystemManager] == null ||
|
||||
!(bool)Session[PublicVariable.IsSystemManager])
|
||||
{
|
||||
Response.Redirect("~/Forms/Home.aspx");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+17
@@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <自動產生>
|
||||
// 這段程式碼是由工具產生的。
|
||||
//
|
||||
// 變更這個檔案可能會導致不正確的行為,而且如果已重新產生
|
||||
// 程式碼,則會遺失變更。
|
||||
// </自動產生>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
|
||||
|
||||
public partial class RoleManage
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<%@ Page Title="匯出參加人員" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="SignUpExport.aspx.cs" Inherits="報到系統.Forms.SignUpExport" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/SignUpExport.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="signUpExportApp" class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">匯出參加人員</h1>
|
||||
</div>
|
||||
|
||||
<!-- 課程選擇面板 -->
|
||||
<div class="search-panel">
|
||||
<div class="search-row">
|
||||
<div class="search-field-inline w-6">
|
||||
<div class="search-input-group">
|
||||
<label class="search-label-inline required">選擇課程</label>
|
||||
<select v-model="selectedCourseID" class="search-input">
|
||||
<option value="">-- 請選擇要匯出的課程 --</option>
|
||||
<option v-for="course in courses" :key="course.ID" :value="course.ID">
|
||||
{{ course.CourseName }} ({{ $format.date(course.CourseDate) }})
|
||||
</option>
|
||||
</select>
|
||||
<label class="search-checkbox-label">
|
||||
<input type="checkbox" v-model="includeClosed"> 包含已結案
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">簽到退時間格式</label>
|
||||
<select v-model="timeFormat" class="search-input">
|
||||
<option value="datetime">年月日時分秒</option>
|
||||
<option value="time">時分</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 預覽與匯出面板 -->
|
||||
<div class="export-panel" v-if="selectedCourseID">
|
||||
<div class="preview-section" v-if="signupData">
|
||||
<h3 class="section-title">準備匯出</h3>
|
||||
|
||||
<div class="course-info">
|
||||
<div class="info-row">
|
||||
<span class="info-label">匯出檔案:</span>
|
||||
<span class="info-value">{{ signupData.FileName }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">參加人員數:</span>
|
||||
<span class="info-value">{{ signupData.DataCount }} 人</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">檔案大小:</span>
|
||||
<span class="info-value">{{ formatFileSize(signupData.FileSize) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 匯出按鈕 -->
|
||||
<div class="export-actions">
|
||||
<button class="btn btn-success" @click="executeExport" :disabled="!signupData || exporting">
|
||||
<span v-if="!exporting">📥 匯出為 Excel 檔案</span>
|
||||
<span v-else>匯出中...</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click="clearSelection">清除選擇</button>
|
||||
</div>
|
||||
|
||||
<!-- 匯出結果訊息 -->
|
||||
<div class="result-section" v-if="exportMessage">
|
||||
<div class="message-box" :class="{ success: exportSuccess, error: !exportSuccess }">
|
||||
<span v-if="exportSuccess">✓ {{ exportMessage }}</span>
|
||||
<span v-else>✗ {{ exportMessage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-message" v-else style="padding: 40px;">
|
||||
請先選擇要匯出的課程
|
||||
</div>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
|
||||
<script src="<%= ResolveUrlWithSysVer("~/Scripts/SignUpExport.js") %>"></script>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class SignUpExport : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
// SignUpExport.aspx 現在使用 Vue.js
|
||||
// 所有邏輯都在前端和 SignUpService.asmx 處理
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<%@ Page Title="匯入參加人員" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="SignUpImport.aspx.cs" Inherits="報到系統.Forms.SignUpImport" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/SignUpImport.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="signUpImportApp" class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">匯入參加人員</h1>
|
||||
</div>
|
||||
|
||||
<!-- 課程選擇面板 -->
|
||||
<div class="search-panel">
|
||||
<div class="search-row">
|
||||
<div class="search-field-inline w-6">
|
||||
<div class="search-input-group">
|
||||
<label class="search-label-inline required">選擇課程</label>
|
||||
<select v-model="selectedCourseID" class="search-input">
|
||||
<option value="">-- 請選擇課程 --</option>
|
||||
<option v-for="course in courses" :key="course.ID" :value="course.ID">
|
||||
{{ course.CourseName }} ({{ $format.date(course.CourseDate) }})
|
||||
</option>
|
||||
</select>
|
||||
<label class="search-checkbox-label">
|
||||
<input type="checkbox" v-model="includeClosed"> 包含已結案
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 檔案上傳面板 -->
|
||||
<div class="upload-panel" v-if="selectedCourseID">
|
||||
<div class="upload-section">
|
||||
<h3 class="section-title">步驟 1:選擇檔案</h3>
|
||||
<div class="upload-area" :class="{ dragging: isDragging }" @dragover.prevent="isDragging = true" @dragleave="isDragging = false" @drop.prevent="onFileDrop">
|
||||
<input type="file" ref="fileInput" @change="onFileSelected" accept=".xlsx" style="display: none;" :disabled="selectedFile !== null">
|
||||
<button class="btn btn-primary" @click="$refs.fileInput.click()" :disabled="selectedFile !== null">選擇 .xlsx 檔案</button>
|
||||
<span class="upload-text">或將檔案拖放到此處</span>
|
||||
<div class="file-info" v-if="selectedFile">
|
||||
<span class="file-name">📄 {{ selectedFile.name }}</span>
|
||||
<span class="file-size">({{ formatFileSize(selectedFile.size) }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 預覽與驗證面板 -->
|
||||
<div class="preview-section" v-if="previewData.length > 0">
|
||||
<h3 class="section-title">步驟 2:預覽及驗證</h3>
|
||||
|
||||
<div class="validation-summary" v-if="validationErrors.length > 0">
|
||||
<div class="error-box">
|
||||
<div class="error-title">⚠️ 發現 {{ validationErrors.length }} 個驗證錯誤</div>
|
||||
<div class="error-list">
|
||||
<div class="error-item" v-for="(error, index) in validationErrors" :key="index">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-info">
|
||||
預計匯入 {{ validRowCount }} 筆資料(共讀取 {{ previewData.length }} 列)
|
||||
</div>
|
||||
|
||||
<div class="preview-table-wrapper">
|
||||
<table class="preview-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center" style="width: 50px;">列</th>
|
||||
<th>姓名</th>
|
||||
<th>手機號</th>
|
||||
<th>身分別</th>
|
||||
<th>身分證字號</th>
|
||||
<th>餐點</th>
|
||||
<th>電子郵件</th>
|
||||
<th>失業給付認定次數</th>
|
||||
<th>性別</th>
|
||||
<th class="text-center" style="width: 60px;">狀態</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in previewData" :key="index" :class="{ 'error-row': item.errors.length > 0 }">
|
||||
<td class="text-center">{{ index + 1 }}</td>
|
||||
<td>{{ item.Name }}</td>
|
||||
<td>{{ item.Mobile }}</td>
|
||||
<td>{{ item.IDType || '未設定' }}</td>
|
||||
<td>{{ item.IDNumber || '未填' }}</td>
|
||||
<td>{{ item.MealType || '未設定' }}</td>
|
||||
<td>{{ item.Email || '未填' }}</td>
|
||||
<td>{{ item.LossJobTimes || '未填' }}</td>
|
||||
<td>{{ item.Gender || '未填' }}</td>
|
||||
<td class="text-center">
|
||||
<span class="status-badge" v-if="item.errors.length === 0" :class="{ ok: true }">✓</span>
|
||||
<span class="status-badge" v-else :class="{ error: true }">✗</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 匯入按鈕 -->
|
||||
<div class="import-section" v-if="previewData.length > 0">
|
||||
<h3 class="section-title">步驟 3:執行匯入</h3>
|
||||
<div class="import-tips">
|
||||
<div class="tip-item">ℹ️ 簽到時間和簽退時間不會匯入</div>
|
||||
<div class="tip-item">ℹ️ 報名方式均為預約</div>
|
||||
</div>
|
||||
<div class="import-options">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" v-model="clearBefore" />
|
||||
<span>先清除再匯入(刪除該課程的所有現有記錄)</span>
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-success" @click="executeImport" :disabled="validRowCount === 0 || importing">
|
||||
<span v-if="!importing">匯入 {{ validRowCount }} 筆資料</span>
|
||||
<span v-else>匯入中...</span>
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click="resetUpload">重新選擇檔案</button>
|
||||
</div>
|
||||
|
||||
<!-- 匯入結果 -->
|
||||
<div class="result-section" v-if="importResult">
|
||||
<div class="result-box" :class="{ success: importResult.success, error: !importResult.success }">
|
||||
<div class="result-title">{{ importResult.success ? '✓ 匯入完成' : '✗ 匯入失敗' }}</div>
|
||||
<div class="result-message">{{ importResult.message }}</div>
|
||||
<div class="result-details" v-if="importResult.details">
|
||||
<span>成功:{{ importResult.details.SuccessCount }} 筆</span>
|
||||
<span v-if="importResult.details.SkippedCount > 0">跳過:{{ importResult.details.SkippedCount }} 筆</span>
|
||||
<span v-if="importResult.details.ErrorCount > 0">失敗:{{ importResult.details.ErrorCount }} 筆</span>
|
||||
</div>
|
||||
<div class="skipped-records" v-if="importResult.details && importResult.details.SkippedRecords && importResult.details.SkippedRecords.length > 0">
|
||||
<div class="skipped-title">❌ 跳過的重複記錄:</div>
|
||||
<div class="skipped-list">
|
||||
<div class="skipped-item" v-for="(record, index) in importResult.details.SkippedRecords.slice(0, 10)" :key="index">
|
||||
{{ record }}
|
||||
</div>
|
||||
<div class="skipped-item" v-if="importResult.details.SkippedRecords.length > 10" style="color: #666;">
|
||||
... 還有 {{ importResult.details.SkippedRecords.length - 10 }} 筆
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-message" v-else style="padding: 40px;">
|
||||
請先選擇要匯入的課程
|
||||
</div>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
|
||||
<script src="<%= ResolveUrlWithSysVer("~/Scripts/SignUpImport.js") %>"></script>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class SignUpImport : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
// SignUpImport.aspx 現在使用 Vue.js
|
||||
// 所有邏輯都在前端和 SignUpService.asmx 處理
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<%@ Page Title="課程參加人員" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="SignUpList.aspx.cs" Inherits="報到系統.Forms.SignUpList" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
|
||||
<link href="<%= ResolveUrlWithSysVer("~/Content/SignUpList.css") %>" rel="stylesheet" type="text/css" />
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
|
||||
<div id="signUpApp" class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">課程參加人員</h1>
|
||||
</div>
|
||||
|
||||
<!-- 課程選擇面板 -->
|
||||
<div class="search-panel">
|
||||
<div class="search-row">
|
||||
<div class="search-field-inline w-6">
|
||||
<label class="search-label-inline">選擇課程</label>
|
||||
<div class="search-input-group">
|
||||
<select v-model="selectedCourseID" class="search-input" @change="onCourseSelected">
|
||||
<option value="">-- 請選擇課程 --</option>
|
||||
<option v-for="course in courses" :key="course.ID" :value="course.ID">
|
||||
{{ course.CourseName }} ({{ $format.date(course.CourseDate) }})
|
||||
</option>
|
||||
</select>
|
||||
<label class="search-checkbox-label">
|
||||
<input type="checkbox" v-model="includeClosed"> 包含已結案
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">序號</label>
|
||||
<input type="number" v-model.number="search.seqNo" class="search-input" placeholder="請輸入序號" @keyup.enter="performSearch">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">姓名搜尋</label>
|
||||
<input type="text" v-model="search.name" class="search-input" placeholder="請輸入姓名" @keyup.enter="performSearch">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">手機搜尋</label>
|
||||
<input type="text" v-model="search.mobile" class="search-input" placeholder="請輸入手機號" @keyup.enter="performSearch">
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">身分別</label>
|
||||
<select v-model="search.idType" class="search-input">
|
||||
<option value="">-- 全部 --</option>
|
||||
<option value="___EMPTY___">-- 未輸入 --</option>
|
||||
<option value="失業給付">失業給付</option>
|
||||
<option value="一般民眾">一般民眾</option>
|
||||
<option value="青年尋職">青年尋職</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">身分證字號</label>
|
||||
<div class="search-input-group">
|
||||
<input type="text" v-model="search.idNumber" class="search-input" placeholder="請輸入身分證字號" @keyup.enter="performSearch">
|
||||
<label class="search-checkbox-label">
|
||||
<input type="checkbox" v-model="search.idNumberEmpty"> 未輸入
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">餐點</label>
|
||||
<select v-model="search.mealType" class="search-input">
|
||||
<option value="">-- 全部 --</option>
|
||||
<option value="___EMPTY___">-- 未輸入 --</option>
|
||||
<option value="素食">素食</option>
|
||||
<option value="非素食">非素食</option>
|
||||
<option value="不用餐">不用餐</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="search-field-inline w-3">
|
||||
<label class="search-label-inline">電子郵件</label>
|
||||
<div class="search-input-group">
|
||||
<input type="text" v-model="search.email" class="search-input" placeholder="請輸入電子郵件" @keyup.enter="performSearch">
|
||||
<label class="search-checkbox-label">
|
||||
<input type="checkbox" v-model="search.emailEmpty"> 未輸入
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-actions">
|
||||
<button class="btn btn-secondary" @click="clearSearch">清除</button>
|
||||
<button class="btn btn-primary" @click="performSearch">搜尋</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋結果 -->
|
||||
<div class="result-panel" v-if="selectedCourseID">
|
||||
<div class="result-header">
|
||||
<span class="result-title">參加人員清單</span>
|
||||
<div class="header-actions">
|
||||
<!-- 批發簡訊功能區 -->
|
||||
<div class="sms-send-compact">
|
||||
<span class="sms-selected-count">發送簡訊已選 {{ selectedIds.length }} 人</span>
|
||||
<button class="btn btn-secondary btn-sm" @click="clearSmsSelection"
|
||||
:disabled="selectedIds.length === 0">清除已選</button>
|
||||
<button class="btn btn-primary btn-sm" @click="sendSmsBatch"
|
||||
:disabled="selectedIds.length === 0 || sendingSmsBatch">
|
||||
{{ sendingSmsBatch ? '發送中...' : '發送簡訊' }}
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-sm" @click="checkBalance">查詢餘額</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center" style="width: 50px;">
|
||||
<input type="checkbox" v-model="selectAllCurrentPage" @change="toggleSelectAll" :indeterminate="hasIndeterminate">
|
||||
</th>
|
||||
<th class="text-center" style="width: 50px;">序號</th>
|
||||
<th class="text-center">姓名</th>
|
||||
<th class="text-center">性別</th>
|
||||
<th class="text-center">手機號</th>
|
||||
<th class="text-center">身分別</th>
|
||||
<th class="text-center">身分證字號</th>
|
||||
<th class="text-center">餐點</th>
|
||||
<th class="text-center">電子郵件</th>
|
||||
<th class="text-center">失業給付認定次數</th>
|
||||
<th class="text-center">報名方式</th>
|
||||
<th class="text-center">簡訊發送</th>
|
||||
<th class="text-center">簽到時間</th>
|
||||
<th class="text-center">簽退時間</th>
|
||||
<th class="text-center">操作
|
||||
<button class="btn btn-success btn-sm" style="margin-left: 10px;" @click="openAddModal">新增</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="loading">
|
||||
<td colspan="15" class="empty-message">載入中...</td>
|
||||
</tr>
|
||||
<tr v-else-if="signups.length === 0">
|
||||
<td colspan="15" class="empty-message">查無資料</td>
|
||||
</tr>
|
||||
<tr v-else v-for="item in signups" :key="item.ID">
|
||||
<td class="text-center">
|
||||
<input type="checkbox" :value="item.ID" v-model="selectedIds" @change="updateSelectAllState">
|
||||
</td>
|
||||
<td class="text-center">{{ item.SeqNo }}</td>
|
||||
<td>{{ item.Name }}</td>
|
||||
<td class="text-center">{{ item.Gender || '未設定' }}</td>
|
||||
<td class="text-center">{{ item.Mobile }}</td>
|
||||
<td class="text-center">{{ item.IDType || '未設定' }}</td>
|
||||
<td class="text-center">{{ item.IDNumber || '未填' }}</td>
|
||||
<td class="text-center">{{ item.MealType || '未設定' }}</td>
|
||||
<td class="text-center">{{ item.Email || '未填' }}</td>
|
||||
<td class="text-center">{{ item.LossJobTimes !== null && item.LossJobTimes !== undefined ? item.LossJobTimes : '未填' }}</td>
|
||||
<td class="text-center">{{ item.SignUpType || '未設定' }}</td>
|
||||
<td class="text-center">{{ item.MessageSending }}</td>
|
||||
<td class="text-center">{{ $format.dateTime(item.CheckInTime) }}</td>
|
||||
<td class="text-center">{{ $format.dateTime(item.CheckOutTime) }}</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-primary btn-sm" @click="openEditModal(item)">更正</button>
|
||||
<button class="btn btn-danger btn-sm" @click="deleteSignUp(item)">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分頁 -->
|
||||
<div class="pagination" v-if="totalCount > 0">
|
||||
<div class="pagination-control">
|
||||
<button class="btn btn-secondary btn-sm" :disabled="currentPage === 1" @click="goToFirstPage">第一頁</button>
|
||||
<button class="btn btn-secondary btn-sm" :disabled="currentPage === 1" @click="goToPage(currentPage - 1)">上一頁</button>
|
||||
<span class="pagination-label">第</span>
|
||||
<select v-model.number="currentPage" class="pagination-select" @change="loadSignUps">
|
||||
<option v-for="page in totalPages" :key="page" :value="page">{{ page }}</option>
|
||||
</select>
|
||||
<span class="pagination-label">頁</span>
|
||||
<button class="btn btn-secondary btn-sm" :disabled="currentPage === totalPages" @click="goToPage(currentPage + 1)">下一頁</button>
|
||||
<button class="btn btn-secondary btn-sm" :disabled="currentPage === totalPages" @click="goToLastPage">最後頁</button>
|
||||
<span class="pagination-label">|</span>
|
||||
<span class="pagination-label">每頁</span>
|
||||
<select v-model.number="pageSize" class="pagination-select" @change="onPageSizeChanged">
|
||||
<option :value="10">10</option>
|
||||
<option :value="20">20</option>
|
||||
<option :value="50">50</option>
|
||||
<option :value="100">100</option>
|
||||
</select>
|
||||
<span class="pagination-label">筆,共</span>
|
||||
<span class="pagination-count">{{ totalCount }}</span>
|
||||
<span class="pagination-label">筆</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-panel" v-else>
|
||||
<div class="empty-message" style="padding: 40px;">請先選擇要查詢的課程</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增/更正彈窗 -->
|
||||
<div class="modal" :class="{ show: showModal }">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">{{ isNewMode ? '新增參加人員' : '更正參加人員' }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- 第一行:姓名、手機號 -->
|
||||
<div class="form-row">
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline required">姓名</label>
|
||||
<input type="text" v-model="editItem.Name" class="form-control" maxlength="100" placeholder="請輸入姓名">
|
||||
</div>
|
||||
<div class="error-message" v-if="validationErrors.Name">{{ validationErrors.Name }}</div>
|
||||
</div>
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline required">手機號</label>
|
||||
<input type="text" v-model="editItem.Mobile" class="form-control" maxlength="20" placeholder="請輸入手機號">
|
||||
</div>
|
||||
<div class="error-message" v-if="validationErrors.Mobile">{{ validationErrors.Mobile }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第二行:身分別、身分證字號 -->
|
||||
<div class="form-row">
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">身分別</label>
|
||||
<select v-model="editItem.IDType" class="form-control">
|
||||
<option value="">-- 請選擇 --</option>
|
||||
<option value="失業給付">失業給付</option>
|
||||
<option value="一般民眾">一般民眾</option>
|
||||
<option value="青年尋職">青年尋職</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">身分證字號</label>
|
||||
<input type="text" v-model="editItem.IDNumber" class="form-control" maxlength="20" placeholder="請輸入身分證字號">
|
||||
</div>
|
||||
<div class="error-message" v-if="validationErrors.IDNumber">{{ validationErrors.IDNumber }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第三行:餐點、報名方式 -->
|
||||
<div class="form-row">
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">餐點</label>
|
||||
<select v-model="editItem.MealType" class="form-control">
|
||||
<option value="">-- 請選擇 --</option>
|
||||
<option value="素食">素食</option>
|
||||
<option value="非素食">非素食</option>
|
||||
<option value="不用餐">不用餐</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">報名方式</label>
|
||||
<select v-model="editItem.SignUpType" class="form-control">
|
||||
<option value="">-- 請選擇 --</option>
|
||||
<option value="預約">預約</option>
|
||||
<option value="現場">現場</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第四行:電子郵件 -->
|
||||
<div class="form-row">
|
||||
<div class="form-col-100">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">電子郵件</label>
|
||||
<input type="text" v-model="editItem.Email" class="form-control" maxlength="100" placeholder="請輸入電子郵件">
|
||||
</div>
|
||||
<div class="error-message" v-if="validationErrors.Email">{{ validationErrors.Email }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第五行:性別、失業給付認定次數 -->
|
||||
<div class="form-row">
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">性別</label>
|
||||
<select v-model="editItem.Gender" class="form-control">
|
||||
<option value="">-- 請選擇 --</option>
|
||||
<option value="男">男</option>
|
||||
<option value="女">女</option>
|
||||
<option value="其他">其他</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-col-50">
|
||||
<div class="form-group-inline">
|
||||
<label class="form-label-inline">失業給付認定次數</label>
|
||||
<input type="number" v-model.number="editItem.LossJobTimes" class="form-control" min="0" placeholder="請輸入次數">
|
||||
</div>
|
||||
<div class="error-message" v-if="validationErrors.LossJobTimes">{{ validationErrors.LossJobTimes }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="closeModal">取消</button>
|
||||
<button class="btn btn-primary" @click="saveSignUp">存檔</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="script" runat="server">
|
||||
<script src="<%= ResolveUrlWithSysVer("~/Scripts/SignUpList.js") %>"></script>
|
||||
</asp:Content>
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace 報到系統.Forms
|
||||
{
|
||||
public partial class SignUpList : PageBase
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
// SignUpList.aspx 現在使用 Vue.js
|
||||
// 所有邏輯都在前端和 SignUpService.asmx 處理
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user