276 lines
17 KiB
Plaintext
276 lines
17 KiB
Plaintext
<%@ 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>
|