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

158 lines
8.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<%@ 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>