function CreateReceiptEditApp() { const loadMarker = document.getElementById('receiptEditApp'); if (loadMarker) { DoCreateReceiptEditApp(); } } function DoCreateReceiptEditApp() { window.__vms = window.__vms || {} if (window.__vms.receiptEditApp) return; window.__vms.receiptEditApp = new Vue({ el: '#receiptEditApp', mixins: [DateMixin, ToastMixin, ApiMixin], data: { mode: 'view', // view, add, edit 編號: 0, originalMainData: null, originalLaborData: null, originalTravelData: null, mainData: { 編號: 0, 表單編號: '', 活動日期: '', 身分別: '', 講師_非講師編號: '', 領款人姓名: '', 身分證字號: '', 憑證類別: '', 計畫代號: '', 計畫名稱: '', 活動地點: '', 活動類別: '', 活動名稱事由: '', 製單人員: '', 付款方式: '', 作廢: 0, 列印次數: 0, 已關帳: false, DB_APPNO: 1, DB_CRDAT: '', DB_CRUSR: '', DB_CRUSR_NAME: '', DB_TRDAT: '', DB_TRUSR: '', DB_TRUSR_NAME: '' }, laborData: { 編號: '', 領據編號: 0, 費用別: '', 單價: 0, 數量: 0, 單位: '', 扣繳類別: '', 合計金額: 0, 應扣繳稅額: 0, 服務費: 0, 作廢: 0, DB_APPNO: 1 }, travelData: { 編號: '', 領據編號: 0, 票價資訊: '', 費用別: '', 起點: '', 訖點: '', 捷運公車: 0, 火車: 0, 計程車: 0, 高鐵飛機: 0, 自行開車: 0, 過路費停車費: 0, 住宿費: 0, 合計: 0, 作廢: 0, DB_APPNO: 1 }, dropdown: { 身分別: [], 憑證類別: [], 付款方式: [], 勞務費_費用別: [], 差旅費_費用別: [], 扣繳類別: [], 單位別: [] }, recipientList: [], showRecipientDropdown: false, planList: [], showPlanDropdown: false, originStations: [], showOriginDropdown: false, destStations: [], showDestDropdown: false, stations: [], showActivityLocationPhrase: false, activityLocationPhrases: [], showActivityCategoryPhrase: false, activityCategoryPhrases: [], showTicketPricePhrase: false, ticketPricePhrases: [], loading: false, variables: { 領據執行業務所得課稅門檻: 0, 領據薪資課稅門檻: 0, 領據基本工資: 0, 領據執行業務所得稅率: 0, 領據薪資稅率: 0 }, // 花花: 上下筆導航用 navIdList: [], navCurrentIndex: -1, navCurrentPage: 1, navPageSize: 20, navTotalPages: 0, navSearchParams: {} }, created() { // 從 AJAX 取得下拉選單資料 this.loading = true; this.loadVariables(); this.loadDropdownLists(); this.loadPhraseLists(); this.loading = false; this.mainData.製單人員 = V製單人員; this.編號 = 0; if (document.getElementById('ctl00_ContentPlaceHolder1_hid編號').value !== '') { this.編號 = parseInt(document.getElementById('ctl00_ContentPlaceHolder1_hid編號').value) || 0; } // 花花: 初始化上下筆導航狀態(從查詢畫面傳來的 HiddenField 讀取) this._initNavigation(); }, mounted() { if (this.編號 !== 0) { this.mode = 'view'; this.loadData(this.編號) } else { this.mode = 'add'; this.reset(); this.backupData(); this.setDefault(); } }, watch: { 'laborData.單價'() { this.calculateLaborTotal(); }, 'laborData.數量'() { this.calculateLaborTotal(); }, 'laborData.扣繳類別'() { this.calculateTax(); }, 'mainData.身分別'() { if (this.mode === 'add' || this.mode === 'edit') { this.mainData.講師_非講師編號 = ''; this.mainData.領款人姓名 = ''; this.mainData.身分證字號 = ''; this.recipientList = []; } }, 'travelData.捷運公車'() { this.calculateTravelTotal(); }, 'travelData.火車'() { this.calculateTravelTotal(); }, 'travelData.計程車'() { this.calculateTravelTotal(); }, 'travelData.高鐵飛機'() { this.calculateTravelTotal(); }, 'travelData.自行開車'() { this.calculateTravelTotal(); }, 'travelData.過路費停車費'() { this.calculateTravelTotal(); }, 'travelData.住宿費'() { this.calculateTravelTotal(); } }, computed: { isViewMode() { return this.mode === 'view'; }, isEditMode() { return this.mode === 'edit' || this.mode === 'add'; }, canAddNew() { return this.mode === 'view'; }, canEdit() { return this.mode === 'view' && this.編號 !== 0; }, canCancel() { return this.mode === 'add' || this.mode === 'edit'; }, canSave() { return this.mode === 'add' || this.mode === 'edit'; }, canReturnToList() { return this.mode === 'view'; }, closed() { return this.mainData.已關帳; }, // 花花: 上下筆導航可用性(僅 view 模式可用) hasPrev() { if (this.mode !== 'view') return false; if (this.navIdList.length === 0 || this.navCurrentIndex < 0) return false; return this.navCurrentIndex > 0 || this.navCurrentPage > 1; }, hasNext() { if (this.mode !== 'view') return false; if (this.navIdList.length === 0 || this.navCurrentIndex < 0) return false; return this.navCurrentIndex < this.navIdList.length - 1 || this.navCurrentPage < this.navTotalPages; }, laborWarnings() { const warnings = []; const 扣繳類別 = this.laborData.扣繳類別 || ''; const 有金額 = (this.laborData.單價 || 0) > 0 || (this.laborData.數量 || 0) > 0 || (this.laborData.合計金額 || 0) > 0; // 費用別警告 if (['演講費', '撰稿費'].indexOf(this.laborData.費用別) >= 0 && 扣繳類別 === '50') { warnings.push('扣繳類別只能是執行業務所得'); } // 有金額但費用別未填 if (有金額 && !this.laborData.費用別) { warnings.push('勞務費已輸入金額,請選擇費用別'); } // 有金額但單位未填 if (有金額 && !this.laborData.單位) { warnings.push('勞務費已輸入金額,請選擇單位'); } // 合計金額警告 if (扣繳類別 === '50' && this.laborData.合計金額 >= this.variables.領據基本工資) { warnings.push(`薪資不得超過基本工資${this.variables.領據基本工資}(含),超過請拆成不同日期的領據`); } if ((扣繳類別.startsWith('9A') || 扣繳類別.startsWith('9B')) && this.laborData.合計金額 >= this.variables.領據執行業務所得課稅門檻) { warnings.push(`執行業務所得不得超過${this.variables.領據執行業務所得課稅門檻}(含),超過請拆成不同日期的領據`); } return warnings; } }, methods: { async loadVariables() { try { const result = await this.callApi('領據處理', 'GetVariables', {}); if (result.Data) { this.variables = result.Data; } } catch (error) { console.error('載入片語清單失敗:', error); } }, async loadDropdownLists() { this.dropdown.身分別 = await this.loadDropdownList("身分別"); this.dropdown.憑證類別 = await this.loadDropdownList("憑證類別"); this.dropdown.付款方式 = await this.loadDropdownList("付款方式"); this.dropdown.勞務費_費用別 = await this.loadDropdownList("勞務費_費用別"); this.dropdown.差旅費_費用別 = await this.loadDropdownList("差旅費_費用別"); this.dropdown.扣繳類別 = await this.loadDropdownList("扣繳類別"); this.dropdown.單位別 = await this.loadDropdownList("單位別"); }, async loadDropdownList(key) { try { const result = await this.callApi('領據處理', 'GetDropdownLists', { key }); return result.Data || []; } catch (error) { console.error('載入下拉選單失敗:', error); return []; } }, async loadPhraseLists() { try { const result = await this.callApi('領據處理', 'GetPhraseLists', {}); if (result.Data) { this.activityLocationPhrases = result.Data.activityLocation || []; this.activityCategoryPhrases = result.Data.activityCategory || []; this.ticketPricePhrases = result.Data.ticketPrice || []; } } catch (error) { console.error('載入公用變數失敗:', error); } }, toggleActivityLocationPhrase() { if (this.isViewMode) return; this.showActivityLocationPhrase = !this.showActivityLocationPhrase; this.showActivityCategoryPhrase = false; this.showTicketPricePhrase = false; }, toggleActivityCategoryPhrase() { if (this.isViewMode) return; this.showActivityCategoryPhrase = !this.showActivityCategoryPhrase; this.showActivityLocationPhrase = false; this.showTicketPricePhrase = false; }, toggleTicketPricePhrase() { if (this.isViewMode) return; this.showTicketPricePhrase = !this.showTicketPricePhrase; this.showActivityLocationPhrase = false; this.showActivityCategoryPhrase = false; }, insertPhrase(fieldName, phrase) { let input; if (fieldName === '活動地點') { input = this.$refs.活動地點Input; this.showActivityLocationPhrase = false; } else if (fieldName === '活動類別') { input = this.$refs.活動類別Input; this.showActivityCategoryPhrase = false; } else if (fieldName === '票價資訊') { input = this.$refs.票價資訊Input; this.showTicketPricePhrase = false; } if (input) { const startPos = input.selectionStart || input.value.length; const endPos = input.selectionEnd || input.value.length; const currentValue = input.value; const newValue = currentValue.substring(0, startPos) + phrase + currentValue.substring(endPos); if (fieldName === '活動地點') { this.mainData.活動地點 = newValue; } else if (fieldName === '活動類別') { this.mainData.活動類別 = newValue; } else if (fieldName === '票價資訊') { this.travelData.票價資訊 = newValue; } // 更新游標位置 this.$nextTick(() => { input.focus(); input.setSelectionRange(startPos + phrase.length, startPos + phrase.length); }); } }, calculateLaborTotal() { const price = parseFloat(this.laborData.單價) || 0; const qty = parseFloat(this.laborData.數量) || 0; this.laborData.合計金額 = Math.round(price * qty); this.calculateTax(); }, async calculateTax() { const laborFee = parseInt(this.laborData.合計金額) || 0; const category = this.laborData.扣繳類別 || ''; if (laborFee > 0 && category) { this.loading = true; try { const result = await this.callApi('領據處理', 'CalcTax', { laborFee, category }); this.laborData.應扣繳稅額 = result.Data; } catch (error) { console.error('計算稅額失敗:', error); } finally { this.loading = false; } } else { this.laborData.應扣繳稅額 = 0; } }, calculateTravelTotal() { this.travelData.合計 = (parseInt(this.travelData.捷運公車) || 0) + (parseInt(this.travelData.火車) || 0) + (parseInt(this.travelData.計程車) || 0) + (parseInt(this.travelData.高鐵飛機) || 0) + (parseInt(this.travelData.自行開車) || 0) + (parseInt(this.travelData.過路費停車費) || 0) + (parseInt(this.travelData.住宿費) || 0); }, searchStation(field) { const key = this.travelData[field]; if (key && key.length > 0) { axios({ method: 'post', url: `${API_TRAIN_FARE}/SelectStation`, headers: { 'Content-Type': 'application/json; charset=utf-8' }, data: JSON.stringify({ key }) }) .then(response => { const result = response.data.d || response.data; if (result.Success) { this.stations = result.Data || []; } else { this.stations = []; this.ShowWarning(result.Message); } }) .catch(error => { console.error('查詢車站失敗:', error); }); } }, async searchRecipient() { const key = this.mainData.領款人姓名; const identity = this.mainData.身分別; const activationDate = this.mainData.活動日期; if (!key || key.length === 0) { this.recipientList = []; this.showRecipientDropdown = false; return; } if (!identity) { this.ShowWarning('請先選擇身分別'); this.recipientList = []; this.showRecipientDropdown = false; return; } if (!activationDate) { this.ShowWarning('請輸入活動日期'); this.recipientList = []; this.showRecipientDropdown = false; return; } this.loading = true; try { const result = await this.callApi('領據處理', 'SearchRecipient', { key, identity, activationDate }); this.recipientList = result.Data || []; this.showRecipientDropdown = this.recipientList.length > 0; } catch (error) { console.error('查詢領款人失敗:', error); this.recipientList = []; this.showRecipientDropdown = false; this.ShowWarning(error.message); } finally { this.loading = false; } }, hideRecipientDropdown() { setTimeout(() => { this.showRecipientDropdown = false; this.recipientList = []; }, 200); }, selectRecipient(recipient) { this.mainData.講師_非講師編號 = recipient.Value; this.mainData.領款人姓名 = recipient.Name; this.mainData.身分證字號 = recipient.IdNumber; this.mainData.憑證類別 = recipient.VoucherType; this.laborData.扣繳類別 = recipient.ConductType; this.recipientList = []; this.showRecipientDropdown = false; }, async searchInstructorEvaluation() { const date = this.mainData.活動日期; const teacherNo = this.mainData.講師_非講師編號; const name = this.mainData.領款人姓名; if (!date) { this.ShowWarning('請輸入活動日期'); return; } if (!teacherNo && !name) { this.ShowWarning('請輸入講師編號或領款人姓名'); return; } this.loading = true; try { const result = await this.callApi('領據處理', 'SearchInstructorEvaluation', { 日期: date, 講師編號: teacherNo || '', 姓名: name || '' }); const data = result.Data || []; if (data.length === 0) { this.ShowWarning('無相符講師評鑑'); } else { const lines = data.map(item => `日期:${this.FormatDate(item.日期)},課程名稱:${item.課程名稱}`); this.ShowSuccess(lines.join('\n')); } } catch (error) { console.error('查詢講師評鑑失敗:', error); this.ShowError('查詢講師評鑑失敗: ' + (error.message || '請稍後重試')); } finally { this.loading = false; } }, async searchPlan() { const key = this.mainData.計畫代號; const year = this.mainData.活動日期 ? new Date(this.mainData.活動日期).getFullYear() - 1911 : null; if (year === null) { this.ShowWarning("請輸入活動日期"); return; } if (!key || key.length === 0) { this.planList = []; this.showPlanDropdown = false; return; } this.loading = true; try { const result = await this.callApi('領據處理', 'SearchPlan', { key, year }); this.planList = result.Data || []; this.showPlanDropdown = this.planList.length > 0; } catch (error) { console.error('查詢計畫失敗:', error); this.planList = []; this.showPlanDropdown = false; this.ShowWarning(error.message); } finally { this.loading = false; } }, selectPlan(plan) { this.mainData.計畫代號 = plan.Code; this.mainData.計畫名稱 = plan.Name; this.planList = []; this.showPlanDropdown = false; }, hidePlanDropdown() { setTimeout(() => { this.showPlanDropdown = false; this.planList = []; }, 200); }, async searchOriginStation() { const key = this.travelData.起點; if (!key || key.length === 0) { this.originStations = []; this.showOriginDropdown = false; return; } this.loading = true; try { const result = await this.callApi('火車票價', 'SelectOriginStation', { key }); this.originStations = result.Data || []; this.showOriginDropdown = this.originStations.length > 0; } catch (error) { console.error('查詢起點失敗:', error); this.originStations = []; this.showOriginDropdown = false; this.ShowWarning(error.message); } finally { this.loading = false; } }, selectOriginStation(station) { this.travelData.起點 = station.Name; this.originStations = []; this.showOriginDropdown = false; }, hideOriginDropdown() { setTimeout(() => { this.showOriginDropdown = false; this.originStations = []; }, 200); this.calculateFare(); }, async searchDestStation() { const key = this.travelData.訖點; if (!key || key.length === 0) { this.destStations = []; this.showDestDropdown = false; return; } this.loading = true; try { const result = await this.callApi('火車票價', 'SelectDestStation', { key }); this.destStations = result.Data || []; this.showDestDropdown = this.destStations.length > 0; } catch (error) { console.error('查詢訖點失敗:', error); this.destStations = []; this.showDestDropdown = false; this.ShowWarning(error.message); } finally { this.loading = false; } }, selectDestStation(station) { this.travelData.訖點 = station.Name; this.destStations = []; this.showDestDropdown = false; }, hideDestDropdown() { setTimeout(() => { this.showDestDropdown = false; this.destStations = []; }, 200); this.calculateFare(); }, async calculateFare() { const 起站 = this.travelData.起點; const 迄站 = this.travelData.訖點; if (!起站 || !迄站) { return; } this.loading = true; try { const result = await this.callApi('火車票價', 'SelectFare', { 起站, 迄站 }); this.travelData.火車 = result.Data; this.calculateTravelTotal(); } catch (error) { console.error('查詢票價失敗:', error); this.ShowWarning(error.message); } finally { this.loading = false; } }, async save() { if (!this.validate() || (this.laborWarnings || []).length > 0) { return; } this.loading = true; try { const data = { isNew: this.mode === 'add', mainItem: this.mainData, laborItem: this.laborData, travelItem: this.travelData }; const result = await this.callApiWithErrorMsg('領據處理', 'Save', data, '儲存'); this.ShowSuccess(result.Message); // 新增存檔更新編號變數 if (this.mode === 'add') { this.編號 = result.Data.編號; } // 返回查看模式 this.mode = 'view'; this.loadData(this.編號); this.backupData(); } catch (error) { console.error('Save error:', error); } finally { this.loading = false; } }, validate() { if (!this.mainData.身分別) { this.ShowWarning('請選擇身分別'); return false; } if (!this.mainData.領款人姓名) { this.ShowWarning('請輸入領款人姓名'); return false; } if (!this.mainData.計畫代號) { this.ShowWarning('請輸入計畫代號'); return false; } return true; }, returnToList() { if ((this.mode === 'add' || this.mode === 'edit') && !confirm("是否放棄" + (this.mode === 'add' ? '新增' : '修改'))) return; window.__doPostBack('ctl00$ContentPlaceHolder1$btnTab1', ''); }, backupData() { this.originalMainData = JSON.parse(JSON.stringify(this.mainData)); this.originalLaborData = JSON.parse(JSON.stringify(this.laborData)); this.originalTravelData = JSON.parse(JSON.stringify(this.travelData)); }, restoreData() { if (this.originalMainData) { this.mainData = JSON.parse(JSON.stringify(this.originalMainData)); } if (this.originalLaborData) { this.laborData = JSON.parse(JSON.stringify(this.originalLaborData)); } if (this.originalTravelData) { this.travelData = JSON.parse(JSON.stringify(this.originalTravelData)); } }, addNew() { if (this.mode !== 'view') { return; } this.mode = 'add'; this.reset(); this.backupData(); this.setDefault(); }, edit() { if (this.mode !== 'view') { return; } this.mode = 'edit'; this.backupData(); }, cancel() { if (this.mode === 'view') { return; } if (!confirm("是否放棄" + (this.mode === 'add' ? '新增' : '修改') + ",資料將復原?")) { return; } this.restoreData(); this.mode = 'view'; }, reset() { this.mainData = { 編號: 0, 表單編號: '', 活動日期: '', 身分別: '', 講師_非講師編號: '', 領款人姓名: '', 身分證字號: '', 憑證類別: '', 計畫代號: '', 計畫名稱: '', 活動地點: '', 活動類別: '', 活動名稱事由: '', 付款方式: '', 作廢: 0, 列印次數: 0, DB_APPNO: 1, DB_CRDAT: '', DB_CRUSR: '', DB_CRUSR_NAME: '', DB_TRDAT: '', DB_TRUSR: '', DB_TRUSR_NAME: '', }; this.laborData = { 編號: '', 領據編號: 0, 費用別: '', 單價: 0, 數量: 0, 單位: '', 扣繳類別: '', 合計金額: 0, 應扣繳稅額: 0, 服務費: 0, 作廢: 0, DB_APPNO: 1 }; this.travelData = { 編號: '', 領據編號: 0, 票價資訊: '', 費用別: '', 起點: '', 訖點: '', 捷運公車: 0, 火車: 0, 計程車: 0, 高鐵飛機: 0, 自行開車: 0, 過路費停車費: 0, 住宿費: 0, 合計: 0, 作廢: 0, DB_APPNO: 1 }; }, setDefault() { this.mainData.DB_CRDAT = this.FormatDate(new Date()); this.mainData.DB_CRUSR = document.getElementById('ctl00_hidCurrentUser').value; this.mainData.DB_CRUSR_NAME = document.getElementById('ctl00_hidCurrentUserName').value; }, async loadData(no) { this.loading = true; try { const result = await this.callApiWithErrorMsg('領據處理', 'GetOne', { no }, '載入'); const data = result.Data; this.mainData = { 編號: data.編號, 表單編號: data.表單編號, 活動日期: this.FormatDate(data.活動日期), 身分別: data.身分別, 講師_非講師編號: data.講師_非講師編號, 領款人姓名: data.領款人姓名, 身分證字號: data.身分證字號, 憑證類別: data.憑證類別, 計畫代號: data.計畫代號, 計畫名稱: data.計畫名稱, 活動地點: data.活動地點, 活動類別: data.活動類別, 活動名稱事由: data.活動名稱事由, 付款方式: data.付款方式, 作廢: data.作廢, 列印次數: data.列印次數, 已關帳: data.已關帳, DB_APPNO: data.DB_APPNO, DB_CRDAT: this.FormatDate(data.DB_CRDAT), DB_CRUSR: data.DB_CRUSR, DB_CRUSR_NAME: data.DB_CRUSR_NAME, DB_TRDAT: data.DB_TRUSR !== null ? this.FormatDate(data.DB_TRDAT) : '', DB_TRUSR: data.DB_TRUSR !== null ? data.DB_TRUSR : '', DB_TRUSR_NAME: data.DB_TRUSR != null ? data.DB_TRUSR_NAME: '' }; this.laborData = { 編號: data.勞務費_編號, 領據編號: data.編號, 費用別: data.勞務費_費用別, 單價: data.勞務費_單價, 數量: data.勞務費_數量, 單位: data.勞務費_單位, 扣繳類別: data.勞務費_扣繳類別, 合計金額: data.勞務費_合計金額, 應扣繳稅額: data.勞務費_應扣繳稅額, 服務費: data.勞務費_服務費, 作廢: data.勞務費_作廢 }; this.travelData = { 編號: data.差旅費_編號, 領據編號: data.編號, 票價資訊: data.差旅費_票價資訊, 費用別: data.差旅費_費用別, 起點: data.差旅費_起點, 訖點: data.差旅費_訖點, 捷運公車: data.差旅費_捷運公車, 火車: data.差旅費_火車, 計程車: data.差旅費_計程車, 高鐵飛機: data.差旅費_高鐵飛機, 自行開車: data.差旅費_自行開車, 過路費停車費: data.差旅費_過路費停車費, 住宿費: data.差旅費_住宿費, 合計: data.差旅費_合計, 作廢: data.差旅費_作廢 }; if (data.main) { this.mainData = data.main; } if (data.labor) { this.laborData = data.labor; } if (data.travel) { this.travelData = data.travel; } this.backupData(); } catch (error) { console.error('LoadData error:', error); } finally { this.loading = false; } }, async print() { if (this.mainData.作廢 !== 0) { this.ShowError('已作廢不可列印'); } this.loading = true; try { const result = await this.callApiWithErrorMsg( '領據處理', 'Print', { idList: [this.mainData.編號 ] }, '列印領據' ); if (result.Success) { // 下載 Excel 檔案 if (result.Data && result.Data.fileName) { const link = document.createElement('a'); link.href = `data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,${result.Data.excelData}`; link.download = result.Data.fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); this.ShowSuccess(result.Message); } else { this.ShowWarning(result.Message); } } else { this.ShowError('列印失敗: ' + (error.message || '請稍後重試')); } } catch (error) { this.ShowError('列印失敗: ' + (error.message || '請檢查網路連線')); } finally { this.loadData(this.編號); this.loading = false; } }, async voidVoucher() { if (this.mainData.作廢 === 0 && !confirm("是否確定作廢?") || this.mainData.作廢 !== 0 && !confirm("是否確定取消作廢?")) { return; } this.loading = true; try { const result = await this.callApi('領據處理', 'Void', { no: this.mainData.編號 }); this.loadData(this.mainData.編號); this.ShowSuccess('作廢操作完成'); } catch (error) { console.error('Void error:', error); this.ShowError('作廢操作失敗: ' + (error.message || '請檢查網路連線')); } finally { this.loading = false; } }, async voidLaborFee() { if (this.laborData.作廢 === 0 && !confirm("是否確定作廢勞務費?") || this.laborData.作廢 !== 0 && !confirm("是否確定取消作廢勞務費?")) { return; } this.loading = true; try { const result = await this.callApi('領據處理', 'VoidLaborFee', { receiptNo: this.mainData.編號 }); this.loadData(this.mainData.編號); this.ShowSuccess('勞務費作廢操作完成'); } catch (error) { console.error('VoidLaborFee error:', error); this.ShowError('勞務費作廢操作失敗: ' + (error.message || '請檢查網路連線')); } finally { this.loading = false; } }, async voidTravelFee() { if (this.travelData.作廢 === 0 && !confirm("是否確定作廢差旅費?") || this.travelData.作廢 !== 0 && !confirm("是否確定取消作廢差旅費?")) { return; } this.loading = true; try { const result = await this.callApi('領據處理', 'VoidTravelFee', { receiptNo: this.mainData.編號 }); this.loadData(this.mainData.編號); this.ShowSuccess('差旅費作廢操作完成'); } catch (error) { console.error('VoidTravelFee error:', error); this.ShowError('差旅費作廢操作失敗: ' + (error.message || '請檢查網路連線')); } finally { this.loading = false; } }, // 花花: 上下筆導航 — 初始化和執行 _initNavigation() { const hidIdList = document.getElementById('ctl00_ContentPlaceHolder1_hidIdList'); if (hidIdList && hidIdList.value) { try { this.navIdList = JSON.parse(hidIdList.value); } catch(e) { this.navIdList = []; } } // 在 idList 中找到目前編號的位置 this.navCurrentIndex = this.navIdList.indexOf(this.編號); this.navCurrentPage = 1 * document.getElementById('ctl00_ContentPlaceHolder1_hidCurrentPage').value || 1; this.navPageSize = 1 * document.getElementById('ctl00_ContentPlaceHolder1_hidPageSize').value || 20; this.navTotalPages = 1 * document.getElementById('ctl00_ContentPlaceHolder1_hidNavTotalPages').value || 0; // 從 HiddenField 讀取搜尋條件副本,供跨頁查詢使用 this.navSearchParams = { 活動日期_1: document.getElementById('ctl00_ContentPlaceHolder1_hid活動日期_1').value || null, 活動日期_2: document.getElementById('ctl00_ContentPlaceHolder1_hid活動日期_2').value || null, 身分別: document.getElementById('ctl00_ContentPlaceHolder1_hid身分別').value || null, 講師_非講師編號: null, 憑證類別: document.getElementById('ctl00_ContentPlaceHolder1_hid憑證類別').value || null, 計畫代號: document.getElementById('ctl00_ContentPlaceHolder1_hid計畫代號').value || null, 計畫名稱: document.getElementById('ctl00_ContentPlaceHolder1_hid計畫名稱').value || null, 活動類別: document.getElementById('ctl00_ContentPlaceHolder1_hid活動類別').value || null, 付款方式: document.getElementById('ctl00_ContentPlaceHolder1_hid付款方式').value || null, 領款人姓名: document.getElementById('ctl00_ContentPlaceHolder1_hid付款方式').value || null, orderBy: '' }; }, async goPrev() { if (!this.hasPrev) return; if (this.navCurrentIndex > 0) { // 當頁內上一筆:直接從 navIdList 取 this.navCurrentIndex--; this.編號 = this.navIdList[this.navCurrentIndex]; this.loadData(this.編號); } else { // 跨頁上一筆:取上一頁最後一筆 await this._fetchPageAndNavigate(this.navCurrentPage - 1, 'last'); } }, async goNext() { if (!this.hasNext) return; if (this.navCurrentIndex < this.navIdList.length - 1) { // 當頁內下一筆:直接從 navIdList 取 this.navCurrentIndex++; this.編號 = this.navIdList[this.navCurrentIndex]; this.loadData(this.編號); } else { // 跨頁下一筆:取下一頁第一筆 await this._fetchPageAndNavigate(this.navCurrentPage + 1, 'first'); } }, async _fetchPageAndNavigate(pageIndex, position) { this.loading = true; try { const params = { ...this.navSearchParams, pageIndex, pageSize: this.navPageSize }; const result = await this.callApiWithErrorMsg('領據處理', 'SelectPage', params, '查詢'); const newList = this.ConvertDatesInObject(result.Data || [], '-'); if (newList.length === 0) return; this.navIdList = newList.map(item => item.編號); this.navCurrentPage = pageIndex; this.navTotalPages = Math.ceil(result.TotalCount / this.navPageSize); this.navCurrentIndex = position === 'first' ? 0 : this.navIdList.length - 1; this.編號 = this.navIdList[this.navCurrentIndex]; // 同步更新 hidden fields,避免 postback 後狀態遺失 document.getElementById('ctl00_ContentPlaceHolder1_hidIdList').value = JSON.stringify(this.navIdList); document.getElementById('ctl00_ContentPlaceHolder1_hidNavTotalPages').value = this.navTotalPages; document.getElementById('ctl00_ContentPlaceHolder1_hidCurrentPage').value = this.navCurrentPage; this.loadData(this.編號); } catch (error) { console.error('導航查詢失敗:', error); this.ShowError('導航失敗: ' + (error.message || '請稍後重試')); } finally { this.loading = false; } }, } }); }