// 前台導航欄和區域選擇功能 document.addEventListener('DOMContentLoaded', function() { const loginForm = document.getElementById('loginForm'); const registerForm = document.getElementById('registerForm'); const loginModal = document.getElementById('loginModal'); const registerModal = document.getElementById('registerModal'); // 更新導航欄 active 狀態 updateNavbarActive(); // 檢查登入狀態 checkLoginStatus(); // 若帶有 auth=required,提示需先登入並顯示登入彈窗 try { const url = new URL(window.location.href); const authRequired = url.searchParams.get('auth'); if (authRequired === 'required') { // 顯示提示 if (typeof showAlert === 'function') { showAlert('請先登入或註冊後再進入此頁面', 'warning'); } // 顯示登入模態框 if (loginModal) { const modal = new bootstrap.Modal(loginModal); modal.show(); } // 清理網址中的提示參數,避免重複彈出 url.searchParams.delete('auth'); history.replaceState({}, '', url.pathname + (url.search ? '?' + url.searchParams.toString() : '') + url.hash); } } catch (e) { console.error('處理登入提示參數時發生錯誤:', e); } // 初始化區域選擇下拉選單 initRegionDropdown(); // 處理個人資料保護聲明模態框關閉後返回註冊畫面 const privacyModal = document.getElementById('privacyModal'); if (privacyModal && registerModal) { // 追蹤是否應該在關閉個人資料保護聲明後返回註冊畫面 let shouldReturnToRegister = false; // 監聽註冊模態框打開個人資料保護聲明的連結點擊事件 // 使用事件委派,因為連結可能在註冊表單中動態生成 document.addEventListener('click', function(e) { const privacyLink = e.target.closest('a[data-bs-target="#privacyModal"]'); if (privacyLink) { // 檢查點擊的連結是否在註冊模態框內 if (registerModal && registerModal.contains(privacyLink)) { shouldReturnToRegister = true; } } }); // 監聽個人資料保護聲明模態框關閉事件 privacyModal.addEventListener('hidden.bs.modal', function (event) { if (shouldReturnToRegister) { // 使用 setTimeout 確保當前模態框完全關閉後再打開註冊模態框 setTimeout(() => { const registerModalInstance = bootstrap.Modal.getOrCreateInstance(registerModal); // 檢查註冊模態框是否已經顯示,如果沒有則顯示 if (!registerModal.classList.contains('show')) { registerModalInstance.show(); } }, 300); // 重置標記 shouldReturnToRegister = false; } }); } // 全域掛載重開函式 window.reopenPreferenceExploration = function() { if(window.questionnaireManager && typeof questionnaireManager.startPreferenceExploration === 'function'){ questionnaireManager.preferenceData=[]; questionnaireManager.champWinner=null; questionnaireManager.eliminationRounds=[]; questionnaireManager.currentEliminationRound=0; questionnaireManager.currentPairs=[]; questionnaireManager.currentPairIndex=0; const preferenceModal = document.getElementById('preferenceModal'); if(preferenceModal){ const bsModal = new bootstrap.Modal(preferenceModal); bsModal.show(); } questionnaireManager.startPreferenceExploration(); } }; window.reopenQuestionnaire = function() { if(window.questionnaireManager && typeof questionnaireManager.loadQuestions === 'function'){ questionnaireManager.questionnaireData={}; questionnaireManager.preferenceData=[]; questionnaireManager.currentRound=1; questionnaireManager.loadQuestions(); const questionnaireModal = document.getElementById('questionnaireModal'); if(questionnaireModal){ const bsModal = new bootstrap.Modal(questionnaireModal); bsModal.show(); } } }; // 更新導航欄 active 狀態函數 function updateNavbarActive() { const currentPath = window.location.pathname; const navLinks = document.querySelectorAll('.navbar-nav .nav-link'); // 移除所有 active 類別 navLinks.forEach(link => { link.classList.remove('active'); }); // 根據當前路徑設定 active 狀態 navLinks.forEach(link => { const href = link.getAttribute('href'); const dataPage = link.getAttribute('data-page'); if (href === currentPath || (currentPath === '/' && dataPage === 'home') || (currentPath.startsWith('/start_match') && dataPage === 'start_match') || (currentPath.startsWith('/medical_map') && dataPage === 'medical_map') || (currentPath.startsWith('/adoption_process') && dataPage === 'adoption_process') || (currentPath.startsWith('/model_accuracy') && dataPage === 'model_accuracy') || (currentPath.startsWith('/pet_info') && dataPage === 'dog_list') || (currentPath.startsWith('/service_info') && dataPage === 'service_info') || (currentPath.startsWith('/user/profile') && dataPage === 'profile')) { link.classList.add('active'); } }); } // 檢查登入狀態函數 function checkLoginStatus() { // 先隱藏登入按鈕,避免閃爍 const loginNavItem = document.querySelector('.nav-item:last-child'); if (loginNavItem) { loginNavItem.style.visibility = 'hidden'; } fetch('/api/user/status') .then(response => response.json()) .then(data => { if (data.logged_in) { updateNavbarAfterLogin(data.user.name); // 檢查是否有收容所瀏覽權限 checkShelterAccessPermission(); } }) .catch(error => { console.error('Error checking login status:', error); }) .finally(() => { // 恢復顯示 if (loginNavItem) { loginNavItem.style.visibility = 'visible'; } }); } // 檢查收容所瀏覽權限並顯示/隱藏模型評分連結 function checkShelterAccessPermission() { // 檢查是否有 store_id_frontend(收容所瀏覽權限) // 通過檢查 session 或調用 API 來確認 fetch('/api/user/status') .then(response => response.json()) .then(data => { // 如果有 store_id_frontend 且不等於預設值,表示有收容所瀏覽權限 const hasPermission = data.store_id_frontend && data.store_id_frontend !== '3a2b41a5-7abe-429c-815c-d9f1bd217729'; const modelAccuracyNavItem = document.getElementById('modelAccuracyNavItem'); if (modelAccuracyNavItem) { if (hasPermission) { modelAccuracyNavItem.style.display = 'block'; } else { modelAccuracyNavItem.style.display = 'none'; } } }) .catch(error => { console.error('檢查收容所瀏覽權限時發生錯誤:', error); // 發生錯誤時隱藏連結 const modelAccuracyNavItem = document.getElementById('modelAccuracyNavItem'); if (modelAccuracyNavItem) { modelAccuracyNavItem.style.display = 'none'; } }); } // 登入表單提交 if (loginForm) { loginForm.addEventListener('submit', function(e) { e.preventDefault(); const email = document.getElementById('loginEmail').value; const password = document.getElementById('loginPassword').value; // 顯示載入狀態 const submitBtn = loginForm.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.innerHTML = '登入中...'; submitBtn.disabled = true; // 發送登入請求到後端 fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: email, password: password }) }) .then(response => { // 檢查回應內容類型 const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { // 如果收到 HTML 回應,表示可能是錯誤頁面 return response.text().then(text => { console.error('收到非 JSON 回應:', text.substring(0, 200)); throw new Error('伺服器回應格式錯誤'); }); } return response.json(); }) .then(data => { if (data && data.success === true) { // 更新導航欄 updateNavbarAfterLogin(data.user.name); // 檢查收容所瀏覽權限 checkShelterAccessPermission(); // 關閉模態框 const modal = bootstrap.Modal.getInstance(loginModal); if (modal) { modal.hide(); } // 顯示成功訊息 showAlert(data.message, 'success'); // 重置表單 loginForm.reset(); } else { showAlert(data ? (data.message || '登入失敗') : '伺服器無回應', 'danger'); } }) .catch(error => { console.error('登入錯誤:', error); showAlert('登入失敗:' + (error.message || '請檢查網路連線或稍後再試'), 'danger'); }) .finally(() => { submitBtn.innerHTML = originalText; submitBtn.disabled = false; }); }); } // 註冊表單提交 if (registerForm) { registerForm.addEventListener('submit', function(e) { e.preventDefault(); const name = document.getElementById('registerName').value; const email = document.getElementById('registerEmail').value; const password = document.getElementById('registerPassword').value; const confirmPassword = document.getElementById('confirmPassword').value; const agreePrivacy = document.getElementById('agreePrivacy'); // 驗證密碼 if (password !== confirmPassword) { showAlert('密碼確認不一致,請重新輸入', 'danger'); return; } // 驗證個資同意 if (!agreePrivacy || !agreePrivacy.checked) { showAlert('請閱讀並同意個人資料保護聲明', 'warning'); return; } // 顯示載入狀態 const submitBtn = registerForm.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.innerHTML = '註冊中...'; submitBtn.disabled = true; // 發送註冊請求到後端 fetch('/api/register', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name: name, email: email, password: password, confirm_password: confirmPassword }) }) .then(response => { // 檢查回應內容類型 const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { // 如果收到 HTML 回應,表示可能是錯誤頁面 return response.text().then(text => { console.error('收到非 JSON 回應:', text.substring(0, 200)); throw new Error('伺服器回應格式錯誤'); }); } return response.json(); }) .then(data => { if (data.success) { // 更新導航欄 updateNavbarAfterLogin(data.user.name); // 檢查收容所瀏覽權限 checkShelterAccessPermission(); // 顯示問卷表單 if (data.show_questionnaire) { try { localStorage.setItem('autoStartPreferenceAfterQuestionnaire', 'true'); } catch(e) {} showQuestionnaireModal(data.user.name); } // 關閉註冊模態框 const modal = bootstrap.Modal.getInstance(registerModal); modal.hide(); // 重置表單 registerForm.reset(); } else { showAlert(data.message || '註冊失敗', 'danger'); } }) .catch(error => { console.error('註冊錯誤:', error); showAlert('註冊失敗:' + (error.message || '請檢查網路連線或稍後再試'), 'danger'); }) .finally(() => { submitBtn.innerHTML = originalText; submitBtn.disabled = false; }); }); } // 忘記密碼表單提交(第一步:驗證 email 和暱稱) const forgotPasswordForm = document.getElementById('forgotPasswordForm'); if (forgotPasswordForm) { forgotPasswordForm.addEventListener('submit', function(e) { e.preventDefault(); const email = document.getElementById('forgotPasswordEmail').value; const name = document.getElementById('forgotPasswordName').value; if (!email || !name) { showAlert('請輸入完整的電子郵件和暱稱', 'warning'); return; } // 顯示載入狀態 const submitBtn = forgotPasswordForm.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.innerHTML = '驗證中...'; submitBtn.disabled = true; // 發送驗證請求到後端 fetch('/api/forgot-password', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: email, name: name }) }) .then(response => { // 檢查回應內容類型 const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { return response.text().then(text => { console.error('收到非 JSON 回應:', text.substring(0, 200)); throw new Error('伺服器回應格式錯誤'); }); } return response.json(); }) .then(data => { if (data && data.success === true && data.token) { // 驗證成功,顯示第二步(重設密碼表單) document.getElementById('forgotPasswordStep1').style.display = 'none'; document.getElementById('forgotPasswordStep2').style.display = 'block'; document.getElementById('resetTokenHidden').value = data.token; } else { showAlert(data ? (data.message || '驗證失敗') : '伺服器無回應', 'danger'); submitBtn.innerHTML = originalText; submitBtn.disabled = false; } }) .catch(error => { console.error('忘記密碼錯誤:', error); showAlert('驗證失敗:' + (error.message || '請檢查網路連線或稍後再試'), 'danger'); submitBtn.innerHTML = originalText; submitBtn.disabled = false; }); }); } // 重置密碼表單提交(第二步:重設密碼) const resetPasswordFormModal = document.getElementById('resetPasswordFormModal'); if (resetPasswordFormModal) { resetPasswordFormModal.addEventListener('submit', function(e) { e.preventDefault(); const token = document.getElementById('resetTokenHidden').value; const password = document.getElementById('newPasswordModal').value; const confirmPassword = document.getElementById('confirmNewPasswordModal').value; if (!token) { showAlert('驗證資訊已過期,請重新申請', 'danger'); resetForgotPasswordForm(); return; } if (password !== confirmPassword) { showAlert('密碼確認不一致,請重新輸入', 'danger'); return; } if (password.length < 6) { showAlert('密碼長度至少需要6個字元', 'warning'); return; } // 顯示載入狀態 const submitBtn = resetPasswordFormModal.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.innerHTML = '處理中...'; submitBtn.disabled = true; // 發送重設密碼請求 fetch('/api/reset-password', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ token: token, password: password, confirm_password: confirmPassword }) }) .then(response => response.json()) .then(data => { if (data && data.success === true) { showAlert(data.message || '密碼重設成功!', 'success'); // 關閉模態框 const forgotPasswordModal = document.getElementById('forgotPasswordModal'); const modal = bootstrap.Modal.getInstance(forgotPasswordModal); if (modal) { modal.hide(); } // 重置表單並返回第一步 resetForgotPasswordForm(); // 延遲後跳轉到登入 setTimeout(() => { const loginModal = document.getElementById('loginModal'); if (loginModal) { const bsModal = new bootstrap.Modal(loginModal); bsModal.show(); } }, 1500); } else { showAlert(data ? (data.message || '重設失敗') : '伺服器無回應', 'danger'); submitBtn.innerHTML = originalText; submitBtn.disabled = false; } }) .catch(error => { console.error('重設密碼錯誤:', error); showAlert('重設失敗:' + (error.message || '請檢查網路連線或稍後再試'), 'danger'); submitBtn.innerHTML = originalText; submitBtn.disabled = false; }); }); } // 重置忘記密碼表單的函數 window.resetForgotPasswordForm = function() { document.getElementById('forgotPasswordStep1').style.display = 'block'; document.getElementById('forgotPasswordStep2').style.display = 'none'; document.getElementById('forgotPasswordForm').reset(); document.getElementById('resetPasswordFormModal').reset(); document.getElementById('resetTokenHidden').value = ''; } // 更新導航欄為登入後狀態 function updateNavbarAfterLogin(userName) { const navItem = document.querySelector('.nav-item:last-child'); navItem.innerHTML = `
`; } // 登出功能 window.logout = function() { fetch('/api/logout', { method: 'POST', headers: { 'Content-Type': 'application/json', } }) .then(response => response.json()) .then(data => { if (data.success) { // 重置導航欄為登入前狀態 const navItem = document.querySelector('.nav-item:last-child'); navItem.innerHTML = ` `; // 隱藏問卷連結 const questionnaireNav = document.getElementById('questionnaireNav'); if (questionnaireNav) { questionnaireNav.style.display = 'none'; } // 隱藏模型評分連結 const modelAccuracyNavItem = document.getElementById('modelAccuracyNavItem'); if (modelAccuracyNavItem) { modelAccuracyNavItem.style.display = 'none'; } // 如果在需要登入的頁面,重定向到首頁 const currentPath = window.location.pathname; if (currentPath === '/start_match' || currentPath.startsWith('/start_match')) { window.location.href = '/'; } else { showAlert(data.message, 'info'); } } else { showAlert('登出失敗', 'danger'); } }) .catch(error => { console.error('Error:', error); showAlert('登出失敗,請稍後再試', 'danger'); }); } // 顯示提示訊息 function showAlert(message, type) { // 移除現有的提示訊息 const existingAlert = document.querySelector('.alert'); if (existingAlert) { existingAlert.remove(); } // 創建新的提示訊息 const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`; alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; alertDiv.innerHTML = ` ${message} `; document.body.appendChild(alertDiv); // 3秒後自動移除 setTimeout(() => { if (alertDiv.parentNode) { alertDiv.remove(); } }, 3000); } // 初始化區域選擇下拉選單 function initRegionDropdown() { const shelterList = document.getElementById('shelterList'); const regionDropdown = document.getElementById('regionDropdown'); // 載入收容所列表 fetch('/front_header_footer/shelters') .then(response => { return response.json(); }) .then(data => { if (data.success && data.data) { // 清空現有內容 shelterList.innerHTML = ''; // 新增收容所選項 data.data.forEach(shelter => { const li = document.createElement('li'); // 使用 textContent 和 setAttribute 避免 XSS 問題 const link = document.createElement('a'); link.className = 'dropdown-item'; link.href = '#'; link.setAttribute('data-region', shelter); link.textContent = shelter; li.appendChild(link); shelterList.appendChild(li); }); // 綁定點擊事件 bindRegionClickEvents(); } else { console.error('API返回資料格式錯誤:', data); } }) .catch(error => { console.error('載入收容所列表失敗:', error); // 如果API失敗,顯示預設選項 shelterList.innerHTML = `為了提供更精準的寵物配對服務,我們需要了解您的需求和偏好。
接下來將進行:
這些資料將幫助系統為您推薦最適合的毛寶貝!