document.addEventListener('DOMContentLoaded', () => { // ───────────────────────────────────────────────────────────────────────── // 1. 文字分析相關元素 const inputTextArea = document.getElementById('predict_info'); const inputButton = document.getElementById('detect_button'); const clearButton = document.getElementById('clear_button'); const normalOrScam = document.getElementById('is_scam'); const confidenceScoreSpan = document.getElementById('confidence_score'); const suspiciousPhrasesDiv = document.getElementById('suspicious_phrases'); const feedbackArea = document.getElementById('feedback_area'); const feedbackCorrectBtn = document.getElementById('feedback_correct'); const feedbackWrongBtn = document.getElementById('feedback_wrong'); const feedbackStatus = document.getElementById('feedback_status'); let lastPrediction = null; const TEXT_API = '/predict'; const FEEDBACK_API = '/feedback'; // 文字檢測:當按「檢測!」時觸發 inputButton.addEventListener('click', async () => { const message = inputTextArea.value.trim(); if (!message) { alert('請輸入您想檢測的訊息內容。'); return; } // 顯示 loading normalOrScam.textContent = '檢測中...'; normalOrScam.style.color = 'gray'; confidenceScoreSpan.textContent = '計算中...'; suspiciousPhrasesDiv.innerHTML = '

正在分析訊息,請稍候...

'; feedbackArea.style.display = 'none'; feedbackStatus.textContent = ''; feedbackCorrectBtn.style.display = 'inline-block'; feedbackWrongBtn.style.display = 'inline-block'; try { const res = await fetch(TEXT_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: message }) }); if (!res.ok) throw new Error(`伺服器錯誤: ${res.status}`); const data = await res.json(); // 更新文字分析結果 normalOrScam.textContent = data.status; confidenceScoreSpan.textContent = data.confidence + '%'; // 可疑詞句 suspiciousPhrasesDiv.innerHTML = ''; if (Array.isArray(data.suspicious_keywords) && data.suspicious_keywords.length) { const ul = document.createElement('ul'); data.suspicious_keywords.forEach(kw => { const li = document.createElement('li'); li.textContent = kw; ul.appendChild(li); }); suspiciousPhrasesDiv.appendChild(ul); } else { suspiciousPhrasesDiv.innerHTML = '

沒有偵測到可疑詞句。

'; } // 顯示回饋區 feedbackArea.style.display = 'block'; lastPrediction = { text: message, model_status: data.status }; } catch (err) { console.error('文字檢測失敗:', err); alert(`文字檢測失敗,請檢查後端。\n錯誤:${err.message}`); // 還原預設文字區 normalOrScam.textContent = '待檢測'; normalOrScam.style.color = 'inherit'; confidenceScoreSpan.textContent = '待檢測'; suspiciousPhrasesDiv.innerHTML = '

請輸入訊息並點擊「檢測!」按鈕。

'; } }); // 「清除」按鈕:清空文字、圖片結果 clearButton.addEventListener('click', () => { inputTextArea.value = ''; // 清空文字檢測結果 normalOrScam.textContent = '待檢測'; normalOrScam.style.color = 'inherit'; confidenceScoreSpan.textContent = '待檢測'; suspiciousPhrasesDiv.innerHTML = '

請輸入訊息並點擊「檢測!」按鈕。

'; feedbackArea.style.display = 'none'; feedbackStatus.textContent = ''; // 清空圖片OCR結果 extractedTextSpan.textContent = '尚未上傳圖片'; imgIsScamSpan.textContent = '尚未上傳圖片'; imgConfidenceSpan.textContent = '尚未上傳圖片'; imgSuspiciousDiv.innerHTML = '

尚未上傳圖片。

'; imageUpload.value = ''; }); // 使用者回饋 feedbackCorrectBtn.addEventListener('click', () => submitFeedback('正確')); feedbackWrongBtn.addEventListener('click', () => submitFeedback('錯誤')); async function submitFeedback(user_feedback) { if (!lastPrediction) return; const payload = { ...lastPrediction, user_feedback }; try { const r = await fetch(FEEDBACK_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); await r.json(); feedbackStatus.textContent = '✅ 感謝您的回饋!'; feedbackCorrectBtn.style.display = 'none'; feedbackWrongBtn.style.display = 'none'; } catch (e) { feedbackStatus.textContent = '❌ 回饋提交失敗'; } } // ───────────────────────────────────────────────────────────────────────── // ───────────────────────────────────────────────────────────────────────── // 2. 圖片OCR相關元素 const imageUpload = document.getElementById('image_upload'); const imageButton = document.getElementById('image_button'); const extractedTextSpan = document.getElementById('extracted_text'); const imgIsScamSpan = document.getElementById('img_is_scam'); const imgConfidenceSpan = document.getElementById('img_confidence'); const imgSuspiciousDiv = document.getElementById('img_suspicious_phrases'); const IMAGE_API = '/analyze-image'; imageButton.addEventListener('click', async () => { // 確認 user 已經選擇檔案 if (!imageUpload.files.length) { alert('請先選擇一個圖片檔案。'); return; } // 顯示 Loading extractedTextSpan.textContent = '辨識中...'; imgIsScamSpan.textContent = '辨識中...'; imgConfidenceSpan.textContent = '辨識中...'; imgSuspiciousDiv.innerHTML = '

正在分析圖片,請稍候...

'; // 建立 FormData,append 檔案 const formData = new FormData(); formData.append('file', imageUpload.files[0]); try { const resp = await fetch(IMAGE_API, { method: 'POST', body: formData }); if (!resp.ok) throw new Error(`伺服器錯誤: ${resp.status}`); const data = await resp.json(); // 此 data 格式:{ extracted_text: "...", analysis_result: { status, confidence, suspicious_keywords } } const extracted = data.extracted_text || ''; const result = data.analysis_result || {}; // 更新前端OCR結果 extractedTextSpan.textContent = extracted || '(無文字)'; imgIsScamSpan.textContent = result.status || '(無法辨識)'; imgConfidenceSpan.textContent = (result.confidence !== undefined) ? result.confidence + '%' : '(無法辨識)'; imgSuspiciousDiv.innerHTML = ''; if (Array.isArray(result.suspicious_keywords) && result.suspicious_keywords.length) { const ul2 = document.createElement('ul'); result.suspicious_keywords.forEach(kw => { const li2 = document.createElement('li'); li2.textContent = kw; ul2.appendChild(li2); }); imgSuspiciousDiv.appendChild(ul2); } else { imgSuspiciousDiv.innerHTML = '

無可疑詞句。

'; } } catch (err) { console.error('圖片辨識失敗:', err); extractedTextSpan.textContent = '(圖片辨識失敗)'; imgIsScamSpan.textContent = '(失敗)'; imgConfidenceSpan.textContent = '0%'; imgSuspiciousDiv.innerHTML = `

❌ ${err.message}

`; } }); // ───────────────────────────────────────────────────────────────────────── });