271 lines
7.8 KiB
JavaScript
271 lines
7.8 KiB
JavaScript
// history_detail_scripts.js - 从 history_detail.html 中提取的 JavaScript 代码
|
||
// 这个文件包含了历史详情页面的主要 JavaScript 逻辑
|
||
|
||
window.addEventListener('storage', function(e) {
|
||
if (e.key === 'paperBurnerSettings') {
|
||
// 重新加载设置并刷新 chatbot 配置
|
||
if (window.ChatbotCore && typeof window.ChatbotCore.getChatbotConfig === 'function') {
|
||
// 你可以强制刷新 Chatbot UI 或重载配置
|
||
window.ChatbotUI && window.ChatbotUI.updateChatbotUI && window.ChatbotUI.updateChatbotUI();
|
||
}
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 将 exact 文本转为模糊正则,允许空格、换行模糊匹配,大小写不敏感
|
||
* @param {string} exact
|
||
* @returns {RegExp}
|
||
*/
|
||
function escapeRegExp(string) {
|
||
// 更安全地转义所有正则特殊字符
|
||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||
}
|
||
|
||
function fuzzyRegFromExact(exact) {
|
||
// 先转义所有正则特殊字符
|
||
let pattern = escapeRegExp(exact);
|
||
// 将所有空白替换为 \s+,允许跨行、多个空格
|
||
pattern = pattern.replace(/\s+/g, '\\s+');
|
||
// 可选:忽略前后空白
|
||
pattern = '\\s*' + pattern + '\\s*';
|
||
return new RegExp(pattern, 'gi');
|
||
}
|
||
|
||
/**
|
||
* 检查是否有OCR数据
|
||
* @returns {boolean}
|
||
*/
|
||
function hasOcrData() {
|
||
return window.data && window.data.ocr && window.data.ocr.trim() !== '';
|
||
}
|
||
|
||
/**
|
||
* 检查是否有翻译数据
|
||
* @returns {boolean}
|
||
*/
|
||
function hasTranslationData() {
|
||
return window.data && window.data.translation && window.data.translation.trim() !== '';
|
||
}
|
||
|
||
/**
|
||
* 检查是否有原始PDF数据
|
||
* @returns {boolean}
|
||
*/
|
||
function hasOriginalPdfData() {
|
||
return window.data && window.data.metadata && window.data.metadata.originalPdfBase64;
|
||
}
|
||
|
||
/**
|
||
* 创建确认对话框
|
||
* @param {string} title - 对话框标题
|
||
* @param {string} message - 对话框消息
|
||
* @param {string} confirmText - 确认按钮文本
|
||
* @param {string} cancelText - 取消按钮文本
|
||
* @returns {Promise<boolean>} 用户是否确认
|
||
*/
|
||
function showConfirmDialog(title, message, confirmText = '确认', cancelText = '取消') {
|
||
return new Promise((resolve) => {
|
||
// 创建对话框容器
|
||
const dialogOverlay = document.createElement('div');
|
||
dialogOverlay.id = 'pbx-confirm-dialog-overlay';
|
||
dialogOverlay.style.cssText = `
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 10000;
|
||
`;
|
||
|
||
const dialogBox = document.createElement('div');
|
||
dialogBox.style.cssText = `
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
max-width: 400px;
|
||
width: 90%;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||
`;
|
||
|
||
dialogBox.innerHTML = `
|
||
<h3 style="margin: 0 0 16px 0; font-size: 18px; color: #1a1a1a;">${title}</h3>
|
||
<p style="margin: 0 0 24px 0; font-size: 14px; color: #666; line-height: 1.6;">${message}</p>
|
||
<div style="display: flex; gap: 12px; justify-content: flex-end;">
|
||
<button id="pbx-dialog-cancel" style="
|
||
padding: 10px 20px;
|
||
border: 1px solid #ddd;
|
||
background: white;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
color: #666;
|
||
">${cancelText}</button>
|
||
<button id="pbx-dialog-confirm" style="
|
||
padding: 10px 20px;
|
||
border: none;
|
||
background: #4f46e5;
|
||
color: white;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
">${confirmText}</button>
|
||
</div>
|
||
`;
|
||
|
||
dialogOverlay.appendChild(dialogBox);
|
||
document.body.appendChild(dialogOverlay);
|
||
|
||
// 按钮事件
|
||
const confirmBtn = dialogBox.querySelector('#pbx-dialog-confirm');
|
||
const cancelBtn = dialogBox.querySelector('#pbx-dialog-cancel');
|
||
|
||
const cleanup = () => {
|
||
document.body.removeChild(dialogOverlay);
|
||
};
|
||
|
||
confirmBtn.onclick = () => {
|
||
cleanup();
|
||
resolve(true);
|
||
};
|
||
|
||
cancelBtn.onclick = () => {
|
||
cleanup();
|
||
resolve(false);
|
||
};
|
||
|
||
// 点击遮罩层关闭
|
||
dialogOverlay.onclick = (e) => {
|
||
if (e.target === dialogOverlay) {
|
||
cleanup();
|
||
resolve(false);
|
||
}
|
||
};
|
||
|
||
// ESC 键关闭
|
||
const handleEsc = (e) => {
|
||
if (e.key === 'Escape') {
|
||
cleanup();
|
||
resolve(false);
|
||
document.removeEventListener('keydown', handleEsc);
|
||
}
|
||
};
|
||
document.addEventListener('keydown', handleEsc);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 触发重新处理文档(OCR和/或翻译)
|
||
* @param {boolean} includeTranslation - 是否包含翻译
|
||
*/
|
||
async function triggerReprocess(includeTranslation) {
|
||
const docId = window.docIdForLocalStorage;
|
||
const docName = window.data ? window.data.name : '未知文档';
|
||
|
||
if (!docId) {
|
||
alert('无法获取文档ID,请刷新页面重试。');
|
||
return;
|
||
}
|
||
|
||
// 保存处理模式到 localStorage,以便主页面读取
|
||
const reprocessConfig = {
|
||
docId: docId,
|
||
docName: docName,
|
||
includeTranslation: includeTranslation,
|
||
timestamp: Date.now()
|
||
};
|
||
localStorage.setItem('pbx_reprocess_config', JSON.stringify(reprocessConfig));
|
||
|
||
// 跳转到主页面
|
||
const baseUrl = window.location.pathname.replace('/views/history/history_detail.html', '/');
|
||
window.location.href = baseUrl + '?reprocess=1';
|
||
}
|
||
|
||
/**
|
||
* 处理"Word文档"标签点击
|
||
* 如果没有OCR数据,询问用户是否需要生成
|
||
*/
|
||
async function handleOcrTabClick() {
|
||
// 如果已有OCR数据,直接显示
|
||
if (hasOcrData()) {
|
||
showTab('ocr');
|
||
return;
|
||
}
|
||
|
||
// 检查是否有原始PDF
|
||
if (!hasOriginalPdfData()) {
|
||
alert('当前记录没有保存原始PDF数据,无法重新处理。');
|
||
return;
|
||
}
|
||
|
||
// 弹出确认对话框
|
||
const confirmed = await showConfirmDialog(
|
||
'生成Word文档',
|
||
'当前文档尚未进行OCR处理。是否需要启动OCR处理生成Word文档?\n\n处理完成后可在本页面查看和导出。',
|
||
'启动OCR',
|
||
'取消'
|
||
);
|
||
|
||
if (confirmed) {
|
||
await triggerReprocess(false); // 仅OCR,不翻译
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理"仅翻译"标签点击
|
||
* 如果没有翻译数据,询问用户是否需要生成翻译对照文档
|
||
*/
|
||
async function handleTranslationTabClick() {
|
||
// 如果已有翻译数据,直接显示
|
||
if (hasTranslationData()) {
|
||
showTab('translation');
|
||
return;
|
||
}
|
||
|
||
// 检查是否有原始PDF
|
||
if (!hasOriginalPdfData()) {
|
||
alert('当前记录没有保存原始PDF数据,无法重新处理。');
|
||
return;
|
||
}
|
||
|
||
// 弹出确认对话框
|
||
const confirmed = await showConfirmDialog(
|
||
'生成翻译对照文档',
|
||
'当前文档尚未进行翻译处理。是否需要启动OCR+翻译处理?\n\n这将生成原文Word文档和翻译对照文档,处理完成后可在本页面查看和导出。',
|
||
'启动OCR+翻译',
|
||
'取消'
|
||
);
|
||
|
||
if (confirmed) {
|
||
await triggerReprocess(true); // OCR + 翻译
|
||
}
|
||
}
|
||
|
||
// 绑定 tab 按钮点击事件
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
if (document.getElementById('tab-ocr')) {
|
||
document.getElementById('tab-ocr').onclick = handleOcrTabClick;
|
||
}
|
||
if (document.getElementById('tab-translation')) {
|
||
document.getElementById('tab-translation').onclick = handleTranslationTabClick;
|
||
}
|
||
if (document.getElementById('tab-chunk-compare')) {
|
||
document.getElementById('tab-chunk-compare').onclick = function() { showTab('chunk-compare'); };
|
||
}
|
||
if (document.getElementById('tab-pdf-compare')) {
|
||
document.getElementById('tab-pdf-compare').onclick = function() { showTab('pdf-compare'); };
|
||
}
|
||
if (document.getElementById('tab-original-file')) {
|
||
document.getElementById('tab-original-file').onclick = function() { showTab('original-file'); };
|
||
}
|
||
|
||
// 页面加载后渲染详情
|
||
if (typeof renderDetail === 'function') {
|
||
renderDetail();
|
||
}
|
||
});
|