审稿人证书生成

This commit is contained in:
2026-07-10 09:12:09 +08:00
parent 262e634f85
commit 3448ffe961
29 changed files with 7825 additions and 492 deletions

View File

@@ -1,6 +1,9 @@
import {
AlignmentType,
BorderStyle,
CommentRangeEnd,
CommentRangeStart,
CommentReference,
Document,
LineRuleType,
Packer,
@@ -18,6 +21,8 @@ import { saveAs } from 'file-saver';
import JSZip from 'jszip';
import { TableUtils } from '@/common/js/TableUtils';
import { isMathFormulaTableRecord } from '@/utils/mathFormulaModule';
import { expandCitationBracket } from '@/utils/manuscriptCitationRelevance';
import { tokenizeReferenceHighlightSegments } from '@/utils/manuscriptMediaReferences';
/** 斑马纹 rgb(250, 231, 232) */
const ODD_ROW_FILL = 'FAE7E8';
@@ -430,33 +435,117 @@ function normalizeCitationBracketCommaSpacing(text) {
});
}
/** 匹配正文参考文献标号:\[[0-9, -\-\]{1,}\] */
const CITATION_BRACKET_PATTERN = /\[[0-9, \-]+\]/g;
function appendRunsForCitationText(text, style, appendRun) {
function createCitationCommentMatcher(citationComments) {
const byRef = {};
(citationComments || []).forEach(function (comment) {
if (!comment || comment.refNo == null) {
return;
}
const key = String(comment.refNo);
if (!byRef[key]) {
byRef[key] = [];
}
byRef[key].push(comment);
});
Object.keys(byRef).forEach(function (key) {
byRef[key].sort(function (a, b) {
return Number(a.occurrence || 0) - Number(b.occurrence || 0);
});
});
const nextIndex = {};
return {
matchBracket: function (inner) {
const nums = expandCitationBracket(inner);
for (let i = 0; i < nums.length; i += 1) {
const refNo = nums[i];
const key = String(refNo);
const idx = nextIndex[key] || 0;
nextIndex[key] = idx + 1;
const list = byRef[key];
if (list && list[idx]) {
return list[idx].commentId;
}
}
return null;
}
};
}
function appendRunsForReferenceHighlightText(text, style, appendRun, commentMatcher) {
const normalized = normalizeCitationBracketCommaSpacing(text);
if (!normalized) {
return;
}
let lastIndex = 0;
let match;
CITATION_BRACKET_PATTERN.lastIndex = 0;
while ((match = CITATION_BRACKET_PATTERN.exec(normalized)) !== null) {
if (match.index > lastIndex) {
appendRun(normalized.slice(lastIndex, match.index), style);
const segments = tokenizeReferenceHighlightSegments(normalized);
segments.forEach(function (segment) {
if (!segment || !segment.text) {
return;
}
appendRun(match[0], Object.assign({}, style, { blue: true }));
lastIndex = match.index + match[0].length;
}
if (segment.blue && /^\[/.test(segment.text)) {
const bracketText = segment.text;
const inner = bracketText.slice(1, -1);
const commentId = commentMatcher ? commentMatcher.matchBracket(inner) : null;
if (commentId != null && typeof appendRun.onCommentBracket === 'function') {
appendRun.onCommentBracket(bracketText, Object.assign({}, style, { blue: true }), commentId);
} else {
appendRun(bracketText, Object.assign({}, style, { blue: true }));
}
return;
}
if (segment.blue) {
appendRun(segment.text, Object.assign({}, style, { blue: true }));
return;
}
appendRun(segment.text, style);
});
}
if (lastIndex < normalized.length) {
appendRun(normalized.slice(lastIndex), style);
function createStyledExportTextRun(text, style, boldDefault) {
return new TextRun({
text,
font: FONT_NAME,
size: TABLE_FONT_SIZE,
bold: boldDefault || (style && style.bold),
italics: style && style.italic,
superScript: style && style.sup,
subScript: style && style.sub,
color: style && style.blue ? BLUE_COLOR : undefined,
kern: FONT_KERN_MIN_1PT
});
}
function createCitationAppendRun(runs, boldDefault, commentMatcher) {
const appendRun = function (text, style) {
if (!text) {
return;
}
runs.push(createStyledExportTextRun(text, style, boldDefault));
};
if (commentMatcher) {
appendRun.onCommentBracket = function (bracketText, style, commentId) {
runs.push(new CommentRangeStart(commentId));
runs.push(createStyledExportTextRun(bracketText, style, boldDefault));
runs.push(new CommentRangeEnd(commentId));
runs.push(
new TextRun({
children: [new CommentReference(commentId)]
})
);
};
}
return appendRun;
}
function htmlToTextRuns(html, options) {
const { bold = false } = options || {};
const opts = options || {};
const bold = opts.bold === true;
const citationComments = opts.citationComments || null;
const commentMatcher =
citationComments && citationComments.length ? createCitationCommentMatcher(citationComments) : null;
const runs = [];
const raw = String(html || '');
@@ -474,24 +563,13 @@ function htmlToTextRuns(html, options) {
if (typeof document === 'undefined') {
const plainRuns = [];
appendRunsForCitationText(htmlToPlainText(raw), { bold, italics: false, sup: false, sub: false, blue: false }, function (
text,
style
) {
plainRuns.push(
new TextRun({
text,
font: FONT_NAME,
size: TABLE_FONT_SIZE,
bold: bold || style.bold,
italics: style.italic,
superScript: style.sup,
subScript: style.sub,
color: style.blue ? BLUE_COLOR : undefined,
kern: FONT_KERN_MIN_1PT
})
);
});
const appendRun = createCitationAppendRun(plainRuns, bold, commentMatcher);
appendRunsForReferenceHighlightText(
htmlToPlainText(raw),
{ bold, italics: false, sup: false, sub: false, blue: false },
appendRun,
commentMatcher
);
if (plainRuns.length) {
return plainRuns;
}
@@ -508,32 +586,15 @@ function htmlToTextRuns(html, options) {
const root = document.createElement('div');
root.innerHTML = raw;
function appendRun(text, style) {
if (!text) {
return;
}
runs.push(
new TextRun({
text,
font: FONT_NAME,
size: TABLE_FONT_SIZE,
bold: bold || style.bold,
italics: style.italic,
superScript: style.sup,
subScript: style.sub,
color: style.blue ? BLUE_COLOR : undefined,
kern: FONT_KERN_MIN_1PT
})
);
}
const appendRun = createCitationAppendRun(runs, bold, commentMatcher);
function walk(node, style) {
if (node.nodeType === Node.TEXT_NODE) {
appendRunsForCitationText(
appendRunsForReferenceHighlightText(
decodeHtmlEntities(node.textContent).replace(/\u00a0/g, ' '),
style,
appendRun
appendRun,
commentMatcher
);
return;
}
@@ -596,6 +657,10 @@ function htmlToTextRuns(html, options) {
return runs;
}
function htmlToTextRunsWithCitationComments(html, options, citationComments) {
return htmlToTextRuns(html, Object.assign({}, options || {}, { citationComments: citationComments || [] }));
}
/** 表头:相邻片段之间补空格,再规范为“一词一空” */
function formatHeaderWordSpacing(html) {
const raw = String(html || '');
@@ -857,7 +922,7 @@ function buildTableWordChildren(processedItem, options) {
return children;
}
/** 稿件导出:标题 + 表格 + 表注均为单栏(分节符在标题前 / 表前,双栏恢复在表注后 / 表后 */
/** 稿件导出:标题 + 表格 + 表注均为单栏(与图片块相同,由 exportManuscriptWord 分节 */
export function buildTableWordManuscriptParts(processedItem, options) {
const blockChildren = buildTableWordChildren(processedItem, options);
const tableIndex = blockChildren.findIndex(function (child) {
@@ -947,6 +1012,7 @@ export {
FONT_NAME,
htmlToPlainText,
htmlToTextRuns,
htmlToTextRunsWithCitationComments,
PAGE_MARGINS,
patchBodyTableCaptionCenter,
splitHtmlSegments,