|
<!DOCTYPE html> |
|
<html lang="zh"> |
|
|
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;700&display=swap" rel="stylesheet"> |
|
<title>聊天助手</title> |
|
<style> |
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
} |
|
|
|
body { |
|
font-family: 'Noto Sans', sans-serif; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
height: 100vh; |
|
background-color: #f5f5f5; |
|
} |
|
|
|
select { |
|
width: 20%; |
|
padding: 12px 15px; |
|
font-size: 18px; |
|
border: 1px solid #ddd; |
|
border-radius: 8px; |
|
background-color: #fafafa; |
|
color: #333; |
|
cursor: pointer; |
|
box-sizing: border-box; |
|
} |
|
|
|
select:focus { |
|
border-color: #007bff; |
|
outline: none; |
|
} |
|
|
|
option { |
|
padding: 10px; |
|
} |
|
|
|
.chatbox { |
|
height: fit-content; |
|
width: 90%; |
|
background-color: white; |
|
border-radius: 20px; |
|
margin-top: 10px; |
|
margin-bottom: 10px; |
|
} |
|
|
|
.myInputArea { |
|
display: flex; |
|
justify-content: center; |
|
margin-top: 10px; |
|
} |
|
|
|
.myMessageArea { |
|
display: flex; |
|
justify-content: flex-end; |
|
margin-top: 10px; |
|
} |
|
|
|
.GPTmessageArea { |
|
display: flex; |
|
justify-content: flex-start; |
|
margin-top: 10px; |
|
} |
|
|
|
.myMessageBox { |
|
height: fit-content; |
|
width: fit-content; |
|
margin: 10px; |
|
padding: 10px; |
|
border-radius: 10px; |
|
background-color: rgb(195, 252, 233); |
|
} |
|
|
|
.GPTmessageBox { |
|
height: fit-content; |
|
width: fit-content; |
|
margin: 10px; |
|
padding: 10px; |
|
border-radius: 10px; |
|
background-color: rgb(252, 206, 238); |
|
} |
|
|
|
.myInputBox { |
|
height: fit-content; |
|
width: 85%; |
|
margin: 10px; |
|
padding: 10px; |
|
border-radius: 10px; |
|
background-color: #f5f5f5; |
|
} |
|
|
|
.addMessageButton { |
|
width: 35px; |
|
height: 35px; |
|
font-size: 15px; |
|
font-weight: bold; |
|
border: solid 1px #a7a7a7; |
|
border-radius: 10px; |
|
background-color: white; |
|
cursor: pointer; |
|
} |
|
|
|
.sendMessageButton { |
|
background-color: #007bff; |
|
|
|
color: white; |
|
font-size: 16px; |
|
padding: 10px 20px; |
|
border: none; |
|
border-radius: 5px; |
|
cursor: pointer; |
|
transition: background 0.3s; |
|
} |
|
|
|
.sendMessageButton:hover { |
|
background-color: #0056b3; |
|
} |
|
|
|
.addMessageButton:hover { |
|
background-color: rgb(209, 209, 209); |
|
} |
|
|
|
.messagePending { |
|
margin-left: 5px; |
|
width: 70%; |
|
padding: 12px 15px; |
|
font-size: 16px; |
|
border: 2px solid #ccc; |
|
border-radius: 8px; |
|
background-color: #fafafa; |
|
color: #333; |
|
outline: none; |
|
box-sizing: border-box; |
|
transition: border 0.3s ease, box-shadow 0.3s ease; |
|
} |
|
|
|
.messagePending:focus { |
|
border-color: #007bff; |
|
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); |
|
} |
|
|
|
.messagePending::placeholder { |
|
color: #888; |
|
font-style: italic; |
|
} |
|
|
|
#imgInputBox { |
|
width: 100%; |
|
border: solid 1px #a7a7a7; |
|
border-radius: 10px; |
|
height: 50px; |
|
} |
|
|
|
#textInputBox { |
|
margin-top: 10px; |
|
width: 100%; |
|
border: solid 1px #a7a7a7; |
|
border-radius: 10px; |
|
height: 200px; |
|
padding: 10px; |
|
overflow: auto; |
|
} |
|
</style> |
|
</head> |
|
|
|
<body> |
|
<div id="chatbox" class="chatbox"> |
|
|
|
|
|
<div class="myInputArea"> |
|
<div class="myInputBox"> |
|
<div id="imgInputBox"> |
|
<div style="display: flex; justify-content: center; align-items: center; height: 100%;"> |
|
<input type="file" id="imageUpload" accept="image/*" style="display: none;"> |
|
<button onclick="document.getElementById('imageUpload').click()" style="padding: 5px 10px; background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 5px; cursor: pointer;">選擇圖片</button> |
|
<span id="selectedFileName" style="margin-left: 10px; font-size: 14px;"></span> |
|
</div> |
|
</div> |
|
<div style="text-align: center;"> |
|
或 |
|
</div> |
|
<div id="textInputBox"> |
|
|
|
<div style="display: flex;flex-wrap: nowrap;justify-content: end; margin-top: 10px;"> |
|
<select id="character0" name="character" style="width: 90px;"> |
|
<option value="self">自己</option> |
|
<option value="usr">對方</option> |
|
</select> |
|
<input type="text" id="messagePending0" name="messagePending" class="messagePending"> |
|
</div> |
|
|
|
|
|
<div style="display: flex;justify-content: end; margin-top: 10px;"> |
|
<button class="addMessageButton" onclick="addMessageBox()">+</button> |
|
</div> |
|
</div> |
|
|
|
<div style="display: flex;justify-content: end;margin-top: 10px;"> |
|
<button class="sendMessageButton" onclick="sendMessage()">傳送</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
<script> |
|
let messageCount = 1; |
|
let selectedImage = null; |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
const imageUpload = document.getElementById('imageUpload'); |
|
if (imageUpload) { |
|
imageUpload.addEventListener('change', handleImageSelect); |
|
} |
|
}); |
|
|
|
|
|
function handleImageSelect(event) { |
|
const file = event.target.files[0]; |
|
if (file) { |
|
selectedImage = file; |
|
document.getElementById('selectedFileName').textContent = file.name; |
|
|
|
|
|
uploadImage(); |
|
} |
|
} |
|
|
|
|
|
function uploadImage() { |
|
if (!selectedImage) { |
|
alert('請先選擇一張圖片'); |
|
return; |
|
} |
|
|
|
const formData = new FormData(); |
|
formData.append('file', selectedImage); |
|
|
|
|
|
const myInputArea = document.querySelector(".myInputArea"); |
|
if (myInputArea) { |
|
myInputArea.remove(); |
|
} |
|
|
|
|
|
const chatbox = document.getElementById("chatbox"); |
|
const myMessageArea = document.createElement("div"); |
|
myMessageArea.className = "myMessageArea"; |
|
myMessageArea.innerHTML = ` |
|
<div class="myMessageBox"> |
|
<p>上傳的圖片: ${selectedImage.name}</p> |
|
</div> |
|
`; |
|
chatbox.appendChild(myMessageArea); |
|
|
|
|
|
const GPTmessageArea = document.createElement("div"); |
|
GPTmessageArea.className = "GPTmessageArea"; |
|
GPTmessageArea.innerHTML = ` |
|
<div class="GPTmessageBox"> |
|
<p>(等待回應......)</p> |
|
</div> |
|
`; |
|
chatbox.appendChild(GPTmessageArea); |
|
|
|
|
|
fetch('/api/process-image', { |
|
method: 'POST', |
|
body: formData |
|
}) |
|
.then(response => { |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! Status: ${response.status}`); |
|
} |
|
return response.json(); |
|
}) |
|
.then(data => { |
|
console.log("Server Response:", data); |
|
|
|
|
|
const analysis = data.analysis || "無分析"; |
|
const suggestion = data.suggestion || "無建議"; |
|
const chatText = data.chat_text || ""; |
|
|
|
|
|
let formattedChatText = chatText; |
|
if (formattedChatText.startsWith("<chat>") && formattedChatText.endsWith("</chat>")) { |
|
formattedChatText = formattedChatText.substring(6, formattedChatText.length - 7); |
|
} |
|
|
|
|
|
formattedChatText = formattedChatText |
|
.replace(/<self>(.*?)<\/self>/g, "我:$1") |
|
.replace(/<usr>(.*?)<\/usr>/g, "你:$1"); |
|
|
|
|
|
GPTmessageArea.innerHTML = ` |
|
<div class="GPTmessageBox"> |
|
<p><strong>分析:</strong><br>${analysis}</p> |
|
<p><strong>建議回覆:</strong><br>${suggestion}</p> |
|
</div> |
|
`; |
|
}) |
|
.catch(error => { |
|
console.error("Error uploading image:", error); |
|
GPTmessageArea.innerHTML = ` |
|
<div class="GPTmessageBox"> |
|
<p>(錯誤:無法取得回應)</p> |
|
<p>錯誤詳情:${error.message}</p> |
|
</div> |
|
`; |
|
}) |
|
.finally(() => { |
|
|
|
resetInputArea(); |
|
}); |
|
} |
|
|
|
|
|
function resetInputArea() { |
|
const chatbox = document.getElementById("chatbox"); |
|
const newMyInputArea = document.createElement("div"); |
|
newMyInputArea.className = "myInputArea"; |
|
newMyInputArea.innerHTML = ` |
|
<div class="myInputBox"> |
|
<div id="imgInputBox"> |
|
<div style="display: flex; justify-content: center; align-items: center; height: 100%;"> |
|
<input type="file" id="imageUpload" accept="image/*" style="display: none;"> |
|
<button onclick="document.getElementById('imageUpload').click()" style="padding: 5px 10px; background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 5px; cursor: pointer;">選擇圖片</button> |
|
<span id="selectedFileName" style="margin-left: 10px; font-size: 14px;"></span> |
|
</div> |
|
</div> |
|
<div style="text-align: center;"> |
|
或 |
|
</div> |
|
<div id="textInputBox"> |
|
<!--已建立的訊息填寫框--> |
|
<div style="display: flex;flex-wrap: nowrap;justify-content: end; margin-top: 10px;"> |
|
<select id="character0" name="character"> |
|
<option value="self">自己</option> |
|
<option value="usr">對方</option> |
|
</select> |
|
<input type="text" id="messagePending0" name="messagePending" class="messagePending"> |
|
</div> |
|
|
|
<!--建立新的訊息填寫框--> |
|
<div style="display: flex;justify-content: end; margin-top: 10px;"> |
|
<button class="addMessageButton" onclick="addMessageBox()">+</button> |
|
</div> |
|
</div> |
|
<!--傳送訊息--> |
|
<div style="display: flex;justify-content: end;margin-top: 10px;"> |
|
<button class="sendMessageButton" onclick="sendMessage()">傳送</button> |
|
</div> |
|
</div> |
|
`; |
|
chatbox.appendChild(newMyInputArea); |
|
|
|
|
|
const imageUpload = document.getElementById('imageUpload'); |
|
if (imageUpload) { |
|
imageUpload.addEventListener('change', handleImageSelect); |
|
} |
|
|
|
|
|
selectedImage = null; |
|
messageCount = 1; |
|
} |
|
|
|
|
|
function addMessageBox() { |
|
const newMessageBox = document.createElement('div'); |
|
newMessageBox.style.display = "flex"; |
|
newMessageBox.style.justifyContent = "end"; |
|
newMessageBox.style.marginTop = "10px"; |
|
|
|
|
|
const deleteButton = document.createElement('button'); |
|
deleteButton.innerText = 'X'; |
|
deleteButton.style.marginRight = '10px'; |
|
deleteButton.style.cursor = 'pointer'; |
|
deleteButton.style.background = 'red'; |
|
deleteButton.style.color = 'white'; |
|
deleteButton.style.border = 'none'; |
|
deleteButton.style.borderRadius = '50%'; |
|
deleteButton.style.width = '25px'; |
|
deleteButton.style.height = '25px'; |
|
deleteButton.style.textAlign = 'center'; |
|
deleteButton.style.fontSize = '16px'; |
|
|
|
|
|
deleteButton.addEventListener('click', function() { |
|
newMessageBox.remove(); |
|
}); |
|
|
|
|
|
newMessageBox.innerHTML = ` |
|
<select id="character${messageCount}" name="character" style="width: 90px;"> |
|
<option value="self">自己</option> |
|
<option value="usr">對方</option> |
|
</select> |
|
<input type="text" id="messagePending${messageCount}" name="messagePending" class="messagePending"> |
|
`; |
|
|
|
|
|
newMessageBox.insertBefore(deleteButton, newMessageBox.firstChild); |
|
|
|
|
|
document.getElementById('textInputBox').insertBefore(newMessageBox, document.querySelector('.addMessageButton').parentNode); |
|
messageCount++; |
|
} |
|
|
|
function sendMessage() { |
|
let result = "<chat>"; |
|
let formattedMessage = ""; |
|
|
|
for (let i = 0; i < messageCount; i++) { |
|
try { |
|
const character = document.getElementById(`character${i}`); |
|
const messageElement = document.getElementById(`messagePending${i}`); |
|
if (!character || !messageElement) continue; |
|
|
|
const message = messageElement.value.trim(); |
|
if (message) { |
|
result += `<${character.value}>${message}</${character.value}>`; |
|
formattedMessage += `${character.value === 'self' ? "您" : "對方"}: ${message}<br>`; |
|
} |
|
} catch (error) { |
|
console.warn(`Message box ${i} does not exist. Skipping.`); |
|
continue; |
|
} |
|
} |
|
result += "</chat>"; |
|
|
|
|
|
const myInputArea = document.querySelector(".myInputArea"); |
|
if (myInputArea) { |
|
myInputArea.remove(); |
|
} |
|
|
|
|
|
const chatbox = document.getElementById("chatbox"); |
|
const myMessageArea = document.createElement("div"); |
|
myMessageArea.className = "myMessageArea"; |
|
myMessageArea.innerHTML = ` |
|
<div class="myMessageBox"> |
|
<p>${formattedMessage}</p> |
|
</div> |
|
`; |
|
chatbox.appendChild(myMessageArea); |
|
|
|
|
|
const GPTmessageArea = document.createElement("div"); |
|
GPTmessageArea.className = "GPTmessageArea"; |
|
GPTmessageArea.innerHTML = ` |
|
<div class="GPTmessageBox"> |
|
<p>(等待回應......)</p> |
|
</div> |
|
`; |
|
chatbox.appendChild(GPTmessageArea); |
|
|
|
|
|
const data = { |
|
chat_text: result |
|
}; |
|
fetch('/api/process-text', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: JSON.stringify(data) |
|
}) |
|
.then(response => { |
|
if (!response.ok) { |
|
throw new Error(`HTTP error! Status: ${response.status}`); |
|
} |
|
return response.json(); |
|
}) |
|
.then(data => { |
|
console.log("Server Response:", data); |
|
|
|
|
|
const analysis = data.analysis || "無分析"; |
|
const suggestion = data.suggestion || "無建議"; |
|
|
|
|
|
let formattedChatText = result; |
|
if (formattedChatText.startsWith("<chat>") && formattedChatText.endsWith("</chat>")) { |
|
formattedChatText = formattedChatText.substring(6, formattedChatText.length - 7); |
|
} |
|
|
|
|
|
formattedChatText = formattedChatText |
|
.replace(/<self>(.*?)<\/self>/g, "我:$1") |
|
.replace(/<usr>(.*?)<\/usr>/g, "你:$1"); |
|
|
|
|
|
GPTmessageArea.innerHTML = ` |
|
<div class="GPTmessageBox"> |
|
<p><strong>分析:</strong><br>${analysis}</p> |
|
<p><strong>建議回覆:</strong><br>${suggestion}</p> |
|
</div> |
|
`; |
|
}) |
|
.catch(error => { |
|
console.error("Error sending message:", error); |
|
GPTmessageArea.innerHTML = ` |
|
<div class="GPTmessageBox"> |
|
<p>(錯誤:無法取得回應)</p> |
|
<p>錯誤詳情:${error.message}</p> |
|
</div> |
|
`; |
|
}) |
|
.finally(() => { |
|
|
|
resetInputArea(); |
|
}); |
|
} |
|
</script> |
|
</body> |
|
|
|
</html> |