|
|
|
@ -76,17 +76,17 @@
|
|
|
|
|
background-color: #2196F3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#saveWordBtn:hover {
|
|
|
|
|
background-color: #0b7dda;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.icon {
|
|
|
|
|
margin-right: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
0% { transform: rotate(0deg); }
|
|
|
|
|
100% { transform: rotate(360deg); }
|
|
|
|
|
0% {
|
|
|
|
|
transform: rotate(0deg);
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.doc-links {
|
|
|
|
@ -96,18 +96,6 @@
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.doc-link {
|
|
|
|
|
padding: 8px 15px;
|
|
|
|
|
background-color: #e7f3fe;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
color: #2196F3;
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.doc-link:hover {
|
|
|
|
|
background-color: #d0e3fa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.doc-checkboxes {
|
|
|
|
|
display: flex;
|
|
|
|
@ -200,39 +188,44 @@
|
|
|
|
|
|
|
|
|
|
<div class="examples">
|
|
|
|
|
<div class="example-category">
|
|
|
|
|
<h3>问题示例:</h3>
|
|
|
|
|
<h3>已收集问题示例</h3>
|
|
|
|
|
<div class="example-list">
|
|
|
|
|
<div class="example-item" onclick="fillExample('小学数学中有哪些模型?')">小学数学中有哪些模型?</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('帮我写一下 "如何理解点、线、面、体、角"的教学设计')">
|
|
|
|
|
帮我写一下 "如何理解点、线、面、体、角"的教学设计
|
|
|
|
|
</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('苏轼的好朋友都有谁?')">苏轼的好朋友都有谁?</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('苏轼的家人都有谁?')">苏轼的家人都有谁</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('微积分的基本定理是什么?')">微积分的基本定理是什么?</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('线性代数在AI中的应用')">线性代数在AI中的应用</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('帮我写一下 " 如何理解点、线、面、体、角
|
|
|
|
|
"的教学设计')">
|
|
|
|
|
帮我写一下 "如何理解点、线、面、体、角"的教学设计
|
|
|
|
|
</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('苏轼的好朋友都有谁?')">苏轼的好朋友都有谁?</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('苏轼的家人都有谁?')">苏轼的家人都有谁</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="doc-links">
|
|
|
|
|
<h3>知识库范围</h3>
|
|
|
|
|
<div class="doc-checkboxes">
|
|
|
|
|
<label>
|
|
|
|
|
<input type="radio" name="topic" value="Math" checked>
|
|
|
|
|
小学数学
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<input type="radio" name="topic" value="Chinese">
|
|
|
|
|
苏轼知识
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="example-category">
|
|
|
|
|
<h3>未收集问题示例</h3>
|
|
|
|
|
<div class="example-item" onclick="fillExample('微积分的基本定理是什么?')">微积分的基本定理是什么?</div>
|
|
|
|
|
<div class="example-item" onclick="fillExample('线性代数在AI中的应用')">线性代数在AI中的应用</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button id="submitBtn" onclick="submitQuestion()"><span class="icon">💡</span>提问</button>
|
|
|
|
|
<button id="clearBtn" onclick="clearAll()"><span class="icon">🗑️</span>清空</button>
|
|
|
|
|
<div class="doc-links">
|
|
|
|
|
<h3>知识库范围</h3>
|
|
|
|
|
<div class="doc-checkboxes">
|
|
|
|
|
<label>
|
|
|
|
|
<input type="radio" name="topic" value="Math" checked>
|
|
|
|
|
小学数学
|
|
|
|
|
</label>
|
|
|
|
|
<label>
|
|
|
|
|
<input type="radio" name="topic" value="Chinese">
|
|
|
|
|
苏轼知识
|
|
|
|
|
</label>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button id="submitBtn" onclick="submitQuestion()"><span class="icon">💡</span>提问</button>
|
|
|
|
|
<button id="clearBtn" onclick="clearAll()"><span class="icon">🗑️</span>清空</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
// 在页面加载时显示加载动画
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
@ -289,15 +282,15 @@
|
|
|
|
|
const question = document.getElementById('questionInput').value.trim();
|
|
|
|
|
const answerArea = document.getElementById('answerArea');
|
|
|
|
|
const topic = document.querySelector('input[name="topic"]:checked').value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!question) {
|
|
|
|
|
alert('请输入问题!');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 添加加载动画
|
|
|
|
|
answerArea.innerHTML = '<div class="loading-animation"><div class="spinner"></div><div>思考中...</div></div>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fetch('/api/rag', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
@ -309,55 +302,55 @@
|
|
|
|
|
topic: topic
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.then(response => {
|
|
|
|
|
const reader = response.body.getReader();
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
|
let buffer = '';
|
|
|
|
|
let accumulatedContent = '';
|
|
|
|
|
|
|
|
|
|
function processChunk() {
|
|
|
|
|
return reader.read().then(({done, value}) => {
|
|
|
|
|
if (done) return;
|
|
|
|
|
|
|
|
|
|
buffer += decoder.decode(value, {stream: true});
|
|
|
|
|
const lines = buffer.split('\n');
|
|
|
|
|
buffer = lines.pop();
|
|
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
if (line.includes('data:')) {
|
|
|
|
|
const jsonStr = line.replace(/^data:\s*/, '').replace(/^data:\s*/, '').trim();
|
|
|
|
|
if (jsonStr) {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
if (data.reply) {
|
|
|
|
|
accumulatedContent += data.reply;
|
|
|
|
|
answerArea.innerHTML = marked.parse(accumulatedContent, markedOptions);
|
|
|
|
|
MathJax.typesetPromise();
|
|
|
|
|
.then(response => {
|
|
|
|
|
const reader = response.body.getReader();
|
|
|
|
|
const decoder = new TextDecoder();
|
|
|
|
|
let buffer = '';
|
|
|
|
|
let accumulatedContent = '';
|
|
|
|
|
|
|
|
|
|
function processChunk() {
|
|
|
|
|
return reader.read().then(({done, value}) => {
|
|
|
|
|
if (done) return;
|
|
|
|
|
|
|
|
|
|
buffer += decoder.decode(value, {stream: true});
|
|
|
|
|
const lines = buffer.split('\n');
|
|
|
|
|
buffer = lines.pop();
|
|
|
|
|
|
|
|
|
|
for (const line of lines) {
|
|
|
|
|
if (line.includes('data:')) {
|
|
|
|
|
const jsonStr = line.replace(/^data:\s*/, '').replace(/^data:\s*/, '').trim();
|
|
|
|
|
if (jsonStr) {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
if (data.reply) {
|
|
|
|
|
accumulatedContent += data.reply;
|
|
|
|
|
answerArea.innerHTML = marked.parse(accumulatedContent, markedOptions);
|
|
|
|
|
MathJax.typesetPromise();
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.log('忽略解析错误:', e);
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.log('忽略解析错误:', e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return processChunk().then(() => {
|
|
|
|
|
// 在流处理完成后保存完整的markdown内容
|
|
|
|
|
localStorage.setItem('lastMarkdownContent', accumulatedContent);
|
|
|
|
|
|
|
|
|
|
return processChunk().then(() => {
|
|
|
|
|
// 在流处理完成后保存完整的markdown内容
|
|
|
|
|
localStorage.setItem('lastMarkdownContent', accumulatedContent);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return processChunk();
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
// 移除加载动画
|
|
|
|
|
answerArea.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
console.error('Error:', error);
|
|
|
|
|
// 出错时也移除加载动画
|
|
|
|
|
answerArea.innerHTML = '<div style="color:red">请求出错,请重试</div>';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return processChunk();
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
// 移除加载动画
|
|
|
|
|
answerArea.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
console.error('Error:', error);
|
|
|
|
|
// 出错时也移除加载动画
|
|
|
|
|
answerArea.innerHTML = '<div style="color:red">请求出错,请重试</div>';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clearAll() {
|
|
|
|
|