Files
thinkyu/教育訓練系統/Web/Scripts/財稅作業與領據關帳.js
sryang 577060bc78 chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore
- 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
2026-09-10 09:42:37 +08:00

642 lines
28 KiB
JavaScript

let taxReceiptApp = null;
function CreateTaxReceiptApp() {
const loadMarker = document.getElementById('taxReceiptApp');
if (loadMarker) {
DoCreateTaxReceiptApp();
}
}
function DoCreateTaxReceiptApp() {
taxReceiptApp = new Vue({
el: '#taxReceiptApp',
mixins: [ToastMixin, ApiMixin],
data: {
activeTab: 0,
currentYear: '',
currentClosingMonth: '',
newClosingMonth: '',
loading: false,
message: '',
messageType: 'info',
summaryTable: {
startDate: '',
endDate: '',
identity: '',
paymentMethod: '',
identityOptions: [],
paymentMethodOptions: [],
dataList: [],
loading: false,
message: '',
messageType: 'info'
},
detailTable: {
startDate: '',
endDate: '',
identity: '',
certificateType: '',
identityOptions: [],
certificateTypeOptions: [],
dataList: [],
selectAll: false,
senderEmail: '',
emailSubject: '',
emailBody: '',
loading: false,
message: '',
messageType: 'info',
emailMessage: '',
emailMessageType: 'info',
previewData: [],
showEmailPreview: false
}
},
created() {
this.initializeData();
this.loadSummaryTableDropdowns();
this.loadDetailTableDropdowns();
// 確保預覽窗口初始為關閉狀態
this.detailTable.showEmailPreview = false;
this.detailTable.previewData = [];
},
mounted() {
this.loadClosingMonthData();
// 再次確保預覽窗口初始為關閉狀態
this.$nextTick(() => {
this.detailTable.showEmailPreview = false;
this.detailTable.previewData = [];
});
},
methods: {
initializeData() {
// 取得現行年度
const now = new Date();
this.currentYear = now.getFullYear().toString();
},
async loadClosingMonthData() {
try {
const result = await this.callApiWithErrorMsg(
'領據處理',
'GetClosingMonth',
{},
'載入關帳年月資料'
);
if (result && result.Data) {
this.currentClosingMonth = result.Data.currentClosingMonth || '';
// 初始化新關帳年月為目前月份
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
this.newClosingMonth = `${year}-${month}`;
}
} catch (error) {
console.error('載入關帳年月失敗:', error);
this.showMessage('載入關帳年月失敗: ' + (error.message || '請檢查網路連線'), 'danger');
}
},
validateMonth() {
if (!this.newClosingMonth) {
return;
}
// 驗證格式 YYYY-MM
const pattern = /^\d{4}-\d{2}$/;
if (!pattern.test(this.newClosingMonth)) {
this.showMessage('年月格式不正確,請使用 YYYY-MM 格式', 'danger');
this.newClosingMonth = '';
return;
}
// 驗證年份
const [year, month] = this.newClosingMonth.split('-');
const yearNum = parseInt(year);
const monthNum = parseInt(month);
// 月份範圍驗證
if (monthNum < 1 || monthNum > 12) {
this.showMessage('月份必須介於 01 到 12 之間', 'danger');
this.newClosingMonth = '';
return;
}
// 年份不能超過現在
const currentYear = new Date().getFullYear();
if (yearNum > currentYear) {
this.showMessage('年份不能超過現在', 'danger');
this.newClosingMonth = '';
return;
}
},
async saveClosingMonth() {
if (!this.newClosingMonth) {
this.showMessage('請輸入調整的關帳年月', 'warning');
return;
}
// 再次驗證
this.validateMonth();
if (!this.newClosingMonth) {
return;
}
// 確認操作
if (!confirm(`確認要將領據關帳年月調整為 ${this.newClosingMonth} 嗎?\n此操作將影響領據的修改權限。`)) {
return;
}
this.loading = true;
try {
const params = {
closingMonth: this.newClosingMonth
};
const result = await this.callApiWithErrorMsg(
'領據處理',
'UpdateClosingMonth',
params,
'保存關帳年月'
);
if (result && result.Success) {
this.showMessage('關帳年月已成功更新', 'success');
this.currentClosingMonth = this.newClosingMonth;
// 清除輸入欄位
setTimeout(() => {
this.clearForm();
}, 1500);
} else {
this.showMessage(result.Message || '保存失敗,請稍後重試', 'danger');
}
} catch (error) {
console.error('保存失敗:', error);
this.showMessage('保存失敗: ' + (error.message || '請檢查網路連線'), 'danger');
} finally {
this.loading = false;
}
},
clearForm() {
this.newClosingMonth = '';
this.message = '';
},
showMessage(text, type = 'info') {
this.message = text;
this.messageType = type;
// 3 秒後自動隱藏訊息
if (type === 'success' || type === 'info') {
setTimeout(() => {
this.message = '';
}, 3000);
}
},
async loadSummaryTableDropdowns() {
try {
// 加載身分別選項
const identityResult = await this.callApiWithErrorMsg(
'領據處理',
'GetDropdownLists',
{ key: '身分別' },
'載入身分別選項'
);
if (identityResult && identityResult.Data) {
this.summaryTable.identityOptions = identityResult.Data.filter(item => item.Value !== '');
}
// 加載付款方式選項
const paymentResult = await this.callApiWithErrorMsg(
'領據處理',
'GetDropdownLists',
{ key: '付款方式' },
'載入付款方式選項'
);
if (paymentResult && paymentResult.Data) {
this.summaryTable.paymentMethodOptions = paymentResult.Data.filter(item => item.Value !== '');
}
} catch (error) {
console.error('加載下拉清單失敗:', error);
}
},
async searchSummaryTable() {
this.summaryTable.loading = true;
this.summaryTable.message = '';
this.summaryTable.dataList = [];
try {
const params = {
起日: this.summaryTable.startDate ? this.parseDate(this.summaryTable.startDate) : null,
迄日: this.summaryTable.endDate ? this.parseDate(this.summaryTable.endDate) : null,
身分別: this.summaryTable.identity || null,
付款方式: this.summaryTable.paymentMethod || null,
輸出方式: 'DATA'
};
const result = await this.callApiWithErrorMsg(
'領據處理',
'SearchSummaryTable',
params,
'查詢個人費用彙整表'
);
if (result && result.Success) {
this.summaryTable.dataList = result.Data || [];
if (this.summaryTable.dataList.length === 0) {
this.summaryTable.message = '查詢完成,但未找到符合條件的資料';
this.summaryTable.messageType = 'info';
} else {
this.summaryTable.message = `查詢完成,共找到 ${this.summaryTable.dataList.length} 筆資料`;
this.summaryTable.messageType = 'success';
}
} else {
this.summaryTable.message = result.Message || '查詢失敗,請稍後重試';
this.summaryTable.messageType = 'danger';
}
} catch (error) {
console.error('查詢失敗:', error);
this.summaryTable.message = '查詢失敗: ' + (error.message || '請檢查網路連線');
this.summaryTable.messageType = 'danger';
} finally {
this.summaryTable.loading = false;
}
},
async exportSummaryTable() {
if (this.summaryTable.dataList.length === 0) {
this.summaryTable.message = '沒有資料可以匯出,請先執行查詢';
this.summaryTable.messageType = 'warning';
return;
}
this.summaryTable.loading = true;
this.summaryTable.message = '';
try {
const params = {
起日: this.summaryTable.startDate ? this.parseDate(this.summaryTable.startDate) : null,
迄日: this.summaryTable.endDate ? this.parseDate(this.summaryTable.endDate) : null,
身分別: this.summaryTable.identity || null,
付款方式: this.summaryTable.paymentMethod || null,
輸出方式: 'EXPORT'
};
const result = await this.callApiWithErrorMsg(
'領據處理',
'SearchSummaryTable',
params,
'匯出個人費用彙整表'
);
if (result && result.Success && result.Data) {
// 下載 Excel 檔案
const excelData = result.Data;
if (excelData.excelData && excelData.fileName) {
this.downloadExcel(excelData.excelData, excelData.fileName);
this.summaryTable.message = '匯出成功';
this.summaryTable.messageType = 'success';
} else {
this.summaryTable.message = '匯出失敗:無法取得檔案';
this.summaryTable.messageType = 'danger';
}
} else {
this.summaryTable.message = result.Message || '匯出失敗,請稍後重試';
this.summaryTable.messageType = 'danger';
}
} catch (error) {
console.error('匯出失敗:', error);
this.summaryTable.message = '匯出失敗: ' + (error.message || '請檢查網路連線');
this.summaryTable.messageType = 'danger';
} finally {
this.summaryTable.loading = false;
}
},
parseDate(dateString) {
if (!dateString) return null;
const date = new Date(dateString);
return date.toISOString().split('T')[0];
},
formatCurrency(value) {
if (!value) return '0';
return new Intl.NumberFormat('zh-TW', {
style: 'currency',
currency: 'TWD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(value);
},
downloadExcel(excelBase64, fileName) {
const binaryString = atob(excelBase64);
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();
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
},
async loadDetailTableDropdowns() {
try {
// 加載身分別選項
const identityResult = await this.callApiWithErrorMsg(
'領據處理',
'GetDropdownLists',
{ key: '身分別' },
'載入身分別選項'
);
if (identityResult && identityResult.Data) {
this.detailTable.identityOptions = identityResult.Data.filter(item => item.Value !== '');
}
// 加載憑證類別選項
const certResult = await this.callApiWithErrorMsg(
'領據處理',
'GetDropdownLists',
{ key: '憑證類別' },
'載入憑證類別選項'
);
if (certResult && certResult.Data) {
this.detailTable.certificateTypeOptions = certResult.Data.filter(item => item.Value !== '');
}
} catch (error) {
console.error('加載下拉清單失敗:', error);
}
},
async searchDetailTable() {
this.detailTable.loading = true;
this.detailTable.message = '';
this.detailTable.dataList = [];
this.detailTable.selectAll = false;
try {
const params = {
起日: this.detailTable.startDate ? this.parseDate(this.detailTable.startDate) : null,
迄日: this.detailTable.endDate ? this.parseDate(this.detailTable.endDate) : null,
身分別: this.detailTable.identity || null,
憑證類別: this.detailTable.certificateType || null,
輸出方式: 'DATA'
};
const result = await this.callApiWithErrorMsg(
'領據處理',
'SearchDetailTable',
params,
'查詢扣繳明細'
);
if (result && result.Success) {
var resultData = (result.Data || []).map(item => ({
...item,
selected: false,
showDetail: false
}));
this.detailTable.dataList = resultData;
if (this.detailTable.dataList.length === 0) {
this.detailTable.message = '查詢完成,但未找到符合條件的資料';
this.detailTable.messageType = 'info';
} else {
this.detailTable.message = `查詢完成,共找到 ${this.detailTable.dataList.length} 筆資料`;
this.detailTable.messageType = 'success';
}
} else {
this.detailTable.message = result.Message || '查詢失敗,請稍後重試';
this.detailTable.messageType = 'danger';
}
} catch (error) {
console.error('查詢失敗:', error);
this.detailTable.message = '查詢失敗: ' + (error.message || '請檢查網路連線');
this.detailTable.messageType = 'danger';
} finally {
this.detailTable.loading = false;
}
},
async exportDetailTable(kind) {
if (this.detailTable.dataList.length === 0) {
this.detailTable.message = '沒有資料可以匯出,請先執行查詢';
this.detailTable.messageType = 'warning';
return;
}
this.detailTable.loading = true;
this.detailTable.message = '';
try {
const params = {
起日: this.detailTable.startDate ? this.parseDate(this.detailTable.startDate) : null,
迄日: this.detailTable.endDate ? this.parseDate(this.detailTable.endDate) : null,
身分別: this.detailTable.identity || null,
憑證類別: this.detailTable.certificateType || null,
輸出方式: `EXPORT_${kind}`,
};
const result = await this.callApiWithErrorMsg(
'領據處理',
'SearchDetailTable',
params,
'匯出扣繳明細'
);
if (result && result.Success && result.Data) {
const excelData = result.Data;
if (excelData.excelData && excelData.fileName) {
this.downloadExcel(excelData.excelData, excelData.fileName);
this.detailTable.message = '匯出成功';
this.detailTable.messageType = 'success';
} else {
this.detailTable.message = '匯出失敗:無法取得檔案';
this.detailTable.messageType = 'danger';
}
} else {
this.detailTable.message = result.Message || '匯出失敗,請稍後重試';
this.detailTable.messageType = 'danger';
}
} catch (error) {
console.error('匯出失敗:', error);
this.detailTable.message = '匯出失敗: ' + (error.message || '請檢查網路連線');
this.detailTable.messageType = 'danger';
} finally {
this.detailTable.loading = false;
}
},
toggleSelectAll() {
this.detailTable.dataList.forEach(item => {
item.selected = this.detailTable.selectAll;
});
},
async previewEmail() {
// 檢查是否有選中的項目
const selectedItems = this.detailTable.dataList.filter(item => item.selected);
if (selectedItems.length === 0) {
this.detailTable.emailMessage = '請先勾選要發送的項目';
this.detailTable.emailMessageType = 'warning';
return;
}
// 檢查郵件設定
if (!this.detailTable.senderEmail) {
this.detailTable.emailMessage = '請輸入寄件人郵箱';
this.detailTable.emailMessageType = 'warning';
return;
}
if (!this.detailTable.emailSubject) {
this.detailTable.emailMessage = '請輸入郵件主旨';
this.detailTable.emailMessageType = 'warning';
return;
}
if (!this.detailTable.emailBody) {
this.detailTable.emailMessage = '請輸入郵件內文';
this.detailTable.emailMessageType = 'warning';
return;
}
this.detailTable.loading = true;
this.detailTable.emailMessage = '';
try {
// 提取選中項目的編號作為講師非講師編號
const 講師非講師編號 = selectedItems.map(item => item.講師_非講師編號);
const params = {
起日: this.detailTable.startDate ? this.parseDate(this.detailTable.startDate) : null,
迄日: this.detailTable.endDate ? this.parseDate(this.detailTable.endDate) : null,
身分別: this.detailTable.identity || null,
憑證類別: this.detailTable.certificateType || null,
輸出方式: 'PREVIEW',
講師非講師編號: 講師非講師編號,
寄件人郵箱: this.detailTable.senderEmail,
郵件主旨: this.detailTable.emailSubject,
郵件內文: this.detailTable.emailBody
};
const result = await this.callApiWithErrorMsg(
'領據處理',
'SendEmail',
params,
'預覽郵件'
);
if (result && result.Success && result.Data) {
this.detailTable.previewData = result.Data || [];
this.detailTable.showEmailPreview = true;
this.detailTable.emailMessage = '';
} else {
this.detailTable.emailMessage = result.Message || '預覽失敗,請稍後重試';
this.detailTable.emailMessageType = 'danger';
}
} catch (error) {
console.error('預覽失敗:', error);
this.detailTable.emailMessage = '預覽失敗: ' + (error.message || '請檢查網路連線');
this.detailTable.emailMessageType = 'danger';
} finally {
this.detailTable.loading = false;
}
},
closeEmailPreview() {
this.detailTable.showEmailPreview = false;
this.detailTable.previewData = [];
},
async sendEmail() {
// 檢查是否有選中的項目
const selectedItems = this.detailTable.dataList.filter(item => item.selected);
if (selectedItems.length === 0) {
this.detailTable.emailMessage = '請先勾選要發送的項目';
this.detailTable.emailMessageType = 'warning';
return;
}
if (!confirm(`確認要發送郵件給 ${selectedItems.length} 位收件者嗎?`)) {
return;
}
this.detailTable.loading = true;
this.detailTable.emailMessage = '';
try {
// 提取選中項目的編號作為講師非講師編號
const 講師非講師編號 = selectedItems.map(item => item.講師_非講師編號);
const params = {
起日: this.detailTable.startDate ? this.parseDate(this.detailTable.startDate) : null,
迄日: this.detailTable.endDate ? this.parseDate(this.detailTable.endDate) : null,
身分別: this.detailTable.identity || null,
憑證類別: this.detailTable.certificateType || null,
輸出方式: 'SENDMAIL',
講師非講師編號: 講師非講師編號,
寄件人郵箱: this.detailTable.senderEmail,
郵件主旨: this.detailTable.emailSubject,
郵件內文: this.detailTable.emailBody
};
const result = await this.callApiWithErrorMsg(
'領據處理',
'SendEmail',
params,
'發送郵件'
);
if (result && result.Success) {
this.detailTable.emailMessage = `郵件已排定發送給 ${selectedItems.length} 位收件者`;
this.detailTable.emailMessageType = 'success';
// 關閉預覽小窗
this.detailTable.showEmailPreview = false;
this.detailTable.previewData = [];
// 清空勾選
this.detailTable.dataList.forEach(item => {
item.selected = false;
});
this.detailTable.selectAll = false;
} else {
this.detailTable.emailMessage = result.Message || '發送失敗,請稍後重試';
this.detailTable.emailMessageType = 'danger';
}
} catch (error) {
console.error('發送失敗:', error);
this.detailTable.emailMessage = '發送失敗: ' + (error.message || '請檢查網路連線');
this.detailTable.emailMessageType = 'danger';
} finally {
this.detailTable.loading = false;
}
}
},
computed: {
canSendEmail() {
const hasSelected = this.detailTable.dataList.some(item => item.selected);
const hasEmail = this.detailTable.senderEmail && this.detailTable.senderEmail.trim().length > 0;
const hasSubject = this.detailTable.emailSubject && this.detailTable.emailSubject.trim().length > 0;
const hasBody = this.detailTable.emailBody && this.detailTable.emailBody.trim().length > 0;
return hasSelected && hasEmail && hasSubject && hasBody && !this.detailTable.loading;
}
}
});
}