From 77703a855a4fac04b1a2f919226124d1deaa19c0 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: Thu, 4 Jun 2026 17:38:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/global-math.js | 10 +- src/components/common/common.vue | 10 +- src/components/common/langs/en.js | 23 +- src/components/common/langs/zh.js | 20 +- src/components/page/GenerateCharts.vue | 8 + src/components/page/OnlineProofreading.vue | 8 + src/components/page/articleAdd.vue | 53 +- .../page/articleProcessRevision.vue | 62 +- src/components/page/articleReviewerAIAdd.vue | 1 + src/components/page/articleReviewerAdd.vue | 9 +- src/components/page/comArtHtmlCreatNew.vue | 7 + .../page/components/table/DynamicTable.vue | 6 +- .../page/components/table/LatexDataPanel.vue | 337 ++++++++++- .../page/components/table/content.vue | 35 +- .../page/components/table/editTable.vue | 7 +- src/components/page/components/table/word.vue | 139 +++-- .../page/components/table/wordHtml.vue | 7 +- .../components/table/wordHtmlTypesetting.vue | 4 +- src/components/page/edit_per_text.vue | 2 +- src/components/page/mailboxSend.vue | 18 +- src/utils/mathFormulaModule.js | 160 +++++- src/utils/wordImportReport.js | 324 +++++++++++ src/utils/wordMathImport.js | 532 +++++++++++++++++- 23 files changed, 1610 insertions(+), 172 deletions(-) create mode 100644 src/utils/wordImportReport.js diff --git a/public/js/global-math.js b/public/js/global-math.js index 4577c79..8b85a49 100644 --- a/public/js/global-math.js +++ b/public/js/global-math.js @@ -32,17 +32,21 @@ function stripLatexDelimiters(latex) { .trim(); } -/** 块/行内统一用 $$ 渲染,保证公式大小一致;排版由 data-wrap + CSS 控制 */ -function buildLatexContentForRender(latex) { +/** 按 data-wrap 选择行内 $ 或块级 $$ 分隔符 */ +function buildLatexContentForRender(latex, wrapMode) { const raw = stripLatexDelimiters(latex); if (!raw) return ''; + if (wrapMode === 'inline') { + return '$' + raw + '$'; + } return '$$' + raw + '$$'; } function prepareWmathElement(element) { const latexContent = element.getAttribute('data-latex'); if (!latexContent) return; - element.innerHTML = buildLatexContentForRender(latexContent); + const wrap = element.getAttribute('data-wrap') || 'block'; + element.innerHTML = buildLatexContentForRender(latexContent, wrap); } // **定义全局渲染方法** 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 2ae466b..409b57f 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -1087,11 +1087,11 @@ const en = { importWordMathNoNew: 'All formulas already exist. No new rows added.', importWordMathParsed: 'Word parsed: {parsed} formula(s) found, {added} added.', importWordMathParsedNone: 'Word parsed: {parsed} formula(s) found; all already exist.', - mathFormula: 'Latex', + mathFormula: 'LaTeX-compliant', mathFormulaMore: 'more formulas', latexDataCopy: 'Copy', latexDataCopied: 'Copied', - latexDataClickToCopy: 'Click a formula to copy', + latexDataClickToCopy: 'Click a row number or formula to copy', latexDataAdd: 'Add', latexDataEdit: 'Edit', latexDataDelete: 'Delete', @@ -1108,6 +1108,23 @@ const en = { latexMathEditorTooltip: 'Insert or edit a LaTeX formula', latexMathConfirm: 'OK', latexDataCancel: 'Cancel', + latexDataRowIssueTip: 'This formula may have recognition or rendering issues; click Edit to review', + latexDataIssueSummary: '{count} formula(s) failed to render (rows {indexes}). Row numbers are in red. This notice hides once all are fixed and saved.', + latexDataIssueInlinePrefix: '{count} failed to render:', + latexDataIssueLocate: 'Locate issues ({count})', + latexDataWordDownloadHtml: 'Download source HTML', + latexDataWordPreviewTitle: 'Word formula source report', + latexDataWordPreviewHint: + 'Badge number = formula list row. No badge = not in the list. Top links are failed rows only.', + latexDataWordReportIssuePrefix: 'Fix row:', + latexDataWordPreviewLegendOk: 'Green · OK', + latexDataWordPreviewLegendErr: 'Red · needs fix', + latexDataWordPreviewLegendDup: 'Orange · duplicate', + latexDataWordReportDupPrefix: 'Duplicate rows (same LaTeX):', + latexDataWordReportDupGroup: 'Same formula', + latexDataWordReportDupTag: 'Dup #{n}', + latexDataWordReportDupCount: '×{n}', + latexDataWordReportDownloaded: 'Source HTML downloaded. Open it in your browser.', latexDataOk: 'Save', editMathFormulaSuccess: 'Math formulas updated successfully.', selectOne: 'Please select only a single paragraph!', @@ -1591,6 +1608,7 @@ const en = { latestInvitationLabel: 'Latest invitation: ', affiliation: 'Affiliation: ', field: 'Field: ', + fieldAi: 'AI Field: ', detail: 'Detail', select: 'Select', reviewing: 'Reviewing', @@ -1605,6 +1623,7 @@ const en = { dialogAffiliation: 'Affiliation :', dialogResearchAreas: 'Research areas :', dialogField: 'Field :', + dialogFieldAi: 'AI Field :', dialogIntroduction: 'Introduction :', cancel: 'Cancel' }, diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index d53ff6c..5a41c4a 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -1073,7 +1073,7 @@ const zh = { importWordMathNoNew: '公式已存在,未新增重复项', importWordMathParsed: 'Word 解析完成:识别 {parsed} 个公式,新增 {added} 个', importWordMathParsedNone: 'Word 解析完成:识别 {parsed} 个公式,均已存在,未新增', - mathFormula: 'Latex', + mathFormula: 'LaTeX-compliant', mathFormulaMore: '条公式', latexDataCopy: '复制', latexDataCopied: '已复制', @@ -1095,6 +1095,22 @@ const zh = { latexMathConfirm: '确定', latexDataCancel: '取消', latexDataOk: '保存', + latexDataRowIssueTip: '该公式可能识别异常或未正确渲染,建议点「修改」检查', + latexDataIssueSummary: '共 {count} 处未解析成功(序号 {indexes}),对应序号已标红;全部修正保存后本提示将自动消失', + latexDataIssueInlinePrefix: '共 {count} 处未解析:', + latexDataIssueLocate: '定位未解析({count})', + latexDataWordDownloadHtml: '下载源稿 HTML', + latexDataWordPreviewTitle: 'Word 公式源稿报告', + latexDataWordPreviewHint: '角标数字=公式列表行号;无角标表示该处公式不在列表中。顶部仅列出需修改的行号。', + latexDataWordReportIssuePrefix: '需修改:', + latexDataWordPreviewLegendOk: '绿色 · 通过', + latexDataWordPreviewLegendErr: '红色 · 需修改', + latexDataWordPreviewLegendDup: '橙色 · 重复公式', + latexDataWordReportDupPrefix: '重复序号(相同 LaTeX,点击跳转):', + latexDataWordReportDupGroup: '相同公式序号', + latexDataWordReportDupTag: '重复#{n}', + latexDataWordReportDupCount: '×{n}', + latexDataWordReportDownloaded: '已生成并下载源稿 HTML,请用浏览器打开查看', editMathFormulaSuccess: '数字公式更新成功', selectOne:'请只勾选单个段落!', alreadyCommented:'文本中已有批注内容请重新选择', @@ -1572,6 +1588,7 @@ const zh = { latestInvitationLabel: '最新邀请:', affiliation: '单位:', field: '领域:', + fieldAi: 'AI 领域:', detail: '详情', select: '选择', reviewing: '审稿中', @@ -1586,6 +1603,7 @@ const zh = { dialogAffiliation: '单位:', dialogResearchAreas: '研究领域:', dialogField: '领域:', + dialogFieldAi: 'AI 领域:', dialogIntroduction: '简介:', cancel: '取消' }, diff --git a/src/components/page/GenerateCharts.vue b/src/components/page/GenerateCharts.vue index 3047d00..b5fbafd 100644 --- a/src/components/page/GenerateCharts.vue +++ b/src/components/page/GenerateCharts.vue @@ -415,6 +415,7 @@ type="content" @openLatexEditor="openLatexEditor" v-if="addContentVisible" + :enable-import-word-math="true" ref="addContent" style="margin-left: -115px" > @@ -684,6 +685,12 @@ export default { handleSaveContent() { this.$refs.commonContent.getTinymceContent('content'); }, + clearWordSelectionUI() { + const word = this.$refs.commonWord; + if (word && typeof word.clearParagraphSelection === 'function') { + word.clearParagraphSelection(); + } + }, handleSaveAddContent() { this.$refs.addContent.getTinymceContent('addcontent'); }, @@ -800,6 +807,7 @@ export default { if (res.code == 0) { loading.close(); this.editVisible = false; + this.clearWordSelectionUI(); this.refreshCurrentContent('content', am_id, res.data); this.getCommentList(); } else { diff --git a/src/components/page/OnlineProofreading.vue b/src/components/page/OnlineProofreading.vue index 0251f35..8c1c9c5 100644 --- a/src/components/page/OnlineProofreading.vue +++ b/src/components/page/OnlineProofreading.vue @@ -409,6 +409,7 @@ @getContent="getContent" @openLatexEditor="openLatexEditor" v-if="addContentVisible" + :enable-import-word-math="true" ref="addContent" style="margin-left: -115px" > @@ -687,6 +688,12 @@ export default { handleSaveContent() { this.$refs.commonContent.getTinymceContent('content'); }, + clearWordSelectionUI() { + const word = this.$refs.commonWord; + if (word && typeof word.clearParagraphSelection === 'function') { + word.clearParagraphSelection(); + } + }, handleSaveAddContent() { this.$refs.addContent.getTinymceContent('addcontent'); }, @@ -752,6 +759,7 @@ export default { .then(async (res) => { if (res.code == 0) { this.editVisible = false; + this.clearWordSelectionUI(); this.getDate(); this.getCommentList(); } diff --git a/src/components/page/articleAdd.vue b/src/components/page/articleAdd.vue index d017398..543a0f1 100644 --- a/src/components/page/articleAdd.vue +++ b/src/components/page/articleAdd.vue @@ -1102,6 +1102,10 @@ import ProgressBar from '@/components/page/components/article/progress.vue'; import { updateStepStatus, markStepAsSaved } from '@/components/page/components/article/checkStepCompletion.js'; import deepEqual from '@/components/page/components/article/deepEqual.js'; import { set } from 'vue'; +import { + buildArticleAddTableListFromWordFile, + buildArticleAddTableListItems +} from '@/utils/mathFormulaModule'; export default { components: { JournalSelector, @@ -2750,21 +2754,30 @@ export default { // event.percentage 就是当前进度(百分比,浮点数) this.uploadPercentage = Math.round(event.percent); // 取整数 }, + submitArticleTableList(list) { + if (!list || !list.length) return Promise.resolve(); + return this.$api + .post('api/Article/addArticleTable', { + article_id: this.stagingID, + list + }) + .then(() => { + this.isShowCommonWord = true; + setTimeout(() => { + this.isShowProgress = false; + }, 500); + }); + }, addWordTablesList(tables) { - var data = { - article_id: this.stagingID, - - list: tables.map((e) => ({ - table: JSON.stringify([...e]), - type: 0, - html_data: '' - })) - }; - this.$api.post('api/Article/addArticleTable', data).then((res) => { - this.isShowCommonWord = true; - setTimeout(() => { - this.isShowProgress = false; - }, 500); + this.submitArticleTableList(buildArticleAddTableListItems(tables, null)); + }, + processManuscriptWordTables(file) { + if (!file) return; + const extractFn = this.$commonJS.extractWordTablesToArrays.bind(this.$commonJS); + buildArticleAddTableListFromWordFile(file, extractFn).then((list) => { + if (list.length) { + this.submitArticleTableList(list); + } }); }, upLoadWordTables() {}, @@ -2839,17 +2852,9 @@ export default { window.history.replaceState({}, document.title, url.toString()); if (this.form.article_id) { - if (File) { + if (File && File.raw) { loading.close(); - var that = this; - const reader = new FileReader(); - reader.onload = function (e) { - that.$commonJS.extractWordTablesToArrays(File.raw, function (wordTables) { - that.addWordTablesList(wordTables); - loading.close(); - },that.form.article_id); - }; - reader.readAsArrayBuffer(File.raw); + this.processManuscriptWordTables(File.raw); } await this.onStaging(5); await this.TempoAuthor(); diff --git a/src/components/page/articleProcessRevision.vue b/src/components/page/articleProcessRevision.vue index 811e363..11cbb8d 100644 --- a/src/components/page/articleProcessRevision.vue +++ b/src/components/page/articleProcessRevision.vue @@ -525,6 +525,8 @@ + + +
+