Spaces:
Running
Running
| <html lang="zh-Hant"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>PKM_RAG 系統登入</title> | |
| <style> | |
| /* --- 基本與通用樣式 --- */ | |
| body, | |
| html { | |
| margin: 0; | |
| padding: 0; | |
| font-family: 'Poppins', sans-serif, 'Microsoft JhengHei', sans-serif; | |
| height: 100vh; | |
| overflow: hidden; | |
| } | |
| /* --- 登入頁面 (來自 HTML02) --- */ | |
| #login-section { | |
| background: url("https://images.unsplash.com/photo-1503264116251-35a269479413") no-repeat center center/cover; | |
| height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| transition: opacity 0.6s ease-out; | |
| } | |
| .login-container { | |
| background: rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(15px); | |
| border-radius: 20px; | |
| padding: 40px; | |
| width: 380px; | |
| text-align: center; | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); | |
| animation: fadeIn 1.5s ease; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-50px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .login-container h2 { | |
| color: white; | |
| margin-bottom: 25px; | |
| font-size: 2em; | |
| } | |
| .input-box { | |
| margin-bottom: 20px; | |
| text-align: left; | |
| } | |
| .input-box label { | |
| color: white; | |
| font-weight: bold; | |
| display: block; | |
| margin-bottom: 8px; | |
| } | |
| .input-box input { | |
| width: 100%; | |
| padding: 12px; | |
| border: 1px solid transparent; | |
| border-radius: 10px; | |
| background: rgba(255, 255, 255, 0.2); | |
| color: white; | |
| font-size: 16px; | |
| outline: none; | |
| transition: all 0.3s ease; | |
| box-sizing: border-box; | |
| } | |
| .input-box input::placeholder { | |
| color: rgba(255, 255, 255, 0.7); | |
| } | |
| .input-box input:focus { | |
| background: rgba(255, 255, 255, 0.3); | |
| box-shadow: 0 0 10px rgba(0, 242, 254, 0.5); | |
| border-color: rgba(0, 242, 254, 0.7); | |
| } | |
| .captcha-container { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-top: 5px; | |
| } | |
| #captcha-input { | |
| width: 55%; | |
| margin: 0; | |
| } | |
| #captcha-display { | |
| font-size: 24px; | |
| font-weight: bold; | |
| padding: 5px 15px; | |
| border-radius: 10px; | |
| background: rgba(0, 0, 0, 0.3); | |
| color: #fff; | |
| letter-spacing: 4px; | |
| user-select: none; | |
| cursor: pointer; | |
| text-decoration: line-through; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| } | |
| .btn { | |
| width: 100%; | |
| padding: 12px; | |
| border: none; | |
| border-radius: 10px; | |
| background: linear-gradient(135deg, #4facfe, #00f2fe); | |
| color: white; | |
| font-size: 18px; | |
| font-weight: bold; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| margin-top: 15px; | |
| } | |
| .btn:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 0 15px rgba(0, 242, 254, 0.6); | |
| background: linear-gradient(135deg, #00f2fe, #4facfe); | |
| } | |
| .msg { | |
| margin-top: 15px; | |
| font-size: 14px; | |
| color: #ffc107; | |
| font-weight: bold; | |
| min-height: 20px; | |
| } | |
| .msg.error { | |
| color: #ff4d4d; | |
| } | |
| /* --- 主系統頁面 --- */ | |
| #main-app-section { | |
| display: flex; | |
| height: 100vh; | |
| opacity: 0; | |
| transition: opacity 0.6s ease-in; | |
| } | |
| .sidebar { | |
| width: 260px; | |
| background-color: #1c2833; | |
| color: white; | |
| display: flex; | |
| flex-direction: column; | |
| padding: 20px; | |
| box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); | |
| box-sizing: border-box; | |
| } | |
| .sidebar h1 { | |
| font-size: 24px; | |
| text-align: center; | |
| color: #ecf0f1; | |
| margin: 0 0 30px 0; | |
| border-bottom: 1px solid #2c3e50; | |
| padding-bottom: 15px; | |
| } | |
| .tabs { | |
| flex-grow: 1; | |
| } | |
| .tab-button { | |
| width: 100%; | |
| padding: 15px 20px; | |
| border: none; | |
| border-radius: 8px; | |
| margin-bottom: 10px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| font-size: 16px; | |
| text-align: left; | |
| background-color: #2c3e50; | |
| color: white; | |
| } | |
| .tab-button.active { | |
| background-color: #3498db; | |
| transform: translateX(5px); | |
| } | |
| .tab-button:not(.active):hover { | |
| background-color: #34495e; | |
| transform: translateX(2px); | |
| } | |
| #logout-button { | |
| padding: 12px; | |
| border: none; | |
| border-radius: 10px; | |
| background-color: #e74c3c; | |
| color: white; | |
| font-size: 16px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| } | |
| #logout-button:hover { | |
| background-color: #c0392b; | |
| transform: translateY(-2px); | |
| } | |
| .content-area { | |
| flex-grow: 1; | |
| background-color: #ecf0f1; | |
| position: relative; | |
| } | |
| .tab-content { | |
| width: 100%; | |
| height: 100%; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| display: none; | |
| } | |
| .tab-content.active { | |
| opacity: 1; | |
| display: block; | |
| } | |
| .content-area iframe { | |
| width: 100%; | |
| height: 100%; | |
| border: none; | |
| } | |
| /* --- 顯示/隱藏控制 --- */ | |
| .hidden { | |
| display: none ; | |
| opacity: 0 ; | |
| } | |
| /* 添加測試帳號顯示區域 */ | |
| .test-info { | |
| position: fixed; | |
| bottom: 20px; | |
| right: 20px; | |
| background: rgba(0, 0, 0, 0.8); | |
| color: white; | |
| padding: 15px; | |
| border-radius: 10px; | |
| font-size: 12px; | |
| z-index: 1000; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <section id="login-section"> | |
| <div class="login-container"> | |
| <h2>PKM_RAG System</h2> | |
| <form id="login-form" autocomplete="off"> | |
| <div class="input-box"> | |
| <label for="username">帳號 (Username)</label> | |
| <input type="text" id="username" placeholder="請輸入帳號" required> | |
| </div> | |
| <div class="input-box"> | |
| <label for="password">密碼 (Password)</label> | |
| <input type="password" id="password" placeholder="請輸入密碼" required> | |
| </div> | |
| <div class="input-box"> | |
| <label for="captcha-input">驗證碼 (Captcha)</label> | |
| <div class="captcha-container"> | |
| <input type="text" id="captcha-input" placeholder="輸入右側文字" required> | |
| <span id="captcha-display" title="點擊更換驗證碼"></span> | |
| </div> | |
| </div> | |
| <button class="btn" type="submit">登入 (Login)</button> | |
| <p class="msg" id="msg"></p> | |
| </form> | |
| </div> | |
| </section> | |
| <section id="main-app-section" class="hidden"> | |
| <div class="sidebar"> | |
| <h1>主選單</h1> | |
| <div class="tabs"> | |
| <button class="tab-button active" onclick="openTab(event, 'iframe01')">ESG爬蟲</button> | |
| <button class="tab-button" onclick="openTab(event, 'iframe02')">多模態API 應用</button> | |
| <button class="tab-button" onclick="openTab(event, 'iframe03')">Grop+Linebot+Webcam</button> | |
| <button class="tab-button" onclick="openTab(event, 'iframe04')">RAG知識庫</button> | |
| <button class="tab-button" onclick="openTab(event, 'iframe05')">Gemini比對+Resend</button> | |
| </div> | |
| <button id="logout-button">回到登入頁面</button> | |
| </div> | |
| <div class="content-area"> | |
| <div id="iframe01" class="tab-content active"> | |
| <iframe src="https://z9760405-steamlit.hf.space" frameborder="0"></iframe> | |
| </div> | |
| <div id="iframe02" class="tab-content"> | |
| <iframe src="https://cjian2025-groq-api-gradio.hf.space" frameborder="0"></iframe> | |
| </div> | |
| <div id="iframe03" class="tab-content"> | |
| <iframe src="https://z9760405-webcam-groq-linebot-application.hf.space" frameborder="0" | |
| allow="camera; microphone"></iframe> | |
| </div> | |
| <div id="iframe04" class="tab-content"> | |
| <iframe src="https://z9760405-resendlangchain.hf.space" frameborder="0"></iframe> | |
| </div> | |
| <div id="iframe05" class="tab-content"> | |
| <iframe src="https://z9760405-gemini-ocr-resend0914.hf.space" frameborder="0"></iframe> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- 測試用帳號密碼顯示 --> | |
| <div class="test-info"> | |
| <strong>測試帳號:</strong><br> | |
| 帳號: ROOT2025 (不區分大小寫)<br> | |
| 密碼: 123456<br> | |
| 驗證碼: 點擊右側文字更換 | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| console.log('頁面載入完成,開始初始化...'); | |
| // --- DOM 元素 --- | |
| const loginSection = document.getElementById('login-section'); | |
| const mainAppSection = document.getElementById('main-app-section'); | |
| const loginForm = document.getElementById('login-form'); | |
| const usernameInput = document.getElementById('username'); | |
| const passwordInput = document.getElementById('password'); | |
| const captchaInput = document.getElementById('captcha-input'); | |
| const captchaDisplay = document.getElementById('captcha-display'); | |
| const msg = document.getElementById('msg'); | |
| const logoutButton = document.getElementById('logout-button'); | |
| // 檢查所有 DOM 元素是否正確載入 | |
| const elements = { | |
| loginSection, mainAppSection, loginForm, usernameInput, | |
| passwordInput, captchaInput, captchaDisplay, msg, logoutButton | |
| }; | |
| for (const [name, element] of Object.entries(elements)) { | |
| if (!element) { | |
| console.error(`錯誤: 找不到元素 ${name}`); | |
| return; | |
| } | |
| } | |
| console.log('所有 DOM 元素載入成功'); | |
| // --- 預先計算好的 SHA-256 HASH 值 --- | |
| // 正確帳號: ROOT2025 -> sha256 -> d7113846e19051e6d5752f60dcb53329e9563b2c7655452fb9aea5f27969cc4f | |
| const correctUserHash = 'd7113846e19051e6d5752f60dcb53329e9563b2c7655452fb9aea5f27969cc4f'; | |
| // 正確密碼: 123456 -> sha256 -> 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 | |
| const correctPassHash = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'; | |
| let currentCaptcha = ''; | |
| // --- 功能函式 --- | |
| async function sha256(str) { | |
| try { | |
| const textBuffer = new TextEncoder().encode(str); | |
| const hashBuffer = await crypto.subtle.digest('SHA-256', textBuffer); | |
| const hashArray = Array.from(new Uint8Array(hashBuffer)); | |
| const hash = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); | |
| console.log(`SHA256("${str}") = ${hash}`); | |
| return hash; | |
| } catch (error) { | |
| console.error('SHA256 計算錯誤:', error); | |
| throw error; | |
| } | |
| } | |
| function generateCaptcha() { | |
| const chars = 'AbCdEfGhIjKlMnOpQrStUvWxYz0123456789'; | |
| let captcha = ''; | |
| for (let i = 0; i < 5; i++) { | |
| captcha += chars.charAt(Math.floor(Math.random() * chars.length)); | |
| } | |
| currentCaptcha = captcha; | |
| captchaDisplay.textContent = currentCaptcha; | |
| console.log('新驗證碼生成:', currentCaptcha); | |
| } | |
| function showMessage(message, isError = true) { | |
| msg.textContent = message; | |
| msg.className = isError ? 'msg error' : 'msg'; | |
| console.log('顯示訊息:', message, '錯誤狀態:', isError); | |
| } | |
| async function handleLogin(e) { | |
| e.preventDefault(); | |
| console.log('開始處理登入...'); | |
| showMessage('', false); | |
| const username = usernameInput.value.trim(); | |
| const password = passwordInput.value; | |
| const captcha = captchaInput.value.trim(); | |
| console.log('輸入資訊:'); | |
| console.log('- 帳號:', username); | |
| console.log('- 密碼長度:', password.length); | |
| console.log('- 驗證碼:', captcha); | |
| console.log('- 當前驗證碼:', currentCaptcha); | |
| // 步驟 1: 優先檢查驗證碼 | |
| if (captcha.toLowerCase() !== currentCaptcha.toLowerCase()) { | |
| console.log('驗證碼錯誤'); | |
| showMessage('❌ 驗證碼錯誤!請重新輸入。'); | |
| generateCaptcha(); | |
| captchaInput.value = ''; | |
| return; | |
| } | |
| try { | |
| // 步驟 2: 雜湊使用者輸入 | |
| const enteredUserHash = await sha256(username.toUpperCase()); | |
| const enteredPassHash = await sha256(password); | |
| console.log('Hash 比對:'); | |
| console.log('- 輸入帳號 Hash:', enteredUserHash); | |
| console.log('- 正確帳號 Hash:', correctUserHash); | |
| console.log('- 輸入密碼 Hash:', enteredPassHash); | |
| console.log('- 正確密碼 Hash:', correctPassHash); | |
| // 步驟 3: 比對 HASH 值 | |
| if (enteredUserHash === correctUserHash && enteredPassHash === correctPassHash) { | |
| // 登入成功 | |
| console.log('登入成功!'); | |
| showMessage('✅ 登入成功!正在載入系統...', false); | |
| loginSection.style.opacity = '0'; | |
| setTimeout(() => { | |
| loginSection.classList.add('hidden'); | |
| mainAppSection.classList.remove('hidden'); | |
| mainAppSection.style.opacity = '1'; | |
| console.log('切換到主頁面完成'); | |
| }, 600); | |
| } else { | |
| // 登入失敗 | |
| console.log('帳號或密碼錯誤'); | |
| showMessage('❌ 帳號或密碼錯誤!'); | |
| generateCaptcha(); | |
| passwordInput.value = ''; | |
| captchaInput.value = ''; | |
| } | |
| } catch (error) { | |
| console.error('登入處理錯誤:', error); | |
| showMessage('❌ 系統錯誤,請重試!'); | |
| } | |
| } | |
| function handleLogout() { | |
| console.log('處理登出...'); | |
| mainAppSection.style.opacity = '0'; | |
| setTimeout(() => { | |
| mainAppSection.classList.add('hidden'); | |
| loginSection.classList.remove('hidden'); | |
| loginSection.style.opacity = '1'; | |
| loginForm.reset(); | |
| showMessage(''); | |
| generateCaptcha(); | |
| console.log('登出完成'); | |
| }, 600); | |
| } | |
| // --- 事件監聽 --- | |
| loginForm.addEventListener('submit', handleLogin); | |
| logoutButton.addEventListener('click', handleLogout); | |
| captchaDisplay.addEventListener('click', generateCaptcha); | |
| // --- 初始化 --- | |
| console.log('開始初始化驗證碼...'); | |
| generateCaptcha(); | |
| console.log('系統初始化完成'); | |
| // 測試用途:10秒後隱藏測試資訊 | |
| setTimeout(() => { | |
| const testInfo = document.querySelector('.test-info'); | |
| if (testInfo) { | |
| testInfo.style.display = 'none'; | |
| } | |
| }, 10000); | |
| }); | |
| // --- 選單切換功能 --- | |
| function openTab(evt, tabName) { | |
| console.log('切換到頁籤:', tabName); | |
| // 隱藏所有內容 | |
| var tabContents = document.getElementsByClassName("tab-content"); | |
| for (var i = 0; i < tabContents.length; i++) { | |
| tabContents[i].classList.remove("active"); | |
| } | |
| // 移除所有按鈕的 active 類別 | |
| var tabButtons = document.getElementsByClassName("tab-button"); | |
| for (var i = 0; i < tabButtons.length; i++) { | |
| tabButtons[i].classList.remove("active"); | |
| } | |
| // 顯示選中的內容並激活按鈕 | |
| document.getElementById(tabName).classList.add("active"); | |
| evt.currentTarget.classList.add("active"); | |
| console.log('頁籤切換完成:', tabName); | |
| } | |
| </script> | |
| </body> | |
| </html> |