html
func InitHeader(w http.ResponseWriter) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization")
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>聊天演示 - 流式/非流式输出</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header {
text-align: center;
margin-bottom: 30px;
color: #333;
}
.input-section {
margin-bottom: 20px;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #555;
}
input, textarea, select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
box-sizing: border-box;
}
textarea {
resize: vertical;
min-height: 100px;
}
.button-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
button {
flex: 1;
padding: 12px;
border: none;
border-radius: 5px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.btn-stream {
background-color: #007AFF;
color: white;
}
.btn-stream:hover {
background-color: #0056D6;
}
.btn-normal {
background-color: #34C759;
color: white;
}
.btn-normal:hover {
background-color: #28A745;
}
.btn-clear {
background-color: #FF3B30;
color: white;
}
.btn-clear:hover {
background-color: #D70015;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.output-section {
border-top: 1px solid #eee;
padding-top: 20px;
}
.output-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.output-title {
font-weight: 600;
color: #333;
}
.status-indicator {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.status-waiting {
background-color: #F2F2F7;
color: #8E8E93;
}
.status-streaming {
background-color: #E3F2FD;
color: #1976D2;
}
.status-completed {
background-color: #E8F5E8;
color: #2E7D32;
}
.status-error {
background-color: #FFEBEE;
color: #C62828;
}
.output-box {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 5px;
padding: 15px;
min-height: 200px;
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
font-size: 13px;
line-height: 1.5;
white-space: pre-wrap;
overflow-y: auto;
max-height: 400px;
}
.loading {
display: inline-block;
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.error {
color: #d32f2f;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>聊天演示</h1>
<p>测试流式和非流式输出</p>
</div>
<div class="input-section">
<div class="input-group">
<label for="model">模型:</label>
<input type="text" id="model" value="doubao-seed-1-6-251015">
</div>
<div class="input-group">
<label for="message">消息:</label>
<textarea id="message" placeholder="请输入您的问题...">50字介绍勾股定理</textarea>
</div>
<div class="input-group">
<label for="msgId">消息ID:</label>
<input type="text" id="msgId" value="666">
</div>
</div>
<div class="button-group">
<button class="btn-stream" onclick="sendStreamRequest()">流式输出</button>
<button class="btn-normal" onclick="sendNormalRequest()">普通输出</button>
<button class="btn-clear" onclick="clearOutput()">清空输出</button>
</div>
<div class="output-section">
<div class="output-header">
<span class="output-title">响应结果</span>
<span id="status" class="status-indicator status-waiting">等待中</span>
</div>
<div id="output" class="output-box">等待发送请求...</div>
</div>
</div>
<script>
const API_URL = 'http://localhost:8080/chat/send';
function updateStatus(status, text) {
const statusElement = document.getElementById('status');
statusElement.className = `status-indicator status-${status}`;
statusElement.textContent = text;
}
function appendOutput(text, replace = false) {
const outputElement = document.getElementById('output');
if (replace) {
outputElement.textContent = text;
} else {
outputElement.textContent += text;
}
outputElement.scrollTop = outputElement.scrollHeight;
}
function getRequestData() {
return {
model: document.getElementById('model').value,
message: document.getElementById('message').value,
msgId: document.getElementById('msgId').value,
stream: true
};
}
function setButtonsDisabled(disabled) {
document.querySelectorAll('button').forEach(btn => {
btn.disabled = disabled;
});
}
async function sendStreamRequest() {
const data = { ...getRequestData(), stream: true };
try {
setButtonsDisabled(true);
updateStatus('streaming', '流式输出中...');
appendOutput('', true);
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) {
updateStatus('completed', '流式输出完成');
break;
}
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (line.trim()) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
updateStatus('completed', '流式输出完成');
break;
}
try {
const parsed = JSON.parse(data);
if (parsed.content) {
appendOutput(parsed.content);
}
} catch (e) {
appendOutput(data);
}
}
}
}
}
} catch (error) {
console.error('流式请求错误:', error);
updateStatus('error', '请求失败');
appendOutput(`错误: ${error.message}`, true);
} finally {
setButtonsDisabled(false);
}
}
async function sendNormalRequest() {
const data = { ...getRequestData(), stream: false };
try {
setButtonsDisabled(true);
updateStatus('streaming', '请求处理中...');
appendOutput('正在处理请求...', true);
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
updateStatus('completed', '请求完成');
appendOutput(JSON.stringify(result, null, 2), true);
} catch (error) {
console.error('普通请求错误:', error);
updateStatus('error', '请求失败');
appendOutput(`错误: ${error.message}`, true);
} finally {
setButtonsDisabled(false);
}
}
function clearOutput() {
appendOutput('等待发送请求...', true);
updateStatus('waiting', '等待中');
}
// 自动生成随机消息ID
function generateMsgId() {
document.getElementById('msgId').value = Date.now().toString();
}
// 页面加载时生成随机ID
window.onload = function() {
generateMsgId();
}
// 双击消息ID输入框生成新ID
document.getElementById('msgId').addEventListener('dblclick', generateMsgId);
</script>
</body>
</html>
作者:admin 创建时间:2025-11-16 23:06
最后编辑:admin 更新时间:2025-11-16 23:16
最后编辑:admin 更新时间:2025-11-16 23:16