From 5a1bbb0894411e28bc0602f376f3de2a347dee2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com> Date: Wed, 17 Jun 2026 17:02:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E4=BD=9C=E8=80=85=E7=AB=AF?= =?UTF-8?q?=20=20Article=20Proofreading=20=EF=BC=88=E5=8E=9F=E6=AD=A5?= =?UTF-8?q?=E9=AA=A42=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/index.js | 4 +- src/components/common/common.vue | 10 +- src/components/common/langs/en.js | 6 +- src/components/common/langs/zh.js | 6 +- src/components/page/Complete_profile.vue | 8 +- src/components/page/components/table/word.vue | 148 ---------- src/utils/manuscriptCitationRelevance.js | 275 +++++++++++++++--- src/utils/manuscriptReferenceHtml.js | 2 +- 8 files changed, 261 insertions(+), 198 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 487fb66..525753d 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -19,8 +19,8 @@ const service = axios.create({ // baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换 // baseURL: 'http://www.tougao.com/', //测试本地 记得切换 // baseURL: 'http://192.168.110.110/tougao/public/index.php/', - baseURL: '/api', //本地 - // baseURL: '/', //正式 + // baseURL: '/api', //本地 + baseURL: '/', //正式 }); diff --git a/src/components/common/common.vue b/src/components/common/common.vue index a1781ec..9bac70a 100644 --- a/src/components/common/common.vue +++ b/src/components/common/common.vue @@ -2,14 +2,14 @@ //记得切换 //正式 -// const mediaUrl = '/public/'; -// const baseUrl = '/'; +const mediaUrl = '/public/'; +const baseUrl = '/'; //正式环境 -const mediaUrl = 'https://submission.tmrjournals.com/public/'; -// const mediaUrl = 'http://zmzm.tougao.dev.com/public/'; -const baseUrl = '/api' +// const mediaUrl = 'https://submission.tmrjournals.com/public/'; +// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/'; +// const baseUrl = '/api' //测试环境 diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js index 117010f..d1800fc 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -1145,7 +1145,7 @@ const en = { typesettingType1: 'Vertical A4', AnnotationList: 'Annotation List', Annotations: 'Comments', - exportWord: 'Export Word', + exportWord: 'Generate Word file', exportManuscriptEmpty: 'No content to export.', exportManuscriptSuccess: 'Word downloaded.', exportManuscriptFail: 'Failed to export Word.', @@ -1174,8 +1174,10 @@ const en = { citeRelevanceBatchTip: '{n} references per batch within each paragraph', citeRelevanceRefBatch: 'Batch refs {nums}', citeRelevanceBatchProgress: 'Paragraph batch {current}/{total}', - citeRelevanceGroupTotal: '{n} paragraphs to review', + citeRelevanceGroupTotal: '{n} batches to review', citeRelevanceGroupNavLabel: 'Para {paragraph} · {refs}', + citeRelevanceTableNavLabel: 'Table {paragraph} · {refs}', + citeRelevancePreviewTitle: 'Preview', citeRelevanceJumpTo: 'Jump to', citeRelevanceCopyGroup: 'Copy paragraph', citeRelevanceDownloadHtml: 'Download HTML', diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index a9482c6..ec125ea 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -1131,7 +1131,7 @@ const zh = { typesettingType1: '竖向 A4', AnnotationList: '批注列表', Annotations: '批注', - exportWord: '导出 Word', + exportWord: '生成 Word 文件', exportManuscriptEmpty: '没有可导出的内容。', exportManuscriptSuccess: 'Word 已下载。', exportManuscriptFail: 'Word 导出失败。', @@ -1160,8 +1160,10 @@ const zh = { citeRelevanceBatchTip: '每段正文按 {n} 条参考文献一批提问', citeRelevanceRefBatch: '本批文献 {nums}', citeRelevanceBatchProgress: '本段第 {current}/{total} 批', - citeRelevanceGroupTotal: '共 {n} 段待核查', + citeRelevanceGroupTotal: '共 {n} 组待核查', citeRelevanceGroupNavLabel: '段落 {paragraph} · {refs}', + citeRelevanceTableNavLabel: '表格 {paragraph} · {refs}', + citeRelevancePreviewTitle: '内容预览', citeRelevanceJumpTo: '快速跳转', citeRelevanceCopyGroup: '复制本段', citeRelevanceDownloadHtml: '下载 HTML', diff --git a/src/components/page/Complete_profile.vue b/src/components/page/Complete_profile.vue index 774d765..8ced3bd 100644 --- a/src/components/page/Complete_profile.vue +++ b/src/components/page/Complete_profile.vue @@ -136,16 +136,16 @@ -
- + +
- + -->
diff --git a/src/components/page/components/table/word.vue b/src/components/page/components/table/word.vue index f863ba6..8279bd0 100644 --- a/src/components/page/components/table/word.vue +++ b/src/components/page/components/table/word.vue @@ -92,49 +92,6 @@ > {{ $t('commonTable.exportWord') }} -
  • - - - {{ $t('commonTable.refHtmlDownload') }} -
  • -
  • - - - {{ $t('commonTable.refHtmlToWord') }} -
  • -
  • { - try { - const referenceHtmlItems = parseReferencesEditableHtml(String(reader.result || '')); - if (!referenceHtmlItems.length) { - this.$message.warning(this.$t('commonTable.refHtmlParseEmpty')); - return; - } - - await downloadManuscriptWord(this.wordList, this.mediaUrl, 'manuscript', { - fetchReferences: false, - referenceHtmlItems: referenceHtmlItems, - apiClient: this.$api, - articleId: this.articleId, - pArticleId: this.pArticleId - }); - this.$message.success(this.$t('commonTable.refHtmlToWordSuccess')); - } catch (err) { - console.error(err); - if (err && err.message === 'NO_CONTENT') { - this.$message.warning(this.$t('commonTable.exportManuscriptEmpty')); - } else { - this.$message.error(this.$t('commonTable.refHtmlToWordFail')); - } - } finally { - this.exportingReferencesHtmlWord = false; - if (input) { - input.value = ''; - } - } - }; - reader.onerror = () => { - this.exportingReferencesHtmlWord = false; - if (input) { - input.value = ''; - } - this.$message.error(this.$t('commonTable.refHtmlToWordFail')); - }; - reader.readAsText(file, 'UTF-8'); - }, async handleOpenCitationRelevance() { if (this.citationRelevanceLoading) { return; diff --git a/src/utils/manuscriptCitationRelevance.js b/src/utils/manuscriptCitationRelevance.js index 0964680..6f8565c 100644 --- a/src/utils/manuscriptCitationRelevance.js +++ b/src/utils/manuscriptCitationRelevance.js @@ -1,6 +1,9 @@ /** 正文引用标记,如 [1]、[1-7]、[4,6,8] */ const CITATION_BRACKET_RE = /(?:\s*)?\[([^\]]+)\](?:\s*<\/blue>)?/gi; +/** 每批最多分析的参考文献条数 */ +export const CITATION_REVIEW_BATCH_SIZE = 4; + function decodeHtmlEntities(text) { return String(text || '') .replace(/ /g, ' ') @@ -117,17 +120,162 @@ export function formatReferencePlainText(ref) { return parts.filter(Boolean).join('. ').replace(/\.\s*\./g, '.').trim(); } -function isTextParagraphItem(item) { +function isTableItem(item) { + return !!(item && item.type == 2 && item.table); +} + +function isCitableReviewItem(item) { if (!item) { return false; } - if (item.type == 1 || item.type == 2) { + if (item.type == 1) { return false; } + if (isTableItem(item)) { + return true; + } const html = String(item.content || item.text || '').trim(); return !!html; } +function forEachTableRowCells(row, fn) { + if (!row || !Array.isArray(row)) { + return; + } + row.forEach(function (cell) { + if (cell && typeof cell === 'object' && cell.text != null) { + fn(cell); + } + }); +} + +function collectTablePartsHtml(table) { + const parts = []; + if (!table) { + return ''; + } + if (table.title) { + parts.push(String(table.title)); + } + (table.tableHeader || []).forEach(function (row) { + forEachTableRowCells(row, function (cell) { + parts.push(String(cell.text || '')); + }); + }); + (table.tableContent || []).forEach(function (row) { + forEachTableRowCells(row, function (cell) { + parts.push(String(cell.text || '')); + }); + }); + if (table.note) { + parts.push(String(table.note)); + } + return parts.join('\n'); +} + +function buildTablePlainText(table) { + const lines = []; + if (!table) { + return ''; + } + if (table.title) { + lines.push(htmlToPlainText(table.title)); + } + (table.tableHeader || []).forEach(function (row) { + const cells = []; + forEachTableRowCells(row, function (cell) { + cells.push(htmlToPlainText(cell.text)); + }); + if (cells.length) { + lines.push(cells.join(' | ')); + } + }); + (table.tableContent || []).forEach(function (row) { + const cells = []; + forEachTableRowCells(row, function (cell) { + cells.push(htmlToPlainText(cell.text)); + }); + if (cells.length) { + lines.push(cells.join(' | ')); + } + }); + if (table.note) { + lines.push(htmlToPlainText(table.note)); + } + return lines.filter(Boolean).join('\n'); +} + +function getReviewItemSourceHtml(item) { + if (isTableItem(item)) { + return collectTablePartsHtml(item.table); + } + return String(item.content || item.text || ''); +} + +function getReviewItemPlainText(item) { + if (isTableItem(item)) { + return buildTablePlainText(item.table); + } + return htmlToPlainText(getReviewItemSourceHtml(item)); +} + +function buildTableRowPreviewHtml(row, oddRowIds) { + if (!row || !Array.isArray(row)) { + return ''; + } + const isOdd = row.rowId && (oddRowIds || []).indexOf(row.rowId) !== -1; + let cellsHtml = ''; + row.forEach(function (cell) { + if (!cell || typeof cell !== 'object') { + return; + } + cellsHtml += + '' + + (cell.text || '') + + ''; + }); + return '' + cellsHtml + ''; +} + +function buildReviewTablePreviewHtml(item) { + const table = item.table; + if (!table) { + return ''; + } + let tableRows = ''; + (table.tableHeader || []).forEach(function (row) { + tableRows += buildTableRowPreviewHtml(row, []); + }); + (table.tableContent || []).forEach(function (row) { + tableRows += buildTableRowPreviewHtml(row, table.oddRowIds || []); + }); + + return ( + '
    ' + + (table.title ? '
    ' + table.title + '
    ' : '') + + '' + + tableRows + + '
    ' + + (table.note ? '
    ' + table.note + '
    ' : '') + + '
    ' + ); +} + +function buildReviewContentPreviewHtml(item) { + if (isTableItem(item)) { + return buildReviewTablePreviewHtml(item); + } + const html = String(item.content || item.text || ''); + if (!html) { + return ''; + } + return '
    ' + html + '
    '; +} + function buildCitationEntry(citeNum, refList) { const ref = refList[citeNum - 1] || null; return { @@ -144,19 +292,40 @@ export function formatCitationNumsLabel(citeNums) { }).join(' '); } +function chunkCitationNumbers(citeNums, batchSize) { + const size = batchSize || CITATION_REVIEW_BATCH_SIZE; + const chunks = []; + const nums = citeNums || []; + let i = 0; + + for (i = 0; i < nums.length; i += size) { + chunks.push(nums.slice(i, i + size)); + } + + return chunks; +} + export function getCitationReviewGroupMeta(item, index, labels) { const l = labels || {}; const groupNo = index + 1; const paragraphNo = item.paragraphIndex + 1; const citeNumsLabel = formatCitationNumsLabel(item.citeNums); const citeNumsPlain = (item.citeNums || []).join('、'); + const batchTotal = item.batchTotal || 1; + const batchIndex = item.batchIndex || 1; + let batchSuffix = ''; - let title = '正文段落 ' + paragraphNo + ' · ' + citeNumsLabel; - if (l.groupLabel) { - title = l.groupLabel + if (batchTotal > 1 && l.batchProgress) { + batchSuffix = ' · ' + l.batchProgress.replace('{current}', batchIndex).replace('{total}', batchTotal); + } + + let title = (item.sourceType === 'table' ? '表格 ' : '正文段落 ') + paragraphNo + ' · ' + citeNumsLabel + batchSuffix; + const navLabel = item.sourceType === 'table' ? l.tableGroupLabel : l.groupLabel; + if (navLabel) { + title = navLabel .replace('{group}', groupNo) .replace('{paragraph}', paragraphNo) - .replace('{refs}', citeNumsLabel); + .replace('{refs}', citeNumsLabel) + batchSuffix; } return { @@ -170,39 +339,51 @@ export function getCitationReviewGroupMeta(item, index, labels) { } /** - * 构建核查队列:每个含引文的正文段落一组,该段全部参考文献一并询问; - * 同一条文献出现在不同段落时,会在各段落中分别询问。 + * 构建核查队列:含引文的正文段落与表格均纳入;每处按最多 4 条参考文献拆批。 */ -export function buildCitationReviewQueue(wordList, references) { +export function buildCitationReviewQueue(wordList, references, batchSize) { const items = []; const refList = references || []; + const size = batchSize || CITATION_REVIEW_BATCH_SIZE; - (wordList || []).forEach(function (item, paragraphIndex) { - if (!isTextParagraphItem(item)) { + (wordList || []).forEach(function (item, contentIndex) { + if (!isCitableReviewItem(item)) { return; } - const html = item.content || item.text || ''; - const citeNums = parseCitationNumbersFromHtml(html); + const sourceHtml = getReviewItemSourceHtml(item); + const citeNums = parseCitationNumbersFromHtml(sourceHtml); if (!citeNums.length) { return; } - const paragraphText = htmlToPlainText(html); - const citations = citeNums.map(function (citeNum) { - return buildCitationEntry(citeNum, refList); - }); + const sourceType = isTableItem(item) ? 'table' : 'paragraph'; + const sourcePlainText = getReviewItemPlainText(item); + const previewHtml = buildReviewContentPreviewHtml(item); + const citeChunks = chunkCitationNumbers(citeNums, size); + const batchTotal = citeChunks.length; - items.push({ - paragraphIndex: paragraphIndex, - amId: item.am_id, - paragraphHtml: html, - paragraphText: paragraphText, - citeNums: citeNums.slice(), - citations: citations, - hasMissingReference: citations.some(function (c) { - return c.referenceMissing; - }) + citeChunks.forEach(function (chunkNums, batchIndex) { + const citations = chunkNums.map(function (citeNum) { + return buildCitationEntry(citeNum, refList); + }); + + items.push({ + contentIndex: contentIndex, + paragraphIndex: contentIndex, + sourceType: sourceType, + amId: item.am_id, + paragraphHtml: sourceHtml, + paragraphText: sourcePlainText, + previewHtml: previewHtml, + citeNums: chunkNums.slice(), + citations: citations, + batchIndex: batchIndex + 1, + batchTotal: batchTotal, + hasMissingReference: citations.some(function (c) { + return c.referenceMissing; + }) + }); }); }); @@ -216,6 +397,7 @@ export function buildWenaiRelevancePrompt(item) { const paragraphText = item.paragraphText || ''; const citations = item.citations || []; + const isTable = item.sourceType === 'table'; const citeLabels = citations.map(function (c) { return '[' + c.citeNum + ']'; }); @@ -227,12 +409,18 @@ export function buildWenaiRelevancePrompt(item) { }); const labelList = citeLabels.join('、') || ''; + const intro = isTable + ? '请审读以下表格及其引用文献,结合表格内容、标题与注释,判断各文献与表格的相关程度。' + : '请审读以下正文段落及其引用文献,结合该段落的论述内容,判断各文献与正文的相关程度。'; + const contentLabel = isTable ? '【表格】' : '【正文段落】'; + const contentSection = isTable + ? '' + : contentLabel + '\n' + paragraphText + '\n\n'; return ( - '请审读以下正文段落及其引用文献,结合该段落的论述内容,判断各文献与正文的相关程度。\n\n' + - '【正文段落】\n' + - paragraphText + + intro + '\n\n' + + contentSection + '【参考文献】\n' + refBlock.trim() + '\n\n' + @@ -241,8 +429,8 @@ export function buildWenaiRelevancePrompt(item) { '请按文献序号 ' + labelList + ' 依次说明,保留原文序号且顺序连续,不要重新编号或跳号。\n' + - '对每条文献分别给出关联判断:直接相关 / 强相关 / 弱相关 / 不相关,并用一句话简要说明理由。\n' + - '将全部内容合并写成一段连贯的批注文字,语气客观、专业、克制,可直接作为稿件文件批注使用。' + '对每条文献一条条列出分别给出关联判断:直接相关 / 强相关 / 弱相关 / 不相关,并用一句话简要说明理由。\n\n' + + '再结合上述给出的弱相关和不相关的文献写成一段简短的不相关理由批注文字,以编辑的语气建议,可直接作为稿件文件批注使用。' ); } @@ -270,6 +458,7 @@ export function buildCitationReviewStandaloneHtml(reviewItems, labels) { const copyAllText = l.copyAll || '复制全部'; const copySuccessText = l.copySuccess || '已复制'; const copyFailText = l.copyFail || '复制失败'; + const previewTitle = l.previewTitle || '内容预览'; let navHtml = ''; let bodyHtml = ''; @@ -280,6 +469,13 @@ export function buildCitationReviewStandaloneHtml(reviewItems, labels) { const meta = getCitationReviewGroupMeta(item, i, labels); const prompt = buildWenaiRelevancePrompt(item); const promptId = 'cite-prompt-' + meta.groupNo; + const previewBlock = item.previewHtml + ? '
    ' + + escapeHtml(previewTitle) + + '
    ' + + item.previewHtml + + '
    ' + : ''; navHtml += '' + @@ -320,7 +517,7 @@ export function buildCitationReviewStandaloneHtml(reviewItems, labels) { '' + '
    ' + @@ -401,6 +605,9 @@ export function buildCitationReviewHtmlLabels(translate) { copyAll: t('commonTable.citeRelevanceCopyAllWenai'), copySuccess: t('commonTable.citeRelevanceCopySuccess'), copyFail: t('commonTable.citeRelevanceCopyFail'), - groupLabel: t('commonTable.citeRelevanceGroupNavLabel') + groupLabel: t('commonTable.citeRelevanceGroupNavLabel'), + tableGroupLabel: t('commonTable.citeRelevanceTableNavLabel'), + batchProgress: t('commonTable.citeRelevanceBatchProgress'), + previewTitle: t('commonTable.citeRelevancePreviewTitle') }; } diff --git a/src/utils/manuscriptReferenceHtml.js b/src/utils/manuscriptReferenceHtml.js index 453a39a..05ea0ec 100644 --- a/src/utils/manuscriptReferenceHtml.js +++ b/src/utils/manuscriptReferenceHtml.js @@ -147,7 +147,7 @@ export function buildReferencesEditableHtml(references, labels) { '' + escapeHtml(pageTitle) + '' + '