数字公式优化

This commit is contained in:
2026-06-01 17:59:22 +08:00
parent 4b1a10b20c
commit 08aa3327db
13 changed files with 2526 additions and 89 deletions

View File

@@ -1083,6 +1083,33 @@ const en = {
importWordMathLoading: 'Importing Word...',
importWordMathSuccess: 'Word imported. Formulas converted to LaTeX.',
importWordMathFail: 'Word import failed. Please check the file format.',
importWordMathNoFormula: 'No math formulas found in the Word file.',
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',
mathFormulaMore: 'more formulas',
latexDataCopy: 'Copy',
latexDataCopied: 'Copied',
latexDataClickToCopy: 'Click a formula to copy',
latexDataAdd: 'Add',
latexDataEdit: 'Edit',
latexDataDelete: 'Delete',
latexDataDeleteAll: 'Clear all',
latexDataDeleteAllConfirm: 'Clear all formulas?',
latexDataEmpty: 'No formulas yet. Upload Word or add manually.',
latexDataNoFormulaDetected: 'No math formulas detected.',
latexDataPlaceholder: 'Enter LaTeX formula',
latexMathPlaceholder: 'Enter LaTeX code (e.g., \\frac{a}{b}, x^{2}, \\sqrt{x})',
latexMathCopyCode: 'Copy LaTeX code',
latexMathCopied: 'Copied',
latexMathWrapToInline: 'Change to inline with text',
latexMathWrapToBlock: 'Change to block display',
latexMathEditorTooltip: 'Insert or edit a LaTeX formula',
latexMathConfirm: 'OK',
latexDataCancel: 'Cancel',
latexDataOk: 'Save',
editMathFormulaSuccess: 'Math formulas updated successfully.',
selectOne: 'Please select only a single paragraph',
alreadyCommented: 'There are already annotations in the text, please select again!',
Multicolumn: 'Multicolumn',

View File

@@ -1069,6 +1069,33 @@ const zh = {
importWordMathLoading: '正在导入 Word...',
importWordMathSuccess: 'Word 导入成功,公式已转为 LaTeX',
importWordMathFail: 'Word 导入失败,请检查文件格式',
importWordMathNoFormula: '未在 Word 中识别到数学公式',
importWordMathNoNew: '公式已存在,未新增重复项',
importWordMathParsed: 'Word 解析完成:识别 {parsed} 个公式,新增 {added} 个',
importWordMathParsedNone: 'Word 解析完成:识别 {parsed} 个公式,均已存在,未新增',
mathFormula: 'Latex',
mathFormulaMore: '条公式',
latexDataCopy: '复制',
latexDataCopied: '已复制',
latexDataClickToCopy: '点击数字公式即可复制',
latexDataAdd: '新增',
latexDataEdit: '修改',
latexDataDelete: '删除',
latexDataDeleteAll: '清空',
latexDataDeleteAllConfirm: '确定清空全部公式吗?',
latexDataEmpty: '暂无公式,可上传 Word 或手动新增',
latexDataNoFormulaDetected: '暂未检测到数字公式',
latexDataPlaceholder: '输入 LaTeX 公式',
latexMathPlaceholder: '请输入 LaTeX 代码(如 \\frac{a}{b}、x^{2}、\\sqrt{x}',
latexMathCopyCode: '复制 LaTeX 代码',
latexMathCopied: '已复制',
latexMathWrapToInline: '改为行内公式',
latexMathWrapToBlock: '改为独立成行',
latexMathEditorTooltip: '插入或编辑 LaTeX 公式',
latexMathConfirm: '确定',
latexDataCancel: '取消',
latexDataOk: '保存',
editMathFormulaSuccess: '数字公式更新成功',
selectOne:'请只勾选单个段落!',
alreadyCommented:'文本中已有批注内容请重新选择',
Multicolumn:'多列',

View File

@@ -64,12 +64,14 @@
@onAddComment="onAddComment"
@addImage="handleImageAdd"
@addTable="handleTableAdd"
@importWordMath="handleImportWordMath"
@handlePaperclip="handlePaperclip"
@addComment="addCommentSetting"
@goToComment="goToComment"
@edit="handleFigureAndTableEdit"
@delete="handleFigureAndTableDelete"
@goToListComment="goToListComment"
@mathFormulasChange="onMathFormulasChange"
style="width: 100%; height: 100%; padding: 0 0px; box-sizing: border-box; background-color: #fff"
>
<template slot="catalogue1">
@@ -96,6 +98,7 @@
:contentList="Main_List"
:comments="comments"
:wordStyle="wordStyle"
:mathFormulasList="mathFormulasList"
@onDrop="onDrop"
@onLinkUnbind="handleUnbindLink"
@onLinkConfirm="handleConfirmLink"
@@ -121,6 +124,8 @@
@onEditTitle="onEditTitle"
@onAddRow="onAddRow"
@changeComment="changeComment"
@latexAdded="onLatexAdded"
@latexUpdated="onLatexUpdated"
style="width: calc(100%); height: calc(100%)"
:style="`100%`"
>
@@ -417,7 +422,7 @@
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editVisible = false"> Cancel </el-button>
<el-button @click="addContentVisible = false"> Cancel </el-button>
<el-button type="primary" @click="handleSaveAddContent">
<!-- <i class="el-icon-finished" style="margin-right: 5px"></i> -->
Save
@@ -434,6 +439,7 @@ import bus from '@/components/common/bus';
import { del, isShallow } from 'vue';
import Tiff from 'tiff.js';
import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑
import { LATEX_DATA_HTML_DATA, MATH_FORMULA_TABLE_TITLE } from '@/utils/mathFormulaModule';
import Tinymce from '@/components/page/components/Tinymce';
import bottomTinymce from '@/components/page/components/Tinymce';
import catalogue from '@/components/page/components/table/catalogue.vue';
@@ -455,6 +461,7 @@ export default {
editVisible: false,
currentId: null,
ManuscirptContent: [],
mathFormulasList: [],
articleId: this.$route.query.id,
isShowComment: false,
urlList: {
@@ -1526,6 +1533,54 @@ export default {
this.lineStyle.visiTitle = 'Add Table';
this.threeVisible = true;
},
onMathFormulasChange(payload) {
this.mathFormulasList = (payload && payload.list) || [];
},
onLatexAdded(data) {
if (this.$refs.commonWordHtmlTypeSetting) {
this.$refs.commonWordHtmlTypeSetting.onMathFormulaAdded(data);
}
},
onLatexUpdated(data) {
if (this.$refs.commonWordHtmlTypeSetting) {
this.$refs.commonWordHtmlTypeSetting.onMathFormulaUpdated(data);
}
},
async handleImportWordMath(file) {
if (!file) return;
const loading = this.$loading({
lock: true,
text: this.$t('commonTable.importWordMathLoading'),
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
try {
const html = await this.$commonJS.importWordDocumentWithMath(file);
const { tableData } = this.$commonJS.extractWmathTableDataFromHtml(html);
if (!tableData || tableData.length === 0) {
this.$message.warning(this.$t('commonTable.importWordMathNoFormula'));
return;
}
const res = await this.$api.post(this.urlList.addTable, {
article_id: this.articleId,
table_data: JSON.stringify(tableData),
html_data: LATEX_DATA_HTML_DATA,
note: '',
title: MATH_FORMULA_TABLE_TITLE
});
if (res.code == 0) {
this.$message.success(this.$t('commonTable.importWordMathSuccess'));
this.$refs.commonWordHtmlTypeSetting.refresh('addTable', res.data);
} else {
this.$message.error(res.msg || this.$t('commonTable.importWordMathFail'));
}
} catch (err) {
console.error('handleImportWordMath failed', err);
this.$message.error(this.$t('commonTable.importWordMathFail'));
} finally {
loading.close();
}
},
addUploadWordTable(data) {
this.lineStyle = { note: '', table: data.table_data, html_data: data.html_data };
this.lineStyle1 = { note: '', table: data.table_data, html_data: data.html_data };
@@ -1590,6 +1645,26 @@ export default {
this.$message.error(err.msg);
});
}
if (type == 'math') {
this.$api
.post(this.urlList.deleteTable, {
amt_id: data.amt_id,
article_id: this.articleId
})
.then(async (res) => {
if (res.status == 1) {
loading.close();
this.$refs.commonWordHtmlTypeSetting.replacement('math', data.amt_id);
} else {
loading.close();
this.$message.error(res.msg);
}
})
.catch((err) => {
loading.close();
this.$message.error(err.msg);
});
}
})
.catch((err) => {
// this.$message.error(err.msg);
@@ -1602,19 +1677,17 @@ export default {
this.picStyle = { ...data, extension: extension, picUrl: data.url };
this.picStyle.visiTitle = 'Edit Figure';
this.pictVisible = true;
} else if (type == 'table') {
} else if (type == 'table' || type == 'math') {
this.lineStyle = {};
this.lineStyle1 = {};
// 1. 提取处理逻辑
const formattedData = {
...data,
table: JSON.parse(data.table_data)
table: data.table || (data.table_data ? JSON.parse(data.table_data) : [])
};
// 2. 统一赋值
this.lineStyle = formattedData;
this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指向不同引用(如果需要独立修改)
this.lineStyle.visiTitle = 'Edit Table';
this.lineStyle1 = { ...formattedData };
this.lineStyle.visiTitle = type == 'math' ? 'Edit Math Formula' : 'Edit Table';
this.threeVisible = true;
}
},
@@ -2048,7 +2121,7 @@ export default {
strTitle = strTitle.replace(/<br\s*\/?>/gi, '');
var tableStr=JSON.stringify(cleanedTableList)
if (this.lineStyle.visiTitle == 'Edit Table') {
if (this.lineStyle.visiTitle == 'Edit Table' || this.lineStyle.visiTitle == 'Edit Math Formula') {
this.$api
.post(this.urlList.editTable, {
amt_id: this.lineStyle.amt_id,
@@ -2060,7 +2133,11 @@ export default {
.then((res) => {
if (res.code == 0) {
loading.close();
this.$message.success('Successfully edit Table!');
this.$message.success(
this.lineStyle.visiTitle == 'Edit Math Formula'
? this.$t('commonTable.editMathFormulaSuccess')
: 'Successfully edit Table!'
);
this.threeVisible = false;
setTimeout(() => {
this.$refs.commonWordHtmlTypeSetting.refresh(

View File

@@ -74,11 +74,13 @@
@onAddComment="onAddComment"
@addImage="handleImageAdd"
@addTable="handleTableAdd"
@importWordMath="handleImportWordMath"
@handlePaperclip="handlePaperclip"
@addComment="addCommentSetting"
@goToComment="goToComment"
@edit="handleImageEdit"
@goToListComment="goToListComment"
@mathFormulasChange="onMathFormulasChange"
style="width: 100%; height: 100%; padding: 0 0px; box-sizing: border-box; background-color: #fff"
>
</common-word-html-type-setting>
@@ -105,6 +107,8 @@
:contentList="Main_List"
:comments="comments"
:wordStyle="wordStyle"
:articleId="articleId"
:mathFormulasList="mathFormulasList"
@onDrop="onDrop"
@saveContent="saveContent"
@editComment="editComment"
@@ -129,6 +133,8 @@
@onEditTitle="onEditTitle"
@onAddRow="onAddRow"
@changeComment="changeComment"
@latexAdded="onLatexAdded"
@latexUpdated="onLatexUpdated"
style="width: calc(100%); height: calc(100%)"
:style="`100%`"
>
@@ -410,7 +416,7 @@
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editVisible = false"> Cancel </el-button>
<el-button @click="addContentVisible = false"> Cancel </el-button>
<el-button type="primary" @click="handleSaveAddContent">
<!-- <i class="el-icon-finished" style="margin-right: 5px"></i> -->
Save
@@ -427,6 +433,7 @@ import bus from '@/components/common/bus';
import { del, isShallow } from 'vue';
import Tiff from 'tiff.js';
import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑
import { LATEX_DATA_HTML_DATA, MATH_FORMULA_TABLE_TITLE } from '@/utils/mathFormulaModule';
import Tinymce from '@/components/page/components/Tinymce';
import bottomTinymce from '@/components/page/components/Tinymce';
import catalogue from '@/components/page/components/OnlineProofreading/catalogue.vue';
@@ -451,6 +458,7 @@ export default {
editVisible: false,
currentId: null,
ManuscirptContent: [],
mathFormulasList: [],
articleId: this.$route.query.id,
isShowComment: false,
urlList: {
@@ -1354,6 +1362,54 @@ export default {
this.lineStyle.visiTitle = 'Add Table';
this.threeVisible = true;
},
onMathFormulasChange(payload) {
this.mathFormulasList = (payload && payload.list) || [];
},
onLatexAdded(data) {
if (this.$refs.commonWordHtmlTypeSetting) {
this.$refs.commonWordHtmlTypeSetting.onMathFormulaAdded(data);
}
},
onLatexUpdated(data) {
if (this.$refs.commonWordHtmlTypeSetting) {
this.$refs.commonWordHtmlTypeSetting.onMathFormulaUpdated(data);
}
},
async handleImportWordMath(file) {
if (!file) return;
const loading = this.$loading({
lock: true,
text: this.$t('commonTable.importWordMathLoading'),
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
try {
const html = await this.$commonJS.importWordDocumentWithMath(file);
const { tableData } = this.$commonJS.extractWmathTableDataFromHtml(html);
if (!tableData || tableData.length === 0) {
this.$message.warning(this.$t('commonTable.importWordMathNoFormula'));
return;
}
const res = await this.$api.post(this.urlList.addTable, {
article_id: this.articleId,
table_data: JSON.stringify(tableData),
html_data: LATEX_DATA_HTML_DATA,
note: '',
title: MATH_FORMULA_TABLE_TITLE
});
if (res.code == 0) {
this.$message.success(this.$t('commonTable.importWordMathSuccess'));
this.$refs.commonWordHtmlTypeSetting.refresh('addTable', res.data);
} else {
this.$message.error(res.msg || this.$t('commonTable.importWordMathFail'));
}
} catch (err) {
console.error('handleImportWordMath failed', err);
this.$message.error(this.$t('commonTable.importWordMathFail'));
} finally {
loading.close();
}
},
addUploadWordTable(data) {
this.lineStyle = { note: '', table: data.table_data, html_data: data.html_data };
@@ -1367,16 +1423,16 @@ export default {
this.picStyle = { ...data, extension: extension, picUrl: data.url };
this.picStyle.visiTitle = 'Edit Figure';
this.pictVisible = true;
} else if (type == 'table') {
} else if (type == 'table' || type == 'math') {
this.lineStyle = {};
this.lineStyle = {
...data,
table: JSON.parse(data.table_data),
table: data.table || (data.table_data ? JSON.parse(data.table_data) : []),
html_data: data.html_data,
note: data.note,
title: data.title
};
this.lineStyle.visiTitle = 'Edit Table';
this.lineStyle.visiTitle = type == 'math' ? 'Edit Math Formula' : 'Edit Table';
this.threeVisible = true;
}
},
@@ -1757,7 +1813,7 @@ export default {
background: 'rgba(0, 0, 0, 0.7)'
});
if (content && cleanedTableList && cleanedTableList.length > 0) {
if (this.lineStyle.visiTitle == 'Edit Table') {
if (this.lineStyle.visiTitle == 'Edit Table' || this.lineStyle.visiTitle == 'Edit Math Formula') {
this.$api
.post(this.urlList.editTable, {
amt_id: this.lineStyle.amt_id,

View File

@@ -1882,12 +1882,12 @@
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
.el-image {
}
.journal-cover.el-image {
width: 100%;
height: 100%;
}
}
.journal-abbreviation {
font-size: 13px;
color: #606266;

View File

@@ -7,7 +7,24 @@
<div class="table-paper-view" @click.stop>
<div class="close-icon-top" @click="close">×</div>
<div v-if="type === 'table' && processedItem" class="thumbnailTableBox wordTableHtml table_Box pMain">
<div v-if="type === 'table' && processedItem && isLatexDataPreview" class="latex-data-preview">
<div class="tableTitle font">
<span v-html="renderText(processedItem.title)"></span>
</div>
<div
v-for="item in latexFormulaItems"
:key="item.index"
class="latex-preview-row"
>
<span class="latex-preview-index">{{ item.index }}</span>
<span class="latex-preview-content" v-html="item.html"></span>
</div>
<div v-if="processedItem.note" class="tableNote font">
<span v-html="renderText(processedItem.note)"></span>
</div>
</div>
<div v-else-if="type === 'table' && processedItem" class="thumbnailTableBox wordTableHtml table_Box pMain">
<div class="tableTitle font">
<span v-html="renderText(processedItem.title)"></span>
</div>
@@ -58,6 +75,7 @@
<script>
import { TableUtils } from '@/common/js/TableUtils';
import { mediaUrl } from '@/common/js/commonJS.js';
import { isMathFormulaTableTitle, parseFormulaRowsFromTableData } from '@/utils/mathFormulaModule';
export default {
name: 'TablePreviewer',
@@ -70,6 +88,20 @@ export default {
mediaUrl,
};
},
computed: {
isLatexDataPreview() {
return this.processedItem && isMathFormulaTableTitle(this.processedItem.title);
},
latexFormulaItems() {
if (!this.isLatexDataPreview || !this.processedItem) return [];
const raw = this.processedItem.table;
const tableList = typeof raw === 'string' ? JSON.parse(raw) : raw;
return parseFormulaRowsFromTableData(tableList).map((row, index) => ({
index: index + 1,
html: row[0] && row[0].text ? row[0].text : ''
}));
}
},
methods: {
async open(type, item,isNoBg) {
this.visible = true;
@@ -79,16 +111,20 @@ export default {
this.loading = true;
setTimeout(() => {
try {
const processed = this.processTableData(item.table);
this.processedItem = Object.freeze({
...item,
table: {
...item.table,
tableHeader: processed.tableHeader,
tableContent: processed.tableContent,
oddRowIds: isNoBg ? [] : processed.oddRowIds
}
});
if (isMathFormulaTableTitle(item.title)) {
this.processedItem = Object.freeze({ ...item });
} else {
const processed = this.processTableData(item.table);
this.processedItem = Object.freeze({
...item,
table: {
...item.table,
tableHeader: processed.tableHeader,
tableContent: processed.tableContent,
oddRowIds: isNoBg ? [] : processed.oddRowIds
}
});
}
} catch (err) {
console.error("解析表格失败", err);
} finally {
@@ -265,4 +301,45 @@ export default {
display: block;
width: 100%;
}
.latex-data-preview {
width: 100%;
}
.latex-preview-row {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 0;
border-bottom: 1px solid #f0f2f5;
}
.latex-preview-row:last-child {
border-bottom: none;
}
.latex-preview-index {
flex-shrink: 0;
width: 24px;
text-align: right;
font-size: 14px;
color: #909399;
}
.latex-preview-content {
flex: 1;
min-width: 0;
font-size: 16px;
line-height: 1.4;
}
.latex-preview-content ::v-deep wmath {
display: inline !important;
width: auto !important;
}
.latex-preview-content ::v-deep mjx-container {
font-size: 16px !important;
margin: 0 !important;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,35 @@
<ul class="operateBox">
<div class="base-border base-padding-all" :style="{ '--br': '1px', '--p': '0 20px' }">
<ul class="HTitleBox" style="border: none">
<li class="latex-toolbar-item">
<el-popover
v-model="latexPopoverVisible"
placement="bottom-start"
width="720"
trigger="manual"
popper-class="latex-toolbar-popover"
append-to-body
@hide="onLatexPopoverHide"
>
<latex-data-panel
ref="latexDataPanel"
:article-id="articleId"
:list="mathFormulasList"
layout="popover"
@added="onLatexAdded"
@updated="onLatexUpdated"
@close-popover="closeLatexPopover"
/>
<span
slot="reference"
class="latex-toolbar-trigger"
@click.stop="toggleLatexPopover"
>
<i class="el-icon-s-data latex-toolbar-icon"></i>
{{ $t('commonTable.mathFormula') }}
</span>
</el-popover>
</li>
<li
@click="addContent"
class="base-font-size base-bg-imp base-padding-all"
@@ -184,8 +213,8 @@
item.is_h1
? 'isTitleH1 pMainH1'
: item.is_h2
? 'isTitleH2'
: item.is_h3
? 'isTitleH2'
: item.is_h3
? 'isTitleH3'
: ''
"
@@ -202,10 +231,11 @@
<div :class="currentId == item.am_id ? 'glowing-border' : ''" style="position: relative">
<div
class="base-bg base-pos"
class="base-bg base-pos paragraph-select-overlay"
:style="{ '--p-r': '0px', '--p-t': '-40px' }"
v-if="currentId == item.am_id"
style="z-index: 100"
@click.stop="onParagraphSelectOverlayClick(item)"
></div>
<div
@@ -1035,9 +1065,12 @@ const toolbar = 'addImageButton ';
import { TableUtils } from '@/common/js/TableUtils';
import { debounce, throttle } from '@/common/js/debounce';
import { tableStyle, commonWordStyle } from '@/utils/tinymceStyles';
import LatexDataPanel from './LatexDataPanel.vue';
export default {
name: 'tinymce',
components: {},
components: {
LatexDataPanel
},
props: {
id: {
type: String
@@ -1098,6 +1131,12 @@ export default {
wordStyle: {
type: String,
default: ''
},
mathFormulasList: {
type: Array,
default() {
return [];
}
}
},
data() {
@@ -1216,7 +1255,10 @@ export default {
displayList: [],
currentTypeText: '',
tinymceId: this.id || 'vue-tinymce-' + +new Date()
tinymceId: this.id || 'vue-tinymce-' + +new Date(),
latexPopoverVisible: false,
_latexPopoverScrollHandler: null,
_latexPopoverOutsideHandler: null
};
},
@@ -1247,6 +1289,17 @@ export default {
}
},
deep: true // 启用深度监听
},
latexPopoverVisible(val) {
if (val) {
this.$nextTick(() => {
this.bindLatexPopoverScrollListener();
setTimeout(() => this.bindLatexPopoverOutsideClick(), 0);
});
} else {
this.unbindLatexPopoverScrollListener();
this.unbindLatexPopoverOutsideClick();
}
}
},
computed: {
@@ -1362,6 +1415,8 @@ export default {
// window.removeEventListener('resize', this.calcMarkers);
if (this.resizeObs) this.resizeObs.disconnect();
if (this.mutObs) this.mutObs.disconnect();
this.unbindLatexPopoverScrollListener();
this.unbindLatexPopoverOutsideClick();
// 页面销毁前清理定时器和事件监听器
},
@@ -2145,6 +2200,62 @@ export default {
return;
}
},
onLatexAdded(data) {
this.$emit('latexAdded', data);
},
onLatexUpdated(data) {
this.$emit('latexUpdated', data);
},
toggleLatexPopover() {
this.latexPopoverVisible = !this.latexPopoverVisible;
},
closeLatexPopover() {
this.latexPopoverVisible = false;
},
onLatexPopoverHide() {
const panel = this.$refs.latexDataPanel;
if (panel && typeof panel.dismissEditing === 'function') {
panel.dismissEditing();
}
},
bindLatexPopoverScrollListener() {
this.unbindLatexPopoverScrollListener();
this._latexPopoverScrollHandler = () => {
if (this.latexPopoverVisible) {
this.closeLatexPopover();
}
};
const scrollEl = this.$refs.scroll;
if (scrollEl) {
scrollEl.addEventListener('scroll', this._latexPopoverScrollHandler, { passive: true });
}
},
unbindLatexPopoverScrollListener() {
if (!this._latexPopoverScrollHandler) return;
const scrollEl = this.$refs.scroll;
if (scrollEl) {
scrollEl.removeEventListener('scroll', this._latexPopoverScrollHandler);
}
this._latexPopoverScrollHandler = null;
},
bindLatexPopoverOutsideClick() {
this.unbindLatexPopoverOutsideClick();
this._latexPopoverOutsideHandler = (e) => {
if (!this.latexPopoverVisible) return;
const target = e.target;
if (!target) return;
if (target.closest && target.closest('.latex-toolbar-trigger')) return;
if (target.closest && target.closest('.latex-toolbar-popover')) return;
if (target.closest && target.closest('.latex-toolbar-item')) return;
this.closeLatexPopover();
};
document.addEventListener('mousedown', this._latexPopoverOutsideHandler, true);
},
unbindLatexPopoverOutsideClick() {
if (!this._latexPopoverOutsideHandler) return;
document.removeEventListener('mousedown', this._latexPopoverOutsideHandler, true);
this._latexPopoverOutsideHandler = null;
},
cacheSelection() {
const selection = window.getSelection();
@@ -2913,6 +3024,12 @@ export default {
}
});
},
onParagraphSelectOverlayClick(item) {
if (this.isPreview) return;
if (item && item.am_id) {
this.$set(this, 'currentId', item.am_id);
}
},
async handleGeneralClick(event, item, index) {
// 1. 如果有原有的 span 点击逻辑,先执行(或根据条件判断)
if (typeof this.onProofSpanClick === 'function') {
@@ -4078,6 +4195,15 @@ export default {
font-size: 20px;
cursor: pointer;
}
.HTitleBox li.latex-toolbar-item {
padding: 2px 10px;
font-size: 12px;
color: #fff;
background-color: rgb(43, 129, 239);
border: none;
border-radius: 4px;
height: 26px;
}
.operateBox {
width: auto;
display: flex;
@@ -4542,6 +4668,52 @@ font-weight: bold !important;
opacity: 1;
}
}
.latex-toolbar-item {
display: inline-flex;
align-items: center;
list-style: none;
padding: 2px 10px;
margin-left: 10px;
border: none;
border-radius: 4px;
background-color: rgb(43, 129, 239);
height: 26px;
}
.latex-toolbar-trigger {
display: inline-flex;
align-items: center;
cursor: pointer;
font-size: 12px;
font-weight: bold;
color: #fff;
line-height: 1.4;
white-space: nowrap;
}
.latex-toolbar-trigger:hover {
opacity: 0.9;
color: #fff;
}
.latex-toolbar-icon {
margin-right: 4px;
font-size: 13px;
color: #fff;
}
</style>
<style>
.latex-toolbar-popover {
padding: 10px 12px !important;
box-sizing: border-box;
min-width: 520px;
height: 400px;
max-height: 400px;
overflow: hidden;
border-radius: 8px !important;
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.12) !important;
}
</style>
<style scoped lang="less">
.glowing-border {
@@ -4555,6 +4727,18 @@ font-weight: bold !important;
.pMain {
cursor: pointer;
}
.paragraph-select-overlay {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
cursor: pointer;
background: transparent;
}
::v-deep wmath[data-wrap='inline'] {
display: inline-block !important;
width: auto !important;

View File

@@ -473,7 +473,8 @@
<script>
import DynamicTable from './DynamicTable.vue';
import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑
import { mediaUrl } from '@/common/js/commonJS.js';
import { isMathFormulaTableTitle, normalizeLatexTableApiResponse } from '@/utils/mathFormulaModule';
export default {
props: ['articleId', 'imgWidth', 'imgHeight', 'scale', 'isEdit', 'isShowEdit', 'urlList', 'content'],
@@ -498,8 +499,10 @@ export default {
],
images: [],
tables: [],
mathFormulas: [],
imagesList: [],
tablesList: [],
mathFormulasList: [],
tablesHtml: [],
imagesHtml: [],
activeNames: ['images', 'tables']
@@ -620,6 +623,12 @@ export default {
addTable() {
this.$emit('addTable');
},
onMathFormulaAdded(data) {
this.refresh('addTable', normalizeLatexTableApiResponse(data));
},
onMathFormulaUpdated(data) {
this.refresh('editTable', normalizeLatexTableApiResponse(data));
},
handlePaperclip() {
this.$emit('handlePaperclip');
},
@@ -638,11 +647,7 @@ export default {
handleSelectMenu(v) {
this.currentMenu = v;
this.currentSelectType = '0';
// if (v == 1) {
// this.getCommentList();
// } else {
this.filterData();
// }
},
selectType(v) {
this.currentSelectType = v.type;
@@ -663,14 +668,37 @@ export default {
}
this.filterData('table');
} else if (type == 'math') {
const indexInList = this.mathFormulas.findIndex((item) => item.amt_id == id);
if (indexInList !== -1) {
this.mathFormulas.splice(indexInList, 1);
}
this.filterData('math');
}
},
isMathFormulaTable(item) {
return item && isMathFormulaTableTitle(item.title);
},
splitTablesAndMathFormulas(list) {
const tables = [];
const mathFormulas = [];
(list || []).forEach((item) => {
if (this.isMathFormulaTable(item)) {
mathFormulas.push(item);
} else {
tables.push(item);
}
});
return { tables, mathFormulas };
},
filterData(type) {
if (type) {
if (type == 'img') {
this.imagesList = [...this.images];
} else if (type == 'table') {
this.tablesList = [...this.tables];
} else if (type == 'math') {
this.mathFormulasList = [...this.mathFormulas];
}
} else {
if (this.currentMenu == 1) {
@@ -689,7 +717,6 @@ export default {
switch (this.currentSelectType) {
case '0':
this.tablesList = [...this.tables];
// console.log('this.tablesList at line 393:', this.tablesList);
break;
case '1':
this.tablesList = [...this.tables].filter((e) => e.has_selected == 1);
@@ -701,6 +728,13 @@ export default {
} else {
}
}
this.mathFormulasList = [...this.mathFormulas];
this.emitMathFormulasChange();
},
emitMathFormulasChange() {
this.$emit('mathFormulasChange', {
list: [...this.mathFormulasList]
});
},
goToListComment(id, type) {
this.$emit('goToListComment', id, type);
@@ -717,15 +751,22 @@ export default {
})
},
async refresh(type, newData) {
const normalized = normalizeLatexTableApiResponse(newData || {});
var tableIndex;
var mathIndex;
var imgIndex;
var tableData
if(newData.amt_id ) {
tableIndex = this.tables.findIndex((table) => table.amt_id == newData.amt_id);
tableData = newData.table_data?JSON.parse(newData.table_data):[];
var tableData = normalized.table || [];
if (normalized.amt_id) {
mathIndex = this.mathFormulas.findIndex((item) => {
const itemId = item.amt_id || item.ant_id || item.article_table_id;
return itemId != null && String(itemId) === String(normalized.amt_id);
});
if (mathIndex === -1) {
tableIndex = this.tables.findIndex((table) => table.amt_id == normalized.amt_id);
}
}
newData = normalized;
if(newData.ami_id) {
imgIndex = this.images.findIndex((img) => img.ami_id == newData.ami_id);
}
@@ -760,13 +801,38 @@ if(newData.ami_id) {
this.tables.push({
...newData,
article_table_id: newData.amt_id,
table: tableData
});
if (this.isMathFormulaTable(newData)) {
const mathIdx = this.mathFormulas.findIndex(
(item) => item.amt_id == newData.amt_id
);
const payload = {
...newData,
article_table_id: newData.amt_id,
table: tableData
};
if (mathIdx !== -1) {
this.mathFormulas[mathIdx] = payload;
} else if (this.mathFormulas.length > 0) {
this.mathFormulas[0] = payload;
} else {
this.mathFormulas.push(payload);
}
} else {
this.tables.push({
...newData,
article_table_id: newData.amt_id,
table: tableData
});
}
break;
case 'editTable':
if (tableIndex !== -1) {
if (mathIndex !== -1) {
this.mathFormulas[mathIndex] = {
...newData,
article_table_id: newData.amt_id,
table: tableData
};
} else if (tableIndex !== -1) {
this.tables[tableIndex] = {
...newData,
@@ -793,14 +859,18 @@ if(newData.ami_id) {
break;
case 'removeTable':
if (tableIndex !== -1) {
if (mathIndex !== -1) {
this.mathFormulas[mathIndex].has_selected = 0;
} else if (tableIndex !== -1) {
this.tables[tableIndex].has_selected = 0;
}
break;
case 'positioningTable':
if (tableIndex !== -1) {
if (mathIndex !== -1) {
this.mathFormulas[mathIndex].has_selected = 1;
} else if (tableIndex !== -1) {
this.tables[tableIndex].has_selected = 1;
}
@@ -900,8 +970,9 @@ if(newData.ami_id) {
article_id: this.articleId
})
.then(async (res) => {
let parsedList = [];
if (this.urlList) {
this.tables =
parsedList =
res.data.list && res.data.list.length > 0
? res.data.list.map((e) => {
return {
@@ -912,7 +983,7 @@ if(newData.ami_id) {
})
: [];
} else {
this.tables =
parsedList =
res.data.list && res.data.list.length > 0
? res.data.list.map((e) => {
return {
@@ -923,7 +994,11 @@ if(newData.ami_id) {
})
: [];
}
const split = this.splitTablesAndMathFormulas(parsedList);
this.tables = split.tables;
this.mathFormulas = split.mathFormulas;
await this.filterData('table');
await this.filterData('math');
});
}