html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat with OpenAI</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
}
#input-section, #history-section {
margin-bottom: 20px;
}
#output {
padding: 10px;
background-color: #f1f1f1;
min-height: 50px;
margin-top: 10px;
white-space: pre-wrap;
}
#history {
padding: 10px;
background-color: #f9f9f9;
margin-top: 20px;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<h2>Chat with OpenAI</h2>
<div id="input-section">
<input type="text" id="user-input" placeholder="Type your question..." style="width: 80%;">
<button onclick="sendMessage()">Send</button>
</div>
<div id="output"></div>
<div id="history-section">
<h3>Chat History</h3>
<div id="history"></div>
</div>
<script>
//JS部分详见下面
</script>
</body>
</html>
完成比较简陋,需要的自己美化界面。流式输出,存储本地列表。
javascript
const API_KEY = ''; // 替换为你的OpenAI API密钥
const API_URL = '';
// 获取并展示本地历史记录
function loadHistory() {
const historyContainer = document.getElementById('history');
const chatHistory = JSON.parse(localStorage.getItem('chatHistory')) || [];
historyContainer.innerHTML = '';
chatHistory.forEach(entry => {
const question = document.createElement('p');
question.textContent = `Q: ${entry.question}`;
const answer = document.createElement('p');
answer.textContent = `A: ${entry.answer}`;
historyContainer.appendChild(question);
historyContainer.appendChild(answer);
historyContainer.appendChild(document.createElement('hr'));
});
}
// 发送用户问题并流式获取答案
async function sendMessage() {
const userInput = document.getElementById('user-input').value;
if (!userInput) return;
document.getElementById('output').textContent = 'Thinking...';
document.getElementById('user-input').value = '';
try {
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: "gpt-3.5-turbo",
messages: [{"role": "user", "content": userInput}],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let responseText = '';
document.getElementById('output').textContent = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 将流返回的数据块转为文本
const chunk = decoder.decode(value, { stream: true });
// 解析流返回的JSON字符串
const lines = chunk.trim().split("\n");
for (const line of lines) {
if (line.startsWith("data:")) {
const jsonStr = line.replace("data: ", "").trim();
if (jsonStr !== "[DONE]") {
const parsedData = JSON.parse(jsonStr);
const content = parsedData.choices[0].delta.content || '';
responseText += content;
document.getElementById('output').textContent = responseText;
}
}
}
}
saveToHistory(userInput, responseText);
} catch (error) {
document.getElementById('output').textContent = 'Error: ' + error.message;
}
}
// 保存对话记录到localStorage
function saveToHistory(question, answer) {
const chatHistory = JSON.parse(localStorage.getItem('chatHistory')) || [];
chatHistory.push({ question, answer });
localStorage.setItem('chatHistory', JSON.stringify(chatHistory));
loadHistory();
}
// 初次加载历史记录
loadHistory();