Files
pdf-translate-web/static/js/main.js
coder 69e4ca4d64 feat: 前端页面使用网站基础配置
- 使用 Flask context_processor 自动注入 site_config
- 所有页面标题使用 site_name 配置
- 所有页面导航栏品牌使用 site_name 配置
- 所有页面底部使用 site_footer 配置
- 文件上传时使用 max_file_size 配置验证文件大小
- 显示最大文件限制提示
2026-04-16 18:44:57 +08:00

294 lines
8.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* PDF翻译助手前端脚本
*/
// 当前翻译ID
let currentTranslationId = null;
let currentTaskId = null;
// 上传表单处理
document.getElementById('uploadForm').addEventListener('submit', async function(e) {
e.preventDefault();
const fileInput = document.getElementById('pdfFile');
const file = fileInput.files[0];
if (!file) {
alert('请选择PDF文件');
return;
}
if (!file.name.toLowerCase().endsWith('.pdf')) {
alert('只支持PDF文件');
return;
}
// 检查文件大小
const maxSizeMB = parseInt(document.getElementById('submitBtn').dataset.maxSize) || 50;
const fileSizeMB = file.size / (1024 * 1024);
if (fileSizeMB > maxSizeMB) {
alert(`文件大小超出限制(最大${maxSizeMB}MB当前${fileSizeMB.toFixed(1)}MB`);
return;
}
// 显示进度区域
document.getElementById('progressSection').style.display = 'block';
document.getElementById('resultSection').style.display = 'none';
document.getElementById('cacheNotice').style.display = 'none';
// 显示加载状态
const submitBtn = document.getElementById('submitBtn');
const btnText = document.getElementById('btnText');
const btnSpinner = document.getElementById('btnSpinner');
submitBtn.disabled = true;
btnText.textContent = '上传中...';
btnSpinner.style.display = 'inline-block';
// 构建表单数据
const formData = new FormData();
formData.append('file', file);
const instruction = document.getElementById('instruction')?.value;
if (instruction) {
formData.append('instruction', instruction);
}
try {
// 上传文件
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || '上传失败');
}
currentTranslationId = result.translation_id;
currentTaskId = result.task_id;
// 如果使用缓存,直接显示结果
if (result.from_cache) {
document.getElementById('cacheNotice').style.display = 'block';
showResult(currentTranslationId);
} else {
// 开始轮询进度
pollProgress(currentTaskId, currentTranslationId);
}
} catch (error) {
alert('上传失败: ' + error.message);
resetUploadButton();
}
});
// 轮询翻译进度
async function pollProgress(taskId, translationId) {
const progressBar = document.getElementById('progressBar');
const progressMessage = document.getElementById('progressMessage');
const poll = async () => {
try {
// 同时检查任务状态和翻译状态
const taskResponse = await fetch(`/api/task/${taskId}`);
const taskResult = await taskResponse.json();
const transResponse = await fetch(`/api/status/${translationId}`);
const transResult = await transResponse.json();
// 更新进度
if (taskResult.progress) {
progressBar.style.width = taskResult.progress + '%';
progressBar.textContent = taskResult.progress + '%';
}
if (taskResult.message) {
progressMessage.textContent = taskResult.message;
}
// 检查是否完成
if (taskResult.status === 'completed' || transResult.status === 'completed') {
progressBar.style.width = '100%';
progressBar.textContent = '100%';
progressMessage.textContent = '翻译完成!';
// 显示结果
setTimeout(() => showResult(translationId), 500);
return;
}
if (taskResult.status === 'failed') {
progressMessage.textContent = '翻译失败: ' + (taskResult.error || '未知错误');
resetUploadButton();
return;
}
// 继续轮询
setTimeout(poll, 2000);
} catch (error) {
console.error('轮询失败:', error);
setTimeout(poll, 3000);
}
};
poll();
}
// 显示翻译结果
async function showResult(translationId) {
const resultSection = document.getElementById('resultSection');
const resultContent = document.getElementById('resultContent');
try {
const response = await fetch(`/api/result/${translationId}`);
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || '获取结果失败');
}
// 渲染Markdown内容
resultContent.innerHTML = renderMarkdown(result.content);
resultSection.style.display = 'block';
resetUploadButton();
} catch (error) {
alert('获取结果失败: ' + error.message);
resetUploadButton();
}
}
// 重置上传按钮
function resetUploadButton() {
const submitBtn = document.getElementById('submitBtn');
const btnText = document.getElementById('btnText');
const btnSpinner = document.getElementById('btnSpinner');
submitBtn.disabled = false;
btnText.textContent = '开始翻译';
btnSpinner.style.display = 'none';
}
// 下载结果
document.getElementById('downloadBtn')?.addEventListener('click', function() {
if (currentTranslationId) {
window.location.href = `/api/download/${currentTranslationId}`;
}
});
// 对比查看
document.getElementById('viewCompare')?.addEventListener('click', async function() {
if (!currentTranslationId) return;
try {
const response = await fetch(`/api/compare/${currentTranslationId}`);
const result = await response.json();
// 显示对比视图
showCompareView(result);
} catch (error) {
alert('获取对比失败: ' + error.message);
}
});
// 显示对比视图
function showCompareView(data) {
const resultContent = document.getElementById('resultContent');
resultContent.innerHTML = `
<div class="compare-container">
<div class="compare-panel original">
<h5>原文</h5>
<div class="content">${escapeHtml(data.original)}</div>
</div>
<div class="compare-panel translated">
<h5>译文</h5>
<div class="content">${renderMarkdown(data.translated)}</div>
</div>
</div>
`;
}
// 重新翻译
document.getElementById('retranslateBtn')?.addEventListener('click', async function() {
if (!currentTranslationId) return;
const instruction = document.getElementById('retranslateInstruction').value;
if (!instruction.trim()) {
alert('请输入翻译要求');
return;
}
try {
const response = await fetch(`/api/retranslate/${currentTranslationId}`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({instruction: instruction})
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || '重译请求失败');
}
// 开始新的翻译任务
currentTranslationId = result.translation_id;
document.getElementById('progressSection').style.display = 'block';
document.getElementById('resultSection').style.display = 'none';
pollProgress(null, currentTranslationId);
} catch (error) {
alert('重译失败: ' + error.message);
}
});
// 简单Markdown渲染
function renderMarkdown(text) {
// 标题
text = text.replace(/^## (.*)$/gm, '<h2>$1</h2>');
text = text.replace(/^# (.*)$/gm, '<h1>$1</h1>');
// 分隔线
text = text.replace(/^---$/gm, '<hr>');
// 引用
text = text.replace(/^> (.*)$/gm, '<blockquote>$1</blockquote>');
// 段落
text = text.replace(/\n\n/g, '</p><p>');
text = '<p>' + text + '</p>';
return text;
}
// HTML转义
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 检查用户登录状态
async function checkUserStatus() {
try {
const response = await fetch('/api/user/info');
const result = await response.json();
if (result.user) {
console.log('用户已登录:', result.user.username);
}
} catch (error) {
console.error('检查用户状态失败:', error);
}
}
// 页面加载时检查状态
document.addEventListener('DOMContentLoaded', checkUserStatus);