chore: 首次簽入 Thinkyu ASP.NET 專案

- 加入 Visual Studio / ASP.NET .gitignore
- 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
This commit is contained in:
2026-09-10 09:42:37 +08:00
commit 577060bc78
2496 changed files with 501389 additions and 0 deletions
+157
View File
@@ -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>