// ======================================== // 活動管理 - Vue.js 應用程式 // 檔案位置:Scripts/ClassesList.js // ======================================== (() => { 'use strict'; new Vue({ el: '#classesApp', data: { // API apiName: 'ClassesService', // 搜尋條件 search: { courseCode: '', courseName: '', courseDate1: '', courseDate2: '', courseLocation: '', projectID: '', projectName: '', closed: '' }, // 搜尋結果 classes: [], loading: false, // 分頁 currentPage: 1, pageSize: 20, totalCount: 0, // 專案自動完成 projectSuggestions: [], showProjectSuggestions: false, projectSearchTimeout: null, // 彈窗 showModal: false, isNewMode: true, editItem: { ID: 0, CourseCode: '', CourseName: '', CourseDate: '', CourseLocation: '', StartTime: '', EndTime: '', Teacher: '', projectID: '', projectName: '', NotificationTitle: '', NotificationTemplate: '', AgreedTerms: '', Closed: false, DB_APPNO: 0 }, // 編輯模式專案自動完成 editProjectSuggestions: [], showEditProjectSuggestions: false, editProjectSearchTimeout: null, // 驗證錯誤訊息 validationErrors: { CourseName: '', CourseDate: '', StartTime: '', EndTime: '', Teacher: '', CourseLocation: '', NotificationTitle: '', NotificationTemplate: '' } }, computed: { totalPages() { return Math.ceil(this.totalCount / this.pageSize); }, generatePreview() { if (!this.editItem.NotificationTemplate) { return ''; } let preview = this.editItem.NotificationTemplate; // 替換模板變數 preview = preview.replace(/{課程日期}/g, this.editItem.CourseDate ? this.editItem.CourseDate : '(課程日期未設定)'); preview = preview.replace(/{課程名稱}/g, this.editItem.CourseName || '(課程名稱未設定)'); preview = preview.replace(/{課程地點}/g, this.editItem.CourseLocation || '(課程地點未設定)'); preview = preview.replace(/{開始時間}/g, this.editItem.StartTime || '(開始時間未設定)'); preview = preview.replace(/{結束時間}/g, this.editItem.EndTime || '(結束時間未設定)'); preview = preview.replace(/{講師}/g, this.editItem.Teacher || '(講師未設定)'); preview = preview.replace(/{學員姓名}/g, '(學員姓名)'); return preview; } }, mounted() { console.log('[ClassesList] Vue app mounted'); // 初始載入資料 this.performSearch(); }, methods: { /** * 執行搜尋 */ async performSearch() { this.currentPage = 1; await this.loadClasses(); }, /** * 載入活動資料 */ async loadClasses() { this.loading = true; try { const response = await this.$api.call(this.apiName, 'SearchClasses', { pageIndex: this.currentPage, pageSize: this.pageSize, courseCode: this.search.courseCode, courseName: this.search.courseName, courseDate1: this.search.courseDate1 || null, courseDate2: this.search.courseDate2 || null, courseLocation: this.search.courseLocation, projectID: this.search.projectID, closed: this.search.closed }); if (response.Success) { this.classes = (response.Data || []); this.totalCount = response.TotalCount || 0; console.log('[ClassesList] 載入成功,共 ' + this.totalCount + ' 筆'); } else { this.$message.error(response.Message || '查詢失敗'); this.classes = []; this.totalCount = 0; } } catch (error) { console.error('[ClassesList] 載入錯誤:', error); this.$message.error('載入資料發生錯誤'); this.classes = []; this.totalCount = 0; } finally { this.loading = false; } }, /** * 清除搜尋條件 */ clearSearch() { this.search = { courseCode: '', courseName: '', courseDate1: '', courseDate2: '', courseLocation: '', projectID: '', projectName: '', closed: '' }; this.projectSuggestions = []; this.showProjectSuggestions = false; this.performSearch(); }, /** * 換頁 */ goToPage(page) { if (page < 1 || page > this.totalPages) return; this.currentPage = page; this.loadClasses(); }, /** * 格式化專案資訊 (編號 - 名稱) */ formatProjectInfo(item) { if (!item.ProjectID) return ''; return item.ProjectName ? `${item.ProjectID} - ${item.ProjectName}` : item.ProjectID; }, /** * 開啟新增彈窗 */ openAddModal() { this.isNewMode = true; this.editItem = { ID: 0, CourseCode: '(自動產生)', CourseName: '', CourseDate: '', CourseLocation: '', StartTime: '', EndTime: '', Teacher: '', projectID: '', projectName: '', NotificationTitle: '', NotificationTemplate: '', AgreedTerms: '', Closed: false, DB_APPNO: 0 }; this.editProjectSuggestions = []; this.showEditProjectSuggestions = false; this.clearValidationErrors(); this.showModal = true; }, /** * 開啟更正彈窗 */ openEditModal(item) { this.isNewMode = false; const projectDisplay = item.ProjectID && item.ProjectName ? `${item.ProjectID} - ${item.ProjectName}` : (item.ProjectID || ''); this.editItem = { ID: item.ID, CourseCode: item.CourseCode, CourseName: item.CourseName, CourseDate: this.$format.date(item.CourseDate), CourseLocation: item.CourseLocation || '', StartTime: item.StartTime || '', EndTime: item.EndTime || '', Teacher: item.Teacher || '', projectID: item.ProjectID || '', projectName: projectDisplay, NotificationTitle: item.NotificationTitle || '', NotificationTemplate: item.NotificationTemplate || '', AgreedTerms: item.AgreedTerms || '', Closed: item.Closed, DB_APPNO: item.DB_APPNO }; this.editProjectSuggestions = []; this.showEditProjectSuggestions = false; this.clearValidationErrors(); this.showModal = true; }, /** * 關閉彈窗 */ closeModal() { this.showModal = false; }, /** * 插入模板文字 */ insertTemplate(text) { const textarea = this.$refs.templateTextarea; if (!textarea) return; const startPos = textarea.selectionStart; const endPos = textarea.selectionEnd; const currentValue = this.editItem.NotificationTemplate || ''; // 在游標位置插入文字 this.editItem.NotificationTemplate = currentValue.substring(0, startPos) + text + currentValue.substring(endPos); // 設定游標位置 this.$nextTick(() => { textarea.selectionStart = textarea.selectionEnd = startPos + text.length; textarea.focus(); }); }, /** * 清除驗證錯誤訊息 */ clearValidationErrors() { this.validationErrors = { CourseName: '', CourseDate: '', StartTime: '', EndTime: '', Teacher: '', CourseLocation: '', NotificationTitle: '', NotificationTemplate: '' }; }, /** * 搜尋專案 (自動完成) */ searchProjects() { // 清除之前的超時 if (this.projectSearchTimeout) { clearTimeout(this.projectSearchTimeout); } const keyword = this.search.projectName.trim(); // 如果輸入為空,清空建議和ProjectID if (!keyword) { this.projectSuggestions = []; this.search.projectID = ''; return; } // 延遲搜尋以避免過多API呼叫 this.projectSearchTimeout = setTimeout(async () => { try { const response = await this.$api.call(this.apiName, 'SearchProjects', { keyword: keyword }); if (response.Success) { this.projectSuggestions = response.Data || []; this.showProjectSuggestions = this.projectSuggestions.length > 0; } else { this.projectSuggestions = []; this.showProjectSuggestions = false; } } catch (error) { console.error('[ClassesList] 搜尋專案錯誤:', error); this.projectSuggestions = []; this.showProjectSuggestions = false; } }, 300); }, /** * 選擇專案 */ selectProject(suggestion) { this.search.projectID = suggestion.Value; this.search.projectName = `${suggestion.Value} - ${suggestion.Name}`; this.projectSuggestions = []; this.showProjectSuggestions = false; }, /** * 隱藏專案建議 (延遲隱藏以允許點擊) */ hideProjectSuggestions() { setTimeout(() => { this.showProjectSuggestions = false; }, 200); }, /** * 搜尋編輯模式的專案 (自動完成) */ searchEditProjectSuggestions() { // 清除之前的超時 if (this.editProjectSearchTimeout) { clearTimeout(this.editProjectSearchTimeout); } const keyword = this.editItem.projectName.trim(); // 如果輸入為空,清空建議和ProjectID if (!keyword) { this.editProjectSuggestions = []; this.editItem.projectID = ''; return; } // 延遲搜尋以避免過多API呼叫 this.editProjectSearchTimeout = setTimeout(async () => { try { const response = await this.$api.call(this.apiName, 'SearchProjects', { keyword: keyword }); if (response.Success) { this.editProjectSuggestions = response.Data || []; this.showEditProjectSuggestions = this.editProjectSuggestions.length > 0; } else { this.editProjectSuggestions = []; this.showEditProjectSuggestions = false; } } catch (error) { console.error('[ClassesList] 搜尋編輯模式專案錯誤:', error); this.editProjectSuggestions = []; this.showEditProjectSuggestions = false; } }, 300); }, /** * 選擇編輯模式的專案 */ selectEditProject(suggestion) { this.editItem.projectID = suggestion.Value; this.editItem.projectName = `${suggestion.Value} - ${suggestion.Name}`; this.editProjectSuggestions = []; this.showEditProjectSuggestions = false; }, /** * 隱藏編輯模式的專案建議 (延遲隱藏以允許點擊) */ hideEditProjectSuggestions() { setTimeout(() => { this.showEditProjectSuggestions = false; }, 200); }, /** * 驗證輸入 */ validateInput() { this.clearValidationErrors(); let isValid = true; // 驗證課程名稱 if (!this.editItem.CourseName || this.editItem.CourseName.trim() === '') { this.validationErrors.CourseName = '課程名稱為必填欄位'; isValid = false; } else if (this.editItem.CourseName.length > 100) { this.validationErrors.CourseName = '課程名稱最長 100 個字元'; isValid = false; } // 驗證課程日期 if (!this.editItem.CourseDate) { this.validationErrors.CourseDate = '課程日期為必填欄位'; isValid = false; } // 驗證開始時間 if (!this.editItem.StartTime || this.editItem.StartTime.trim() === '') { this.validationErrors.StartTime = '開始時間為必填欄位'; isValid = false; } // 驗證結束時間 if (!this.editItem.EndTime || this.editItem.EndTime.trim() === '') { this.validationErrors.EndTime = '結束時間為必填欄位'; isValid = false; } // 驗證講師 if (!this.editItem.Teacher || this.editItem.Teacher.trim() === '') { this.validationErrors.Teacher = '講師為必填欄位'; isValid = false; } // 驗證通知標題 if (this.editItem.NotificationTitle && this.editItem.NotificationTitle.length > 100) { this.validationErrors.NotificationTitle = '通知標題最長 100 個字元'; isValid = false; } // 驗證通知模板 if (this.editItem.NotificationTemplate && this.editItem.NotificationTemplate.length > 500) { this.validationErrors.NotificationTemplate = '通知模板最長 500 個字元'; isValid = false; } return isValid; }, /** * 儲存活動 */ async saveClasses() { if (!this.validateInput()) return; try { // 準備資料 const item = { ID: this.editItem.ID, CourseCode: this.editItem.CourseCode, CourseName: this.editItem.CourseName.trim(), CourseDate: this.editItem.CourseDate, CourseLocation: this.editItem.CourseLocation, StartTime: this.editItem.StartTime, EndTime: this.editItem.EndTime, Teacher: this.editItem.Teacher, ProjectID: this.editItem.projectID, NotificationTitle: this.editItem.NotificationTitle, NotificationTemplate: this.editItem.NotificationTemplate, AgreedTerms: this.editItem.AgreedTerms, Closed: this.editItem.Closed, DB_APPNO: this.editItem.DB_APPNO }; const response = await this.$api.call(this.apiName, 'SaveClasses', { isNew: this.isNewMode, item: item }); if (response.Success) { this.$message.success(response.Message || '儲存成功'); this.closeModal(); this.loadClasses(); } else { this.$message.error(response.Message || '儲存失敗'); } } catch (error) { console.error('[ClassesList] 儲存錯誤:', error); this.$message.error('儲存發生錯誤'); } }, /** * 儲存後繼續新增 */ async saveAndContinue() { if (!this.validateInput()) return; try { // 準備資料 const item = { ID: 0, CourseCode: '', CourseName: this.editItem.CourseName.trim(), CourseDate: this.editItem.CourseDate, CourseLocation: this.editItem.CourseLocation, StartTime: this.editItem.StartTime, EndTime: this.editItem.EndTime, Teacher: this.editItem.Teacher, ProjectID: this.editItem.projectID, NotificationTitle: this.editItem.NotificationTitle, NotificationTemplate: this.editItem.NotificationTemplate, AgreedTerms: this.editItem.AgreedTerms, Closed: this.editItem.Closed, DB_APPNO: 0 }; const response = await this.$api.call(this.apiName, 'SaveClasses', { isNew: true, item: item }); if (response.Success) { this.$message.success(response.Message || '儲存成功'); // 清除輸入內容,重新進入新增模式 this.editItem = { ID: 0, CourseCode: '(自動產生)', CourseName: '', CourseDate: '', CourseLocation: '', StartTime: '', EndTime: '', Teacher: '', projectID: '', projectName: '', NotificationTitle: '', NotificationTemplate: '', AgreedTerms: '', Closed: false, DB_APPNO: 0 }; this.loadClasses(); } else { this.$message.error(response.Message || '儲存失敗'); } } catch (error) { console.error('[ClassesList] 儲存錯誤:', error); this.$message.error('儲存發生錯誤'); } }, /** * 刪除活動 */ async deleteClasses(item) { if (!confirm(`確定要刪除活動「${item.CourseName}」嗎?`)) { return; } try { const response = await this.$api.call(this.apiName, 'DeleteClasses', { id: item.ID, dbAppNo: item.DB_APPNO }); if (response.Success) { this.$message.success(response.Message || '刪除成功'); this.loadClasses(); } else { this.$message.error(response.Message || '刪除失敗'); } } catch (error) { console.error('[ClassesList] 刪除錯誤:', error); this.$message.error('刪除發生錯誤'); } }, /** * 匯出活動 */ async performExport() { try { // 驗證必要條件:課程日期(起)和課程日期(迄)為必填 if (!this.search.courseDate1 || !this.search.courseDate2) { this.$message.warning('課程日期(起)和課程日期(迄)為必填欄位'); return; } const response = await this.$api.call(this.apiName, 'ExportClassesList', { courseDate1: this.search.courseDate1 || null, courseDate2: this.search.courseDate2 || null, projectID: this.search.projectID || null }); if (response.Success) { // 取得檔案資訊 const fileData = response.Data; const fileName = fileData.FileName; const fileBase64 = fileData.FileBase64; const fileSize = fileData.FileSize; // 轉換 Base64 為 Blob const binaryString = atob(fileBase64); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } const blob = new Blob([bytes], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); // 建立下載連結 const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = fileName; // 執行下載 document.body.appendChild(link); link.click(); // 清理資源 document.body.removeChild(link); window.URL.revokeObjectURL(url); this.$message.success(`匯出成功 (${(fileSize / 1024).toFixed(2)} KB)`); console.log('[ClassesList] 匯出成功:', fileName); } else { this.$message.error(response.Message || '匯出失敗'); } } catch (error) { console.error('[ClassesList] 匯出錯誤:', error); this.$message.error('匯出發生錯誤'); } } } }); })();