终审编委信息

This commit is contained in:
2026-07-06 09:11:20 +08:00
parent 5260ca8ea5
commit 262e634f85
24 changed files with 5529 additions and 519 deletions

View File

@@ -17,8 +17,15 @@ import {
import JSZip from 'jszip';
import { htmlToPlainText, TABLE_FONT_SIZE, FONT_KERN_MIN_1PT, FONT_KERN_MIN_XIAO_ER, FONT_NAME, PAGE_MARGINS, WORD_PARAGRAPH_SPACING } from '@/utils/exportTableWord';
import { formatAuthorFirstName, formatAuthorLastName } from '@/utils/productionSubmissionImport';
import { PAGE_HEIGHT_TWIPS, PAGE_WIDTH_TWIPS, getDocumentGridProperties } from '@/utils/exportWordLayout';
import {
PAGE_HEADER_MARGIN_TWIPS,
PAGE_FOOTER_MARGIN_TWIPS,
PAGE_HEIGHT_TWIPS,
PAGE_WIDTH_TWIPS,
getDocumentGridProperties
} from '@/utils/exportWordLayout';
import orcidIconUrl from '@/assets/img/orcid.png';
import { createTmrLogoImageRun } from '@/utils/exportManuscriptWordHeader';
/** 标题:阿里巴巴普惠体 H / Calibri四号 14pt */
const TITLE_FONT_EAST_ASIA = '阿里巴巴普惠体 H';
@@ -378,6 +385,25 @@ export async function fetchManuscriptHeaderData(apiClient, articleId, pArticleId
}
}
let stageInfo = null;
if (production && production.journal_id && production.journal_stage_id) {
const stagesRes = await withPromiseTimeout(
apiClient
.post('api/Typeset/getStagesOnline', { journal_id: production.journal_id })
.catch(function () {
return null;
}),
12000
);
if (stagesRes && stagesRes.code == 0 && stagesRes.data && stagesRes.data.stages) {
const targetStageId = production.journal_stage_id;
stageInfo =
stagesRes.data.stages.find(function (item) {
return String(item.journal_stage_id) === String(targetStageId);
}) || null;
}
}
if (!production && !authors.length && !previewAuthor) {
return {
pArticleId: resolvedId,
@@ -386,7 +412,8 @@ export async function fetchManuscriptHeaderData(apiClient, articleId, pArticleId
previewAuthor: null,
journal: journal,
organList: organList,
finalReview: finalReview
finalReview: finalReview,
stageInfo: stageInfo
};
}
@@ -397,7 +424,8 @@ export async function fetchManuscriptHeaderData(apiClient, articleId, pArticleId
previewAuthor: previewAuthor,
journal: journal,
organList: organList,
finalReview: finalReview
finalReview: finalReview,
stageInfo: stageInfo
};
}
@@ -884,7 +912,19 @@ function createAffiliationParagraph(index, addressText) {
});
}
function createAuthorTitleParagraph(title) {
function buildTitleParagraphChildren(contentRuns, logoImageData) {
const children = [];
const logoRun = logoImageData && createTmrLogoImageRun(logoImageData);
if (logoRun) {
children.push(logoRun);
}
if (contentRuns && contentRuns.length) {
children.push.apply(children, contentRuns);
}
return children;
}
function createAuthorTitleParagraph(title, logoImageData) {
const text = String(title || '').trim();
if (!text) {
return null;
@@ -896,41 +936,49 @@ function createAuthorTitleParagraph(title) {
widowControl: false,
overflowPunctuation: true,
autoSpaceEastAsianText: true,
children: [
createStyledTextRun(text, {
fontEastAsia: TITLE_FONT_EAST_ASIA,
fontAscii: TITLE_FONT_ASCII,
size: TITLE_FONT_SIZE,
bold: true,
kern: FONT_KERN_MIN_XIAO_ER
})
]
children: buildTitleParagraphChildren(
[
createStyledTextRun(text, {
fontEastAsia: TITLE_FONT_EAST_ASIA,
fontAscii: TITLE_FONT_ASCII,
size: TITLE_FONT_SIZE,
bold: true,
kern: FONT_KERN_MIN_XIAO_ER
})
],
logoImageData
)
});
}
function createMissingTitleParagraph() {
function createMissingTitleParagraph(logoImageData) {
return new Paragraph({
alignment: AlignmentType.JUSTIFIED,
spacing: TITLE_PARAGRAPH_SPACING,
widowControl: false,
overflowPunctuation: true,
autoSpaceEastAsianText: true,
children: [
createStyledTextRun(MISSING_DATA_PLACEHOLDER, {
fontEastAsia: TITLE_FONT_EAST_ASIA,
fontAscii: TITLE_FONT_ASCII,
size: TITLE_FONT_SIZE,
bold: true,
color: MISSING_DATA_COLOR,
kern: FONT_KERN_MIN_XIAO_ER
})
]
children: buildTitleParagraphChildren(
[
createStyledTextRun(MISSING_DATA_PLACEHOLDER, {
fontEastAsia: TITLE_FONT_EAST_ASIA,
fontAscii: TITLE_FONT_ASCII,
size: TITLE_FONT_SIZE,
bold: true,
color: MISSING_DATA_COLOR,
kern: FONT_KERN_MIN_XIAO_ER
})
],
logoImageData
)
});
}
function createManuscriptTitleParagraph(headerData) {
function createManuscriptTitleParagraph(headerData, logoImageData) {
const text = resolveManuscriptDisplayTitle(headerData);
return text ? createAuthorTitleParagraph(text) : createMissingTitleParagraph();
return text
? createAuthorTitleParagraph(text, logoImageData)
: createMissingTitleParagraph(logoImageData);
}
function stripParagraphPropertyTag(pPrInner, tagName) {
@@ -1623,7 +1671,8 @@ export function buildMinimalManuscriptHeaderData(pArticleId) {
previewAuthor: null,
journal: null,
organList: [],
finalReview: null
finalReview: null,
stageInfo: null
};
}
@@ -2148,6 +2197,96 @@ function patchMetadataTableBlock(tblBlock) {
return block;
}
function isHomePageSingleColumnSectPrInner(sectPrInner) {
return /<w:cols\b[^>]*\bw:num="1"/i.test(String(sectPrInner || '')) && !/<w:cols\b[^>]*\bw:num="2"/i.test(String(sectPrInner || ''));
}
function isHomePageDualColumnSectPrInner(sectPrInner) {
return /<w:cols\b[^>]*\bw:num="2"/i.test(String(sectPrInner || ''));
}
function replaceSectPrInnerColsWithSingle(sectPrInner) {
let next = String(sectPrInner || '')
.replace(/<w:cols\b[^>]*\/>/g, '')
.replace(/<w:cols\b[^>]*>[\s\S]*?<\/w:cols>/g, '');
return next + '<w:cols w:num="1"/>';
}
/** 首页元数据表格节结束位置:含表格后空行,不含正文双栏分节符 */
export function findHomePageMetadataTableSectionEndIndex(xml, bounds) {
if (!bounds) {
return -1;
}
let end = bounds.end;
let cursor = bounds.end;
const limit = Math.min(xml.length, bounds.end + 8000);
while (cursor < limit) {
const slice = xml.slice(cursor, limit);
const match = slice.match(/^\s*(<w:p\b[^>]*>[\s\S]*?<\/w:p>)/);
if (!match) {
break;
}
const block = match[1];
const blockEnd = cursor + match[0].length;
const sectMatch = block.match(/<w:sectPr>([\s\S]*?)<\/w:sectPr>/);
if (sectMatch && isHomePageDualColumnSectPrInner(sectMatch[1])) {
break;
}
end = blockEnd;
cursor = blockEnd;
if (sectMatch) {
break;
}
if (/\bIntroduction\b/i.test(block) || /<v:roundrect\b[^>]*fillcolor="#DEEBF7"/i.test(block)) {
break;
}
}
return end;
}
/** 强制首页元数据表格Abstract 等)所在节为单栏 */
export function patchHomePageMetadataTableSingleColumnToXml(xml) {
let bounds = findHomePageMetadataTableBounds(xml);
if (!bounds) {
return xml;
}
let result = xml;
const beforeTableSlice = result.slice(Math.max(0, bounds.start - 8000), bounds.start);
const hasSingleBreakBeforeTable = /<w:sectPr>[\s\S]*?<w:cols\b[^>]*\bw:num="1"/i.test(beforeTableSlice);
if (!hasSingleBreakBeforeTable) {
const breakXml = buildHomePageSectionBreakParagraphXml(buildHomePageContinuousSectPrInner());
result = result.slice(0, bounds.start) + breakXml + result.slice(bounds.start);
bounds = findHomePageMetadataTableBounds(result);
if (!bounds) {
return result;
}
}
const corrMatch = findCorrespondenceParagraphMatch(result);
const regionStart = corrMatch ? corrMatch.index + corrMatch[0].length : 0;
const tableSectionEnd = findHomePageMetadataTableSectionEndIndex(result, bounds);
return result.replace(/<w:sectPr>([\s\S]*?)<\/w:sectPr>/g, function (full, inner, offset) {
if (offset < regionStart || offset >= tableSectionEnd) {
return full;
}
if (isHomePageSingleColumnSectPrInner(inner)) {
return full;
}
return '<w:sectPr>' + replaceSectPrInnerColsWithSingle(inner) + '</w:sectPr>';
});
}
function buildHomePageContinuousSectPrInner() {
const grid = getDocumentGridProperties();
return (
@@ -2165,7 +2304,11 @@ function buildHomePageContinuousSectPrInner() {
PAGE_MARGINS.bottom +
'" w:left="' +
PAGE_MARGINS.left +
'" w:header="708" w:footer="708" w:gutter="0"/>' +
'" w:header="' +
PAGE_HEADER_MARGIN_TWIPS +
'" w:footer="' +
PAGE_FOOTER_MARGIN_TWIPS +
'" w:gutter="0"/>' +
'<w:pgNumType/>' +
'<w:cols w:num="1"/>' +
'<w:docGrid w:linePitch="' +
@@ -2219,9 +2362,9 @@ function patchSectPrBetweenCorrespondenceAndHomePageTable(xml) {
return xml;
}
const continuousSectPrInner = buildHomePageContinuousSectPrInner();
const singleSectPrInner = buildHomePageContinuousSectPrInner();
const between = xml.slice(start, tblIdx).replace(/<w:sectPr>[\s\S]*?<\/w:sectPr>/g, function () {
return '<w:sectPr>' + continuousSectPrInner + '</w:sectPr>';
return '<w:sectPr>' + singleSectPrInner + '</w:sectPr>';
});
return xml.slice(0, start) + between + xml.slice(tblIdx);
@@ -2249,11 +2392,11 @@ function patchCorrespondenceDoubleSectionBreaks(xml) {
return patchSectPrBetweenCorrespondenceAndHomePageTable(xml);
}
const continuousSectPrInner = buildHomePageContinuousSectPrInner();
const singleSectPrInner = buildHomePageContinuousSectPrInner();
const doubleBreakBlock =
buildHomePageSectionBreakParagraphXml(continuousSectPrInner) +
buildHomePageSectionBreakParagraphXml(singleSectPrInner) +
buildHomePageEmptyLineParagraphXml() +
buildHomePageSectionBreakParagraphXml(continuousSectPrInner);
buildHomePageSectionBreakParagraphXml(singleSectPrInner);
return xml.slice(0, start) + doubleBreakBlock + xml.slice(tblIdx);
}
@@ -2291,19 +2434,57 @@ export async function patchHomePageDoubleSectionBreaks(blob) {
});
}
function isHomePageMetadataTableBlock(block) {
const text = String(block || '');
return /Author/i.test(text) && /contributions/i.test(text) && /Abstract/i.test(text);
}
/** 线性扫描定位首页表格Abstract + Author contributions避免全文正则灾难性回溯 */
export function findHomePageMetadataTableBounds(xml) {
if (!xml || !/Author/i.test(xml) || !/contributions/i.test(xml) || !/Abstract/.test(xml)) {
return null;
}
const tblToken = '<w:tbl';
const closeTag = '</w:tbl>';
let searchFrom = 0;
while (searchFrom < xml.length) {
const tblStart = xml.indexOf(tblToken, searchFrom);
if (tblStart < 0) {
return null;
}
const tblEnd = xml.indexOf(closeTag, tblStart);
if (tblEnd < 0) {
return null;
}
const tblEndExclusive = tblEnd + closeTag.length;
const block = xml.slice(tblStart, tblEndExclusive);
if (isHomePageMetadataTableBlock(block)) {
return {
start: tblStart,
end: tblEndExclusive,
block: block
};
}
searchFrom = tblEndExclusive;
}
return null;
}
/** 补齐元数据表格Abstract + Author contributionsWord 表格属性(图五) */
export function patchMetadataTablePropertiesToXml(xml) {
if (!xml || !/Author/i.test(xml) || !/contributions/i.test(xml) || !/Abstract/.test(xml)) {
const bounds = findHomePageMetadataTableBounds(xml);
if (!bounds) {
return xml;
}
const match = xml.match(/<w:tbl\b[\s\S]*?>[\s\S]*?Author[\s\S]*?contributions[\s\S]*?Abstract[\s\S]*?<\/w:tbl>/i);
if (!match || match.index == null) {
return xml;
}
const patched = patchMetadataTableBlock(match[0]);
return xml.slice(0, match.index) + patched + xml.slice(match.index + match[0].length);
const patched = patchMetadataTableBlock(bounds.block);
return xml.slice(0, bounds.start) + patched + xml.slice(bounds.end);
}
/** 补齐元数据表格Abstract + Author contributionsWord 表格属性(图五) */
@@ -2329,7 +2510,7 @@ export async function patchMetadataTableProperties(blob) {
}
function buildHomePageTableSectionChildren(headerData) {
return [buildMetadataTable(headerData), createHeaderBlockEmptyLineParagraph(EMPTY_LINE_AFTER_AFFILIATION)];
return [buildMetadataTable(headerData)];
}
function buildMetadataSectionChildren(headerData) {
@@ -2340,14 +2521,18 @@ function buildMetadataColumnChildren(headerData) {
return buildMetadataSectionChildren(headerData);
}
function buildTitleBlockChildren(headerData, orcidImageData) {
function buildTitleBlockChildren(headerData, orcidImageData, tmrLogoImageData) {
const children = [];
const previewAuthor = headerData.previewAuthor || {};
const authors = headerData.authors || [];
const organIndexMap = buildOrganIndexMap(headerData.organList || []);
const addressList = getAddressList(headerData);
pushTitleBlockModule(children, createManuscriptTitleParagraph(headerData), EMPTY_LINE_AFTER_TITLE);
pushTitleBlockModule(
children,
createManuscriptTitleParagraph(headerData, tmrLogoImageData),
EMPTY_LINE_AFTER_TITLE
);
let authorParagraph = null;
if (authors.length) {
@@ -2392,12 +2577,12 @@ function buildTitleBlockChildren(headerData, orcidImageData) {
return children;
}
export function buildAuthorHeaderSectionGroups(headerData, orcidImageData) {
export function buildAuthorHeaderSectionGroups(headerData, orcidImageData, tmrLogoImageData) {
if (!headerData) {
return [];
}
const titleBlockChildren = buildTitleBlockChildren(headerData, orcidImageData);
const titleBlockChildren = buildTitleBlockChildren(headerData, orcidImageData, tmrLogoImageData);
if (!titleBlockChildren.length) {
return [];
}
@@ -2407,7 +2592,7 @@ export function buildAuthorHeaderSectionGroups(headerData, orcidImageData) {
return [];
}
/** 首页:标题块 → 2 个连续分节符(中间空行 ¶)→ 首页表格(栏)→ 连续分节符 → 正文双栏 */
/** 首页:标题块(单栏)→ 连续分节符 → 首页元数据表格(栏)→ 连续分节符 → 正文双栏 */
return [
{
columnCount: 1,