Spaces:
Running
Running
Upload 2 files
Browse files- static/app.js +38 -6
- static/style.css +58 -0
static/app.js
CHANGED
|
@@ -1357,6 +1357,10 @@ function isImageSource(url) {
|
|
| 1357 |
return /\.(?:bmp|gif|jpe?g|png|tiff?|webp)(?:$|\?)/i.test(String(url || ""));
|
| 1358 |
}
|
| 1359 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1360 |
async function openRandomPreview() {
|
| 1361 |
const requestSeq = randomRequestSeq + 1;
|
| 1362 |
randomRequestSeq = requestSeq;
|
|
@@ -1450,7 +1454,7 @@ function changePreviewTagPage(button) {
|
|
| 1450 |
|
| 1451 |
function compareSelectHTML(item) {
|
| 1452 |
const proofread = item.article && Array.isArray(item.article.parts)
|
| 1453 |
-
?
|
| 1454 |
: "";
|
| 1455 |
if ((!item.variants || !item.variants.length) && !proofread) return "";
|
| 1456 |
const options = item.variants.map((variant) => `<option value="${escapeHTML(variant.doc_id)}">${escapeHTML(variant.publication_name)}</option>`).join("");
|
|
@@ -1507,7 +1511,9 @@ function proofreadHTMLV2(item) {
|
|
| 1507 |
const pdfPage = /\.pdf(?:$|\?)/i.test(selectedUrl) ? `#page=${page}` : "";
|
| 1508 |
const sourceViewer = sourceFiles.length ? (isImageSource(selectedUrl)
|
| 1509 |
? `<div class="proofread-image-viewer" data-image-viewer><div class="proofread-image-tools"><button type="button" class="text-btn-sm" data-image-zoom="out">缩小</button><button type="button" class="text-btn-sm" data-image-zoom="reset">原始大小</button><button type="button" class="text-btn-sm" data-image-zoom="in">放大</button></div><div class="proofread-image-stage"><img data-proofread-source-image src="${sourceURL}" alt="原文件" data-image-scale="1"><a class="text-btn-sm proofread-image-download" href="${sourceDownload}">下载原文件</a></div></div>`
|
| 1510 |
-
:
|
|
|
|
|
|
|
| 1511 |
: '<div class="proofread-no-source"><strong>没有原文件</strong><span>仍可编辑正文并提交校对。</span></div>';
|
| 1512 |
const partTypeOptions = (current) => PART_TYPES.map((type) => `<option value="${type}" ${type === current ? "selected" : ""}>${type}</option>`).join("");
|
| 1513 |
const parts = (article.parts || []).map((part, index) => `<div class="proofread-part-group" data-proofread-part-group="${index}"><div data-part-insert-before></div><label class="proofread-field proofread-part" data-proofread-part="${index}"><span>段落 ${index + 1} / <select data-proofread-part-type="${index}">${partTypeOptions(part.type || "paragraph")}</select></span><span class="proofread-part-actions"><button type="button" class="text-btn-sm" data-add-part-before="${index}">在上新建段落</button><button type="button" class="text-btn-sm" data-add-part-after="${index}">在下新建段落</button><button type="button" class="text-btn-sm" data-delete-part="${index}">删除当前段落</button></span><textarea data-proofread-part-input="${index}">${escapeHTML(partTextWithPivots(article, index))}</textarea></label><div data-part-insert-after></div></div>`).join("");
|
|
@@ -1551,12 +1557,34 @@ function openProofread(item, focusPart = null, restoring = false) {
|
|
| 1551 |
if (!restoring) syncUrl(true);
|
| 1552 |
DOM.previewPanel.classList.add("proofread-mode");
|
| 1553 |
DOM.previewPanel.innerHTML = proofreadHTMLV2(item);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1554 |
const sourceImage = DOM.previewPanel.querySelector("[data-proofread-source-image]");
|
| 1555 |
if (sourceImage) sourceImage.addEventListener("error", () => {
|
| 1556 |
const stage = sourceImage.closest("[data-image-stage], .proofread-image-stage");
|
| 1557 |
if (stage) stage.innerHTML = `<div class="proofread-no-source"><strong>图片无法在线显示</strong><a class="text-btn-sm" href="${sourceDownloadURL(item, STATE.proofreadFile || 0)}">下载原文件</a></div>`;
|
| 1558 |
}, { once: true });
|
| 1559 |
const form = DOM.previewPanel.querySelector("[data-proofread-form]");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1560 |
const sourceHeader = DOM.previewPanel.querySelector(".proofread-source .proofread-pane-header");
|
| 1561 |
if (sourceHeader && !sourceHeader.querySelector("[data-proofread-page]")) {
|
| 1562 |
sourceHeader.insertAdjacentHTML("beforeend", `<label class="proofread-page-control">页码 <input data-proofread-page type="number" min="1" value="${Math.max(1, Number(STATE.proofreadPage) || Number((item.article || {}).page_start) || 1)}"></label>`);
|
|
@@ -2115,10 +2143,14 @@ function attachEvents() {
|
|
| 2115 |
}
|
| 2116 |
const partEditorToggle = event.target.closest("[data-part-editor-toggle]");
|
| 2117 |
if (partEditorToggle) {
|
| 2118 |
-
const
|
| 2119 |
-
if (
|
| 2120 |
-
|
| 2121 |
-
partEditorToggle.textContent =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2122 |
}
|
| 2123 |
return;
|
| 2124 |
}
|
|
|
|
| 1357 |
return /\.(?:bmp|gif|jpe?g|png|tiff?|webp)(?:$|\?)/i.test(String(url || ""));
|
| 1358 |
}
|
| 1359 |
|
| 1360 |
+
function isPDFSource(url) {
|
| 1361 |
+
return /\.pdf(?:$|\?)/i.test(String(url || ""));
|
| 1362 |
+
}
|
| 1363 |
+
|
| 1364 |
async function openRandomPreview() {
|
| 1365 |
const requestSeq = randomRequestSeq + 1;
|
| 1366 |
randomRequestSeq = requestSeq;
|
|
|
|
| 1454 |
|
| 1455 |
function compareSelectHTML(item) {
|
| 1456 |
const proofread = item.article && Array.isArray(item.article.parts)
|
| 1457 |
+
? `<option value="__proofread__">${item.source_files && item.source_files.length ? "对比原文件并校对" : "校对"}</option>`
|
| 1458 |
: "";
|
| 1459 |
if ((!item.variants || !item.variants.length) && !proofread) return "";
|
| 1460 |
const options = item.variants.map((variant) => `<option value="${escapeHTML(variant.doc_id)}">${escapeHTML(variant.publication_name)}</option>`).join("");
|
|
|
|
| 1511 |
const pdfPage = /\.pdf(?:$|\?)/i.test(selectedUrl) ? `#page=${page}` : "";
|
| 1512 |
const sourceViewer = sourceFiles.length ? (isImageSource(selectedUrl)
|
| 1513 |
? `<div class="proofread-image-viewer" data-image-viewer><div class="proofread-image-tools"><button type="button" class="text-btn-sm" data-image-zoom="out">缩小</button><button type="button" class="text-btn-sm" data-image-zoom="reset">原始大小</button><button type="button" class="text-btn-sm" data-image-zoom="in">放大</button></div><div class="proofread-image-stage"><img data-proofread-source-image src="${sourceURL}" alt="原文件" data-image-scale="1"><a class="text-btn-sm proofread-image-download" href="${sourceDownload}">下载原文件</a></div></div>`
|
| 1514 |
+
: (isPDFSource(selectedUrl)
|
| 1515 |
+
? `<div class="proofread-document-viewer"><iframe data-proofread-source title="原文件" src="${sourceURL}${pdfPage}"></iframe><a class="text-btn-sm proofread-document-download" href="${sourceDownload}">下载原文件</a></div>`
|
| 1516 |
+
: `<div class="proofread-no-source"><strong>此原文件暂不支持在线预览</strong><a class="text-btn-sm" href="${sourceDownload}">下载原文件</a></div>`))
|
| 1517 |
: '<div class="proofread-no-source"><strong>没有原文件</strong><span>仍可编辑正文并提交校对。</span></div>';
|
| 1518 |
const partTypeOptions = (current) => PART_TYPES.map((type) => `<option value="${type}" ${type === current ? "selected" : ""}>${type}</option>`).join("");
|
| 1519 |
const parts = (article.parts || []).map((part, index) => `<div class="proofread-part-group" data-proofread-part-group="${index}"><div data-part-insert-before></div><label class="proofread-field proofread-part" data-proofread-part="${index}"><span>段落 ${index + 1} / <select data-proofread-part-type="${index}">${partTypeOptions(part.type || "paragraph")}</select></span><span class="proofread-part-actions"><button type="button" class="text-btn-sm" data-add-part-before="${index}">在上新建段落</button><button type="button" class="text-btn-sm" data-add-part-after="${index}">在下新建段落</button><button type="button" class="text-btn-sm" data-delete-part="${index}">删除当前段落</button></span><textarea data-proofread-part-input="${index}">${escapeHTML(partTextWithPivots(article, index))}</textarea></label><div data-part-insert-after></div></div>`).join("");
|
|
|
|
| 1557 |
if (!restoring) syncUrl(true);
|
| 1558 |
DOM.previewPanel.classList.add("proofread-mode");
|
| 1559 |
DOM.previewPanel.innerHTML = proofreadHTMLV2(item);
|
| 1560 |
+
if (!(item.source_files || []).length) {
|
| 1561 |
+
const sourcePane = DOM.previewPanel.querySelector(".proofread-source");
|
| 1562 |
+
if (sourcePane) sourcePane.remove();
|
| 1563 |
+
const grid = DOM.previewPanel.querySelector(".proofread-grid");
|
| 1564 |
+
if (grid) grid.classList.add("proofread-grid-no-source");
|
| 1565 |
+
}
|
| 1566 |
const sourceImage = DOM.previewPanel.querySelector("[data-proofread-source-image]");
|
| 1567 |
if (sourceImage) sourceImage.addEventListener("error", () => {
|
| 1568 |
const stage = sourceImage.closest("[data-image-stage], .proofread-image-stage");
|
| 1569 |
if (stage) stage.innerHTML = `<div class="proofread-no-source"><strong>图片无法在线显示</strong><a class="text-btn-sm" href="${sourceDownloadURL(item, STATE.proofreadFile || 0)}">下载原文件</a></div>`;
|
| 1570 |
}, { once: true });
|
| 1571 |
const form = DOM.previewPanel.querySelector("[data-proofread-form]");
|
| 1572 |
+
if (form) {
|
| 1573 |
+
form.classList.add("proofread-structure-closed");
|
| 1574 |
+
const partEditor = form.querySelector("[data-part-editor]");
|
| 1575 |
+
if (partEditor) partEditor.hidden = false;
|
| 1576 |
+
form.querySelectorAll("[data-proofread-part-type]").forEach((select) => {
|
| 1577 |
+
select.insertAdjacentHTML("afterend", `<span class="proofread-part-type-label">${escapeHTML(select.value)}</span>`);
|
| 1578 |
+
});
|
| 1579 |
+
const structureActions = form.querySelector(".proofread-form-actions");
|
| 1580 |
+
const descriptionInput = form.querySelector("[data-proofread-description]");
|
| 1581 |
+
const descriptionField = descriptionInput ? descriptionInput.closest(".proofread-field") : null;
|
| 1582 |
+
const descriptionLabel = descriptionField && descriptionField.querySelector("span");
|
| 1583 |
+
if (structureActions && descriptionLabel) {
|
| 1584 |
+
descriptionLabel.classList.add("proofread-description-heading");
|
| 1585 |
+
descriptionLabel.append(structureActions);
|
| 1586 |
+
}
|
| 1587 |
+
}
|
| 1588 |
const sourceHeader = DOM.previewPanel.querySelector(".proofread-source .proofread-pane-header");
|
| 1589 |
if (sourceHeader && !sourceHeader.querySelector("[data-proofread-page]")) {
|
| 1590 |
sourceHeader.insertAdjacentHTML("beforeend", `<label class="proofread-page-control">页码 <input data-proofread-page type="number" min="1" value="${Math.max(1, Number(STATE.proofreadPage) || Number((item.article || {}).page_start) || 1)}"></label>`);
|
|
|
|
| 2143 |
}
|
| 2144 |
const partEditorToggle = event.target.closest("[data-part-editor-toggle]");
|
| 2145 |
if (partEditorToggle) {
|
| 2146 |
+
const form = DOM.previewPanel.querySelector("[data-proofread-form]");
|
| 2147 |
+
if (form) {
|
| 2148 |
+
const open = form.classList.toggle("proofread-structure-open");
|
| 2149 |
+
partEditorToggle.textContent = open ? "收起段落编辑" : "段落编辑";
|
| 2150 |
+
form.querySelectorAll("[data-proofread-part-type]").forEach((select) => {
|
| 2151 |
+
const label = select.nextElementSibling;
|
| 2152 |
+
if (label && label.classList.contains("proofread-part-type-label")) label.textContent = select.value;
|
| 2153 |
+
});
|
| 2154 |
}
|
| 2155 |
return;
|
| 2156 |
}
|
static/style.css
CHANGED
|
@@ -570,6 +570,10 @@ ul, li {
|
|
| 570 |
flex: 1;
|
| 571 |
}
|
| 572 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 573 |
.proofread-source,
|
| 574 |
.proofread-editor {
|
| 575 |
min-width: 0;
|
|
@@ -636,6 +640,31 @@ ul, li {
|
|
| 636 |
border-radius: 4px;
|
| 637 |
}
|
| 638 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 639 |
.proofread-no-source {
|
| 640 |
display: flex;
|
| 641 |
min-height: 220px;
|
|
@@ -724,6 +753,35 @@ ul, li {
|
|
| 724 |
gap: .35rem;
|
| 725 |
flex-wrap: wrap;
|
| 726 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 727 |
.proofread-page-control {
|
| 728 |
display: inline-flex;
|
| 729 |
align-items: center;
|
|
|
|
| 570 |
flex: 1;
|
| 571 |
}
|
| 572 |
|
| 573 |
+
.proofread-grid-no-source {
|
| 574 |
+
grid-template-columns: minmax(0, 1fr);
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
.proofread-source,
|
| 578 |
.proofread-editor {
|
| 579 |
min-width: 0;
|
|
|
|
| 640 |
border-radius: 4px;
|
| 641 |
}
|
| 642 |
|
| 643 |
+
.proofread-document-viewer {
|
| 644 |
+
position: relative;
|
| 645 |
+
display: flex;
|
| 646 |
+
min-height: 520px;
|
| 647 |
+
flex: 1;
|
| 648 |
+
flex-direction: column;
|
| 649 |
+
}
|
| 650 |
+
|
| 651 |
+
.proofread-document-viewer iframe {
|
| 652 |
+
width: 100%;
|
| 653 |
+
min-height: 520px;
|
| 654 |
+
flex: 1;
|
| 655 |
+
border: 0;
|
| 656 |
+
background: #fff;
|
| 657 |
+
}
|
| 658 |
+
|
| 659 |
+
.proofread-document-download {
|
| 660 |
+
position: absolute;
|
| 661 |
+
top: 8px;
|
| 662 |
+
right: 8px;
|
| 663 |
+
padding: 5px 8px;
|
| 664 |
+
background: var(--surface);
|
| 665 |
+
border-radius: 4px;
|
| 666 |
+
}
|
| 667 |
+
|
| 668 |
.proofread-no-source {
|
| 669 |
display: flex;
|
| 670 |
min-height: 220px;
|
|
|
|
| 753 |
gap: .35rem;
|
| 754 |
flex-wrap: wrap;
|
| 755 |
}
|
| 756 |
+
|
| 757 |
+
.proofread-structure-closed .proofread-part-actions,
|
| 758 |
+
.proofread-structure-closed [data-proofread-part-type] {
|
| 759 |
+
display: none;
|
| 760 |
+
}
|
| 761 |
+
|
| 762 |
+
.proofread-part-type-label {
|
| 763 |
+
display: none;
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
.proofread-structure-closed .proofread-part-type-label {
|
| 767 |
+
display: inline;
|
| 768 |
+
}
|
| 769 |
+
|
| 770 |
+
.proofread-structure-closed .proofread-part > span:first-child {
|
| 771 |
+
display: flex;
|
| 772 |
+
align-items: center;
|
| 773 |
+
gap: .3rem;
|
| 774 |
+
}
|
| 775 |
+
|
| 776 |
+
.proofread-description-heading {
|
| 777 |
+
display: flex;
|
| 778 |
+
align-items: center;
|
| 779 |
+
gap: .5rem;
|
| 780 |
+
}
|
| 781 |
+
|
| 782 |
+
.proofread-description-heading .proofread-form-actions {
|
| 783 |
+
margin-left: auto;
|
| 784 |
+
}
|
| 785 |
.proofread-page-control {
|
| 786 |
display: inline-flex;
|
| 787 |
align-items: center;
|