chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore - 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
// 課程報表 - Vue.js 應用程式
|
||||
// 檔案位置:Scripts/ClassesReport.js
|
||||
// ========================================
|
||||
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
new Vue({
|
||||
el: '#classesReportApp',
|
||||
data: {
|
||||
// API
|
||||
apiName: 'ClassesService',
|
||||
// 搜尋條件
|
||||
search: {
|
||||
courseCode: '',
|
||||
courseName: '',
|
||||
courseDate1: '',
|
||||
courseDate2: '',
|
||||
courseLocation: '',
|
||||
projectID: '',
|
||||
projectName: '',
|
||||
closed: '',
|
||||
timeFormat: 'datetime'
|
||||
},
|
||||
// 搜尋結果
|
||||
classes: [],
|
||||
loading: false,
|
||||
// 分頁
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
totalCount: 0,
|
||||
// 專案自動完成
|
||||
projectSuggestions: [],
|
||||
showProjectSuggestions: false,
|
||||
projectSearchTimeout: null,
|
||||
// 匯出
|
||||
exporting: false,
|
||||
exportingId: 0
|
||||
},
|
||||
computed: {
|
||||
totalPages() {
|
||||
return Math.ceil(this.totalCount / this.pageSize);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('[ClassesReport] Vue app mounted');
|
||||
// 初始載入資料
|
||||
this.performSearch();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 執行搜尋
|
||||
*/
|
||||
async performSearch() {
|
||||
this.currentPage = 1;
|
||||
await this.fetchClasses();
|
||||
},
|
||||
|
||||
/**
|
||||
* 查詢課程列表
|
||||
*/
|
||||
async fetchClasses() {
|
||||
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 === '' ? null : this.search.closed === 'true'
|
||||
});
|
||||
|
||||
if (response.Success) {
|
||||
this.classes = response.Data || [];
|
||||
this.totalCount = response.TotalCount || 0;
|
||||
console.log('[ClassesReport] 載入成功,共 ' + this.totalCount + ' 筆');
|
||||
} else {
|
||||
this.$message.error(response.Message || '查詢失敗');
|
||||
this.classes = [];
|
||||
this.totalCount = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ClassesReport] 載入錯誤:', error);
|
||||
this.$message.error('載入資料發生錯誤');
|
||||
this.classes = [];
|
||||
this.totalCount = 0;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除搜尋條件
|
||||
*/
|
||||
clearSearch() {
|
||||
this.search = {
|
||||
courseCode: '',
|
||||
courseName: '',
|
||||
courseDate1: '',
|
||||
courseDate2: '',
|
||||
courseLocation: '',
|
||||
projectID: '',
|
||||
projectName: '',
|
||||
closed: '',
|
||||
timeFormat: 'datetime'
|
||||
};
|
||||
this.performSearch();
|
||||
},
|
||||
|
||||
/**
|
||||
* 搜尋專案
|
||||
*/
|
||||
searchProjects(event) {
|
||||
const query = this.search.projectName.trim();
|
||||
|
||||
if (query.length === 0) {
|
||||
this.projectSuggestions = [];
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(this.projectSearchTimeout);
|
||||
this.projectSearchTimeout = setTimeout(async () => {
|
||||
try {
|
||||
const response = await this.$api.call(this.apiName, 'SearchProjects', {
|
||||
keyword: query
|
||||
});
|
||||
|
||||
if (response.Success) {
|
||||
this.projectSuggestions = response.Data || [];
|
||||
} else {
|
||||
this.projectSuggestions = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ClassesReport] 搜尋專案出錯:', error);
|
||||
this.projectSuggestions = [];
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
|
||||
/**
|
||||
* 選擇專案
|
||||
*/
|
||||
selectProject(project) {
|
||||
this.search.projectID = project.Value;
|
||||
this.search.projectName = project.Value + ' ' + project.Name;
|
||||
this.projectSuggestions = [];
|
||||
this.showProjectSuggestions = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 隱藏專案建議
|
||||
*/
|
||||
hideProjectSuggestions() {
|
||||
setTimeout(() => {
|
||||
this.showProjectSuggestions = false;
|
||||
}, 200);
|
||||
},
|
||||
|
||||
/**
|
||||
* 上一頁
|
||||
*/
|
||||
previousPage() {
|
||||
if (this.currentPage > 1) {
|
||||
this.currentPage--;
|
||||
this.fetchClasses();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 下一頁
|
||||
*/
|
||||
nextPage() {
|
||||
if (this.currentPage < this.totalPages) {
|
||||
this.currentPage++;
|
||||
this.fetchClasses();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 匯出報表
|
||||
*/
|
||||
async exportReport(courseID) {
|
||||
if (this.exporting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.exporting = true;
|
||||
this.exportingId = courseID;
|
||||
|
||||
try {
|
||||
const response = await this.$api.call(this.apiName, 'ExportClassesReport', {
|
||||
classesID: courseID,
|
||||
timeFormat: this.search.timeFormat
|
||||
});
|
||||
|
||||
if (response.Success) {
|
||||
// 下載檔案
|
||||
const fileBase64 = response.Data.FileBase64;
|
||||
const fileName = response.Data.FileName;
|
||||
|
||||
const byteCharacters = atob(fileBase64);
|
||||
const byteNumbers = new Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
const blob = new Blob([byteArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
||||
|
||||
// 建立下載連結
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = fileName;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
this.$message.success('匯出成功');
|
||||
} else {
|
||||
this.$message.error('匯出失敗:' + response.Message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ClassesReport] 匯出報表出錯:', error);
|
||||
this.$message.error('匯出報表出錯:' + error.message);
|
||||
} finally {
|
||||
this.exporting = false;
|
||||
this.exportingId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 呼叫後端 WebService
|
||||
*/
|
||||
async callWebService(methodName, params) {
|
||||
const url = `${this.apiName}.asmx/${methodName}`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
};
|
||||
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user