@ -413,6 +413,12 @@
< button class = "btn btn-outline-primary btn-sm w-100" onclick = "getAISuggestions()" >
< i class = "fas fa-lightbulb me-1" > < / i > 获取写作建议
< / button >
< button class = "btn btn-outline-warning btn-sm w-100" onclick = "checkGrammar()" >
< i class = "fas fa-spell-check me-1" > < / i > 语法检查
< / button >
< button class = "btn btn-outline-info btn-sm w-100" onclick = "vocabularyUpgrade()" >
< i class = "fas fa-language me-1" > < / i > 词汇升级
< / button >
< / div >
< div class = "ai-feedback" id = "aiFeedback" >
@ -463,6 +469,21 @@
display: flex;
flex-direction: column;
}
.ai-assistant {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
position: sticky;
top: 20px;
height: auto; /* 改为自动高度 */
min-height: 400px; /* 适当的最小高度 */
display: flex;
flex-direction: column;
}
@media (max-width: 1200px) {
.writing-content.writing-flex {
flex-direction: column;
@ -616,6 +637,7 @@
padding: 6px 12px;
}
/*
.ai-assistant {
background: white;
border-radius: 10px;
@ -628,7 +650,7 @@
display: flex;
flex-direction: column;
}
*/
.ai-header {
margin-bottom: 15px;
text-align: center;
@ -1154,6 +1176,75 @@
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* 语法检查特定样式 */
.grammar-check-result {
height: auto; /* 高度自适应 */
overflow-y: auto;
}
.error-item {
border-left: 4px solid #dc3545;
transition: all 0.3s ease;
}
.error-item:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform: translateY(-1px);
}
.error-item code {
background: #f8f9fa;
padding: 4px 8px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
border: 1px solid #e9ecef;
}
.recommendation {
border-left: 4px solid #0dcaf0;
}
/* 词汇升级特定样式 */
.vocabulary-upgrade-result {
height: auto;
overflow-y: auto;
}
.suggestion-item {
border-left: 4px solid #28a745;
transition: all 0.3s ease;
}
.suggestion-item:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transform: translateY(-1px);
}
.alternative-item {
border: 1px solid #dee2e6;
transition: all 0.3s ease;
}
.alternative-item:hover {
border-color: #28a745;
background-color: #f8fff9 !important;
}
.assessment-content {
line-height: 1.6;
color: #495057;
}
.usage code {
background: transparent;
color: #f8f9fa;
border: none;
font-family: 'Courier New', monospace;
}
.learning-tips li, .next-steps li {
border-radius: 6px;
}
< / style >
{% endblock %}
@ -1446,6 +1537,213 @@ async function getAISuggestions() {
}
}
// 词汇升级功能
async function vocabularyUpgrade() {
const textarea = document.getElementById(currentStage + 'Content');
const content = textarea.value.trim();
if (!content) {
Utils.showAlert('请先输入要升级的内容', 'warning');
return;
}
// 获取AI设置
const userSettings = Utils.getUserSettings();
if (!userSettings.aiProvider || !userSettings.aiModel) {
Utils.showAlert('请先配置AI设置', 'warning');
return;
}
// 构建AI设置对象
const parsedSettings = {
provider: userSettings.aiProvider,
model: userSettings.aiModel,
base_url: userSettings.aiBaseUrl,
api_key: localStorage.getItem('aiApiKey') || ''
};
showLoading('AI正在分析词汇并生成升级建议...');
try {
// 获取项目信息
const projectResponse = await fetch(`/api/projects/${projectId}`);
let grade = '初中';
let subject = '语文';
if (projectResponse.ok) {
const projectData = await projectResponse.json();
if (projectData.success & & projectData.data) {
grade = projectData.data.grade || '初中';
subject = projectData.data.subject || '语文';
}
}
const response = await fetch('/api/ai/vocabulary_upgrade', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
project_id: projectId,
content: content,
grade: grade,
subject: subject,
ai_provider: parsedSettings.provider,
ai_model: parsedSettings.model,
ai_base_url: parsedSettings.base_url,
ai_api_key: parsedSettings.api_key
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const result = await response.json();
if (result.success) {
displayVocabularyResult(result.data);
} else {
Utils.showAlert('词汇升级失败:' + result.message, 'error');
}
} catch (error) {
console.error('词汇升级错误:', error);
Utils.showAlert('词汇升级失败,请检查网络连接', 'error');
} finally {
hideLoading();
}
}
// 显示词汇升级结果
function displayVocabularyResult(vocabularyData) {
const feedbackDiv = document.getElementById('aiFeedback');
let html = '< div class = "vocabulary-upgrade-result" > ';
// 总体评估
html += '< div class = "vocabulary-header mb-4 p-3 bg-light rounded" > ';
html += '< h6 class = "text-primary mb-2" > < i class = "fas fa-language me-2" > < / i > 词汇升级报告< / h6 > ';
if (vocabularyData.overall_assessment) {
html += `< div class = "mb-3" >
< div class = "fw-bold text-secondary mb-1" > 总体评估:< / div >
< div class = "assessment-content" > ${vocabularyData.overall_assessment}< / div >
< / div > `;
}
if (vocabularyData.total_suggestions !== undefined) {
const suggestionCount = parseInt(vocabularyData.total_suggestions);
html += `< div class = "d-flex align-items-center mb-2" >
< span class = "me-3" > 升级建议:< / span >
< span class = "badge ${suggestionCount > 0 ? 'bg-success' : 'bg-secondary'}" > ${suggestionCount} 个词汇可升级< / span >
< / div > `;
}
html += '< / div > ';
// 词汇建议列表
if (vocabularyData.vocabulary_suggestions & & vocabularyData.vocabulary_suggestions.length > 0) {
html += '< div class = "vocabulary-suggestions mb-4" > ';
html += '< h6 class = "text-success mb-3" > < i class = "fas fa-lightbulb me-2" > < / i > 词汇升级建议< / h6 > ';
vocabularyData.vocabulary_suggestions.forEach((suggestion, index) => {
html += `< div class = "suggestion-item mb-4 p-3 border rounded" > `;
html += `< div class = "suggestion-header d-flex justify-content-between align-items-center mb-3" > `;
html += `< div >
< span class = "badge bg-primary me-2" > 建议 ${index + 1}< / span >
< span class = "fw-bold text-danger" > ${suggestion.original_word || '未知词汇'}< / span >
< span class = "text-muted ms-2" > →< / span >
< / div > `;
html += `< span class = "badge bg-secondary" > ${suggestion.difficulty_level || '中级'}< / span > `;
html += `< / div > `;
if (suggestion.explanation) {
html += `< div class = "explanation mb-3" >
< div class = "text-muted small" > 升级理由:< / div >
< div class = "fw-bold" > ${suggestion.explanation}< / div >
< / div > `;
}
// 替代词汇
if (suggestion.alternatives & & suggestion.alternatives.length > 0) {
html += '< div class = "alternatives" > ';
html += '< div class = "text-success fw-bold mb-2" > 推荐替代词汇:< / div > ';
suggestion.alternatives.forEach((alternative, altIndex) => {
html += `< div class = "alternative-item mb-3 p-2 bg-light rounded" > `;
html += `< div class = "d-flex align-items-center mb-2" >
< span class = "badge bg-success me-2" > ${altIndex + 1}< / span >
< span class = "fw-bold text-primary" > ${alternative.word || '未知词汇'}< / span >
< / div > `;
if (alternative.meaning) {
html += `< div class = "meaning mb-2" >
< div class = "text-muted small" > 含义:< / div >
< div > ${alternative.meaning}< / div >
< / div > `;
}
if (alternative.usage_example) {
html += `< div class = "usage" >
< div class = "text-muted small" > 使用示例:< / div >
< div class = "bg-dark text-white p-2 rounded mt-1" >
< code > ${alternative.usage_example}< / code >
< / div >
< / div > `;
}
html += `< / div > `;
});
html += '< / div > ';
}
html += `< / div > `;
});
html += '< / div > ';
} else {
html += '< div class = "text-center text-muted py-4" > ';
html += '< i class = "fas fa-check-circle fa-3x mb-3 text-success" > < / i > ';
html += '< p class = "mb-0" > 恭喜!当前词汇使用恰当,暂无升级建议< / p > ';
html += '< / div > ';
}
// 学习建议
if (vocabularyData.learning_tips & & vocabularyData.learning_tips.length > 0) {
html += '< div class = "learning-tips mb-4" > ';
html += '< h6 class = "text-info mb-3" > < i class = "fas fa-graduation-cap me-2" > < / i > 学习建议< / h6 > ';
html += '< ul class = "list-unstyled" > ';
vocabularyData.learning_tips.forEach(tip => {
html += `< li class = "mb-2 p-2 bg-info bg-opacity-10 rounded" >
< i class = "fas fa-chevron-right text-info me-2" > < / i > ${tip}
< / li > `;
});
html += '< / ul > < / div > ';
}
// 后续步骤
if (vocabularyData.next_steps & & vocabularyData.next_steps.length > 0) {
html += '< div class = "next-steps" > ';
html += '< h6 class = "text-warning mb-3" > < i class = "fas fa-road me-2" > < / i > 后续步骤< / h6 > ';
html += '< ul class = "list-unstyled" > ';
vocabularyData.next_steps.forEach(step => {
html += `< li class = "mb-2 p-2 bg-warning bg-opacity-10 rounded" >
< i class = "fas fa-flag text-warning me-2" > < / i > ${step}
< / li > `;
});
html += '< / ul > < / div > ';
}
html += '< / div > ';
feedbackDiv.innerHTML = html;
// 添加滚动到顶部功能
feedbackDiv.scrollTop = 0;
}
// 显示AI建议
function displayAISuggestions(suggestions) {
const feedbackDiv = document.getElementById('aiFeedback');
@ -1602,6 +1900,226 @@ function getStageTitle(stage) {
}
// 显示AI反馈
/**
* 显示AI反馈的优化版本
* @param {Object} feedback - 包含strengths, issues, suggestions等属性的反馈对象
*/
function displayAIFeedback(feedback) {
const feedbackDiv = document.getElementById('aiFeedback');
if (!feedbackDiv) {
console.error("Feedback container not found");
return;
}
// 创建主容器
const container = document.createElement('div');
container.className = 'ai-feedback-container';
// 1. 添加标题部分
const header = createFeedbackSection('AI分析报告', 'bi-robot', 'text-primary');
container.appendChild(header);
// 2. 添加优点部分
if (feedback.strengths?.length) {
container.appendChild(
createListSection('优点', feedback.strengths, 'bi-check-circle', 'text-success', 'list-group-item-success')
);
}
// 3. 添加改进部分
if (feedback.issues?.length) {
container.appendChild(
createListSection('需要改进', feedback.issues, 'bi-exclamation-triangle', 'text-warning', 'list-group-item-warning')
);
}
// 4. 添加建议部分(带手风琴效果)
if (feedback.suggestions?.length) {
const suggestionsSection = document.createElement('div');
suggestionsSection.className = 'feedback-section mb-4';
const heading = createSectionHeading('建议', 'bi-lightbulb', 'text-primary');
suggestionsSection.appendChild(heading);
const accordion = document.createElement('div');
accordion.className = 'accordion';
accordion.id = 'suggestionsAccordion';
feedback.suggestions.forEach((suggestion, index) => {
accordion.appendChild(createSuggestionItem(suggestion, index));
});
suggestionsSection.appendChild(accordion);
container.appendChild(suggestionsSection);
}
// 5. 添加下一步建议
if (feedback.next_steps?.length) {
container.appendChild(
createListSection('下一步建议', feedback.next_steps, 'bi-arrow-right-circle', 'text-info', 'list-group-item-info')
);
}
// 6. 添加原始响应(调试用)
if (feedback.raw_response) {
const rawSection = document.createElement('div');
rawSection.className = 'feedback-section mt-4';
const rawHeader = createSectionHeading('原始响应', 'bi-code', 'text-secondary');
rawSection.appendChild(rawHeader);
const rawPre = document.createElement('pre');
rawPre.className = 'bg-light p-3 rounded';
rawPre.textContent = feedback.raw_response;
rawSection.appendChild(rawPre);
container.appendChild(rawSection);
}
// 清空并添加新内容
feedbackDiv.innerHTML = '';
feedbackDiv.appendChild(container);
// 初始化Bootstrap组件
initBootstrapComponents();
}
// 辅助函数:创建反馈部分标题
function createSectionHeading(text, icon, colorClass) {
const heading = document.createElement('h5');
heading.className = `${colorClass} mb-3 d-flex align-items-center`;
const iconElem = document.createElement('i');
iconElem.className = `bi ${icon} me-2`;
heading.appendChild(iconElem);
heading.appendChild(document.createTextNode(text));
return heading;
}
// 辅助函数:创建列表类型的反馈部分
function createListSection(title, items, icon, colorClass, itemClass) {
const section = document.createElement('div');
section.className = 'feedback-section mb-4';
section.appendChild(createSectionHeading(title, icon, colorClass));
const list = document.createElement('ul');
list.className = 'list-group';
items.forEach(item => {
const li = document.createElement('li');
li.className = `list-group-item ${itemClass} d-flex align-items-center`;
const icon = document.createElement('i');
icon.className = `bi ${icon} me-2`;
li.appendChild(icon);
li.appendChild(document.createTextNode(item));
list.appendChild(li);
});
section.appendChild(list);
return section;
}
// 辅助函数:创建建议项(带手风琴效果)
function createSuggestionItem(suggestion, index) {
const item = document.createElement('div');
item.className = 'accordion-item border-0 mb-2';
// 标题部分
const header = document.createElement('h6');
header.className = 'accordion-header';
header.id = `heading${index}`;
const button = document.createElement('button');
button.className = 'accordion-button collapsed bg-light';
button.type = 'button';
button.setAttribute('data-bs-toggle', 'collapse');
button.setAttribute('data-bs-target', `#collapse${index}`);
button.setAttribute('aria-expanded', 'false');
button.setAttribute('aria-controls', `collapse${index}`);
button.textContent = suggestion.讲解 || suggestion.suggestion || '建议';
header.appendChild(button);
// 内容部分
const collapse = document.createElement('div');
collapse.id = `collapse${index}`;
collapse.className = 'accordion-collapse collapse';
collapse.setAttribute('aria-labelledby', `heading${index}`);
collapse.setAttribute('data-bs-parent', '#suggestionsAccordion');
const body = document.createElement('div');
body.className = 'accordion-body p-3';
if (suggestion.示例 || suggestion.example) {
const exampleDiv = document.createElement('div');
exampleDiv.className = 'bg-dark text-white p-3 rounded mb-3';
const exampleLabel = document.createElement('small');
exampleLabel.textContent = '示例:';
exampleDiv.appendChild(exampleLabel);
const exampleContent = document.createElement('p');
exampleContent.className = 'mb-0 mt-2';
exampleContent.textContent = suggestion.示例 || suggestion.example;
exampleDiv.appendChild(exampleContent);
body.appendChild(exampleDiv);
}
if (suggestion.讲解 || suggestion.explanation) {
const explanation = document.createElement('p');
explanation.className = 'mb-0';
explanation.textContent = suggestion.讲解 || suggestion.explanation;
body.appendChild(explanation);
}
collapse.appendChild(body);
item.appendChild(header);
item.appendChild(collapse);
return item;
}
// 辅助函数: 初始化Bootstrap组件
function initBootstrapComponents() {
if (typeof bootstrap !== 'undefined') {
// 初始化所有手风琴
const accordions = document.querySelectorAll('.accordion');
accordions.forEach(acc => new bootstrap.Collapse(acc));
// 添加工具提示
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
}
}
// 辅助函数:创建反馈部分
function createFeedbackSection(title, icon, colorClass) {
const section = document.createElement('div');
section.className = 'feedback-header mb-4 p-3 rounded-top';
section.style.backgroundColor = '#f8f9fa';
const heading = document.createElement('h4');
heading.className = `${colorClass} mb-0 d-flex align-items-center`;
const iconElem = document.createElement('i');
iconElem.className = `bi ${icon} me-2`;
heading.appendChild(iconElem);
heading.appendChild(document.createTextNode(title));
section.appendChild(heading);
return section;
}
/*
function displayAIFeedback(feedback) {
const feedbackDiv = document.getElementById('aiFeedback');
@ -1648,6 +2166,171 @@ function displayAIFeedback(feedback) {
feedbackDiv.innerHTML = html;
}
*/
// 显示语法检查结果
function displayGrammarResult(grammarData) {
const feedbackDiv = document.getElementById('aiFeedback');
let html = '< div class = "grammar-check-result" > ';
// 总体评估和得分
html += '< div class = "grammar-header mb-4 p-3 bg-light rounded" > ';
html += '< h6 class = "text-primary mb-2" > < i class = "fas fa-chart-bar me-2" > < / i > 语法检查报告< / h6 > ';
// 得分显示
if (grammarData.score !== undefined & & grammarData.score !== null) {
const score = parseInt(grammarData.score);
let scoreClass = 'text-danger';
if (score >= 80) scoreClass = 'text-success';
else if (score >= 60) scoreClass = 'text-warning';
html += `< div class = "d-flex align-items-center mb-2" >
< span class = "me-3" > 语法得分:< / span >
< span class = "fs-4 fw-bold ${scoreClass}" > ${score}< / span >
< span class = "text-muted ms-1" > /100< / span >
< / div > `;
}
// 错误数量
if (grammarData.total_errors !== undefined) {
const errorCount = parseInt(grammarData.total_errors);
html += `< div class = "d-flex align-items-center mb-2" >
< span class = "me-3" > 发现错误:< / span >
< span class = "badge ${errorCount > 0 ? 'bg-danger' : 'bg-success'}" > ${errorCount} 处< / span >
< / div > `;
}
// 总体评价
if (grammarData.overall_assessment) {
html += `< div class = "mt-2" >
< small class = "text-muted" > 总体评价:< / small >
< div class = "fw-bold" > ${grammarData.overall_assessment}< / div >
< / div > `;
}
html += '< / div > ';
// 错误分类统计
if (grammarData.error_categories & & Object.keys(grammarData.error_categories).length > 0) {
html += '< div class = "error-categories mb-4" > ';
html += '< h6 class = "text-secondary mb-3" > < i class = "fas fa-tags me-2" > < / i > 错误分类统计< / h6 > ';
html += '< div class = "d-flex flex-wrap gap-2" > ';
Object.entries(grammarData.error_categories).forEach(([category, count]) => {
const countNum = parseInt(count) || 0;
if (countNum > 0) {
html += `< span class = "badge bg-warning text-dark" > ${getErrorCategoryName(category)}: ${countNum}< / span > `;
}
});
// 如果没有错误,显示成功标识
if (Object.values(grammarData.error_categories).every(count => (parseInt(count) || 0) === 0)) {
html += `< span class = "badge bg-success" > 恭喜!未发现语法错误< / span > `;
}
html += '< / div > < / div > ';
}
// 错误详情列表
if (grammarData.errors & & grammarData.errors.length > 0) {
html += '< div class = "errors-list" > ';
html += '< h6 class = "text-danger mb-3" > < i class = "fas fa-exclamation-circle me-2" > < / i > 错误详情< / h6 > ';
grammarData.errors.forEach((error, index) => {
html += `< div class = "error-item mb-3 p-3 border rounded" > `;
html += `< div class = "error-header d-flex justify-content-between align-items-center mb-2" > `;
html += `< span class = "badge bg-danger" > 错误 ${index + 1}< / span > `;
html += `< span class = "badge bg-secondary" > ${getErrorTypeName(error.error_type)}< / span > `;
html += `< / div > `;
html += `< div class = "error-content" > `;
// 原始错误文本
if (error.original_text) {
html += `< div class = "mb-2" >
< strong class = "text-danger" > 原句:< / strong >
< code class = "ms-2 p-1 bg-danger bg-opacity-10 text-danger" > ${error.original_text}< / code >
< / div > `;
}
// 错误解释
if (error.explanation) {
html += `< div class = "mb-2" >
< strong > 解释:< / strong >
< span class = "ms-2" > ${error.explanation}< / span >
< / div > `;
}
// 修正后的文本
if (error.corrected_text) {
html += `< div class = "mb-2" >
< strong class = "text-success" > 修正:< / strong >
< code class = "ms-2 p-1 bg-success bg-opacity-10 text-success" > ${error.corrected_text}< / code >
< / div > `;
}
// 改进建议
if (error.suggestion) {
html += `< div class = "mb-2" >
< strong > 建议:< / strong >
< small class = "ms-2 text-muted" > ${error.suggestion}< / small >
< / div > `;
}
html += `< / div > < / div > `;
});
html += '< / div > ';
} else if (grammarData.total_errors === 0 || !grammarData.errors || grammarData.errors.length === 0) {
html += '< div class = "text-center text-success py-4" > ';
html += '< i class = "fas fa-check-circle fa-3x mb-3" > < / i > ';
html += '< p class = "mb-0" > 恭喜!未发现语法错误< / p > ';
html += '< / div > ';
}
// 学习建议
if (grammarData.recommendation) {
html += '< div class = "recommendation mt-4 p-3 bg-info bg-opacity-10 rounded" > ';
html += '< h6 class = "text-info mb-2" > < i class = "fas fa-graduation-cap me-2" > < / i > 学习建议< / h6 > ';
html += `< p class = "mb-0" > ${grammarData.recommendation}< / p > `;
html += '< / div > ';
}
html += '< / div > ';
feedbackDiv.innerHTML = html;
// 添加滚动到顶部功能
feedbackDiv.scrollTop = 0;
}
// 错误类型名称映射
function getErrorTypeName(errorType) {
const typeMap = {
'grammar': '语法错误',
'punctuation': '标点错误',
'word_usage': '用词错误',
'spelling': '拼写错误',
'sentence': '句子结构',
'expression': '表达错误',
'word': '字词错误'
};
return typeMap[errorType] || errorType;
}
// 错误分类名称映射
function getErrorCategoryName(category) {
const categoryMap = {
'grammar': '语法错误',
'punctuation': '标点错误',
'word_usage': '用词错误',
'spelling': '拼写错误',
'word': '字词错误',
'sentence': '句子错误',
'expression': '表达错误'
};
return categoryMap[category] || category;
}
// 完成评分
async function evaluateProject() {
@ -2235,5 +2918,216 @@ function insertContinuation(text) {
Utils.showAlert('续写内容已插入', 'success');
}
// 语法检查功能
async function checkGrammar() {
const textarea = document.getElementById(currentStage + 'Content');
const content = textarea.value.trim();
if (!content) {
Utils.showAlert('请先输入要检查的内容', 'warning');
return;
}
// 获取AI设置
const userSettings = Utils.getUserSettings();
if (!userSettings.aiProvider || !userSettings.aiModel) {
Utils.showAlert('请先配置AI设置', 'warning');
return;
}
// 构建AI设置对象
const parsedSettings = {
provider: userSettings.aiProvider,
model: userSettings.aiModel,
base_url: userSettings.aiBaseUrl,
api_key: localStorage.getItem('aiApiKey') || ''
};
showLoading('AI正在检查语法错误...');
try {
// 获取项目信息
const projectResponse = await fetch(`/api/projects/${projectId}`);
let grade = '初中';
let subject = '语文';
if (projectResponse.ok) {
const projectData = await projectResponse.json();
if (projectData.success & & projectData.data) {
grade = projectData.data.grade || '初中';
subject = projectData.data.subject || '语文';
}
}
const response = await fetch('/api/ai/check_grammar', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
project_id: projectId,
content: content,
grade: grade,
subject: subject,
ai_provider: parsedSettings.provider,
ai_model: parsedSettings.model,
ai_base_url: parsedSettings.base_url,
ai_api_key: parsedSettings.api_key
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const result = await response.json();
if (result.success) {
displayGrammarResult(result.data);
} else {
Utils.showAlert('语法检查失败:' + result.message, 'error');
}
} catch (error) {
console.error('语法检查错误:', error);
Utils.showAlert('语法检查失败,请检查网络连接', 'error');
} finally {
hideLoading();
}
}
// 显示语法检查结果
function displayGrammarResult(grammarData) {
const feedbackDiv = document.getElementById('aiFeedback');
let html = '< div class = "grammar-check-result" > ';
// 总体评估和得分
html += '< div class = "grammar-header mb-4 p-3 bg-light rounded" > ';
html += '< h6 class = "text-primary mb-2" > < i class = "fas fa-chart-bar me-2" > < / i > 语法检查报告< / h6 > ';
if (grammarData.score !== undefined) {
const score = grammarData.score;
let scoreClass = 'text-danger';
if (score >= 90) scoreClass = 'text-success';
else if (score >= 70) scoreClass = 'text-warning';
html += `< div class = "d-flex align-items-center mb-2" >
< span class = "me-3" > 语法得分:< / span >
< span class = "fs-4 fw-bold ${scoreClass}" > ${score}< / span >
< span class = "text-muted ms-1" > /100< / span >
< / div > `;
}
if (grammarData.total_errors !== undefined) {
html += `< div class = "d-flex align-items-center mb-2" >
< span class = "me-3" > 发现错误:< / span >
< span class = "badge ${grammarData.total_errors > 0 ? 'bg-danger' : 'bg-success'}" > ${grammarData.total_errors} 处< / span >
< / div > `;
}
if (grammarData.overall_assessment) {
html += `< div class = "mt-2" >
< small class = "text-muted" > 总体评价:< / small >
< div class = "fw-bold" > ${grammarData.overall_assessment}< / div >
< / div > `;
}
html += '< / div > ';
// 错误分类统计
if (grammarData.error_categories & & Object.keys(grammarData.error_categories).length > 0) {
html += '< div class = "error-categories mb-4" > ';
html += '< h6 class = "text-secondary mb-3" > < i class = "fas fa-tags me-2" > < / i > 错误分类< / h6 > ';
html += '< div class = "d-flex flex-wrap gap-2" > ';
Object.entries(grammarData.error_categories).forEach(([category, count]) => {
if (count > 0) {
html += `< span class = "badge bg-warning text-dark" > ${getErrorCategoryName(category)}: ${count}< / span > `;
}
});
html += '< / div > < / div > ';
}
// 错误详情列表
if (grammarData.errors & & grammarData.errors.length > 0) {
html += '< div class = "errors-list" > ';
html += '< h6 class = "text-danger mb-3" > < i class = "fas fa-exclamation-circle me-2" > < / i > 错误详情< / h6 > ';
grammarData.errors.forEach((error, index) => {
html += `< div class = "error-item mb-3 p-3 border rounded" > `;
html += `< div class = "error-header d-flex justify-content-between align-items-center mb-2" > `;
html += `< span class = "badge bg-danger" > 错误 ${index + 1}< / span > `;
html += `< span class = "badge bg-secondary" > ${getErrorTypeName(error.error_type)}< / span > `;
html += `< / div > `;
html += `< div class = "error-content" > `;
html += `< div class = "mb-2" > < strong > 原句:< / strong > < code class = "text-danger" > ${error.original_text || '未提供'}< / code > < / div > `;
if (error.explanation) {
html += `< div class = "mb-2" > < strong > 解释:< / strong > ${error.explanation}< / div > `;
}
if (error.corrected_text) {
html += `< div class = "mb-2" > < strong > 修正:< / strong > < code class = "text-success" > ${error.corrected_text}< / code > < / div > `;
}
if (error.suggestion) {
html += `< div class = "mb-2" > < strong > 建议:< / strong > < small class = "text-muted" > ${error.suggestion}< / small > < / div > `;
}
html += `< / div > < / div > `;
});
html += '< / div > ';
} else {
html += '< div class = "text-center text-success py-4" > ';
html += '< i class = "fas fa-check-circle fa-3x mb-3" > < / i > ';
html += '< p class = "mb-0" > 恭喜!未发现语法错误< / p > ';
html += '< / div > ';
}
// 学习建议
if (grammarData.recommendation) {
html += '< div class = "recommendation mt-4 p-3 bg-info bg-opacity-10 rounded" > ';
html += '< h6 class = "text-info mb-2" > < i class = "fas fa-graduation-cap me-2" > < / i > 学习建议< / h6 > ';
html += `< p class = "mb-0" > ${grammarData.recommendation}< / p > `;
html += '< / div > ';
}
html += '< / div > ';
feedbackDiv.innerHTML = html;
}
// 错误类型名称映射
function getErrorTypeName(errorType) {
const typeMap = {
'grammar': '语法错误',
'punctuation': '标点错误',
'word_usage': '用词错误',
'spelling': '拼写错误',
'sentence': '句子结构',
'expression': '表达错误',
'word': '字词错误'
};
return typeMap[errorType] || errorType;
}
// 错误分类名称映射
function getErrorCategoryName(category) {
const categoryMap = {
'grammar': '语法错误',
'punctuation': '标点错误',
'word_usage': '用词错误',
'spelling': '拼写错误',
'word': '字词错误',
'sentence': '句子错误',
'expression': '表达错误'
};
return categoryMap[category] || category;
}
< / script >
{% endblock %}