Compare commits

3 Commits

21 changed files with 1978 additions and 1107 deletions

View File

@@ -9,7 +9,13 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
/* 禁止选中特定段落或组件 */
.no-select {
user-select: none; /* 标准语法 */
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
html, html,
body, body,
#app, #app,

View File

@@ -2055,22 +2055,31 @@ export default {
onAction: () => { onAction: () => {
// 插入自定义表格到编辑器中 // 插入自定义表格到编辑器中
ed.setContent(''); ed.setContent('');
const customCallback = ed.getParam('clear_custom_action');
if (typeof customCallback === 'function') {
customCallback(ed, vueInstance);
} else {
// 3. 如果没有自定义逻辑,执行默认逻辑
vueInstance.$emit('onClear');
}
} }
}); });
ed.ui.registry.addButton('customBlue', { ed.ui.registry.addButton('customBlue', {
text: 'Blue', // 按钮文本 text: 'Blue',
className: 'custom-button-blue', // 添加自定义类 className: 'custom-button-blue',
// shortcut: "Ctrl+J",
onAction: function () { onAction: function () {
// 在选中的文本周围包裹 <blue> 标签 // 必须获取带 HTML 的内容,否则里面的 em/i 标签在拼接前就丢了
var selectedText = ed.selection.getContent(); var selectedText = ed.selection.getContent({ format: 'html' });
if (selectedText) { if (selectedText) {
// 这就是你想要的:直接外层套一个 blue
var wrappedText = `<blue>${selectedText}</blue>`; var wrappedText = `<blue>${selectedText}</blue>`;
// 使用 setContent 强行回写
ed.selection.setContent(wrappedText); ed.selection.setContent(wrappedText);
} else {
this.$message.error('请选择要添加蓝色的文本');
} }
} }
}); });

View File

@@ -18,7 +18,7 @@
</div> </div>
<!-- {{ $t('system.title') }} --> <!-- {{ $t('system.title') }} -->
</div> </div>
<div class="header-right"> <div class="header-right no-select">
<div class="header-user-con"> <div class="header-user-con">
<div class="changelang"> <div class="changelang">
<el-dropdown trigger="click" @command="chengelang"> <el-dropdown trigger="click" @command="chengelang">

View File

@@ -7,7 +7,7 @@
<img v-if="isProofreading" src="../../assets/img/Online Proofreading.png" alt="" height="24px" style="margin-left: 10px;margin-top: 4px;"/> <img v-if="isProofreading" src="../../assets/img/Online Proofreading.png" alt="" height="24px" style="margin-left: 10px;margin-top: 4px;"/>
</div> </div>
<div class="header-right"> <div class="header-right no-select">
<div class="header-user-con"> <div class="header-user-con">
<div class="changelang"> <div class="changelang">
<el-dropdown trigger="click" @command="chengelang"> <el-dropdown trigger="click" @command="chengelang">

View File

@@ -395,16 +395,19 @@ const en = {
typesettingType2: 'Horizontal A4', typesettingType2: 'Horizontal A4',
typesettingType1: 'Vertical A4', typesettingType1: 'Vertical A4',
AnnotationList: 'Annotation List', AnnotationList: 'Annotation List',
Annotations: 'Annotations', Annotations: 'Comments',
exportWord: 'Export Word', exportWord: 'Export Word',
exportImg: 'Export PNG', exportImg: 'Export PNG',
PaperRotation: 'Paper Rotation', PaperRotation: 'Paper Rotation',
removeAnnotations: 'Are you sure you want to delete this Annotation?', removeAnnotations: 'Are you sure you want to delete this Annotation?',
removeProofread: 'Are you sure to delete this suggestion?', removeProofread: 'Are you sure to delete this suggestion?',
removeContent: 'Are you sure you want to delete this content?', removeContent: 'Are you sure you want to delete this content?',
removeimg: 'Are you sure you want to delete this figure?',
removetable: 'Are you sure you want to delete this table?',
reContent: 'Are you sure you want to restore this content?', reContent: 'Are you sure you want to restore this content?',
uploadImageInfo: 'Figures can only upload files in JPG, JPEG, and PNG formats!', uploadImageInfo: 'Figures can only upload files in JPG, JPEG, and PNG formats!',
selectComment: 'Please select the text to add annotations to!', selectComment: 'Please select the text to add annotations to!',
selectLinkText: 'Please select the target text content before associating it with a figure or table.',
selectWord: 'Please select only a single word', selectWord: 'Please select only a single word',
selectOne: 'Please select only a single paragraph', selectOne: 'Please select only a single paragraph',
alreadyCommented: 'There are already annotations in the text, please select again!', alreadyCommented: 'There are already annotations in the text, please select again!',
@@ -422,6 +425,7 @@ const en = {
MoveDown: 'Move Down', MoveDown: 'Move Down',
jump: 'Locate', jump: 'Locate',
editAssociation: 'Edit Association', editAssociation: 'Edit Association',
UnbindAssociation: 'Unbind Association',
}, },
pendingPayment: { pendingPayment: {
title: 'Title', title: 'Title',
@@ -543,7 +547,24 @@ const en = {
state194: 'Proportion of references from JCR Q2', state194: 'Proportion of references from JCR Q2',
state21: 'Probability of article being cited', state21: 'Probability of article being cited',
} },
imageTask: {
"title": "Image Task Center",
"completed": "All Tasks Completed",
"preparing": "Preparing {total} images...",
"progress": "Progress: {current} / {total}",
"allDone": "Successfully processed {total} images",
"manualClose": "Tasks completed. Please close manually.",
"parsing": "Parsing...",
"uploading": "Uploading...",
"success": "Success",
"tooLarge": "Too Large (>1MB)",
"error": "Error: {msg}",
"imgLabel": "Img"
}
} }

View File

@@ -389,9 +389,12 @@ const zh = {
removeAnnotations: '确定要删除这条批注吗?', removeAnnotations: '确定要删除这条批注吗?',
removeProofread: '确定要删除这条建议吗?', removeProofread: '确定要删除这条建议吗?',
removeContent: '确定要删除这条内容吗?', removeContent: '确定要删除这条内容吗?',
removeimg: '确定要删除这张图片吗?',
removetable: '确定要删除这个表格吗?',
reContent: '确定要恢复这条内容吗?', reContent: '确定要恢复这条内容吗?',
uploadImageInfo: 'Figures 只能上传 JPG、JPEG 和 PNG 格式的文件', uploadImageInfo: 'Figures 只能上传 JPG、JPEG 和 PNG 格式的文件',
selectComment: '请选择要添加批注的文本', selectComment: '请选择要添加批注的文本',
selectLinkText: '执行图表关联前,请先选定目标文本内容',
selectWord:'请只选中单个单词!', selectWord:'请只选中单个单词!',
selectOne:'请只勾选单个段落!', selectOne:'请只勾选单个段落!',
alreadyCommented:'文本中已有批注内容请重新选择', alreadyCommented:'文本中已有批注内容请重新选择',
@@ -406,8 +409,9 @@ const zh = {
BatchAddcontent: '批量添加内容', BatchAddcontent: '批量添加内容',
MoveUp: '上移', MoveUp: '上移',
MoveDown: '下移', MoveDown: '下移',
jump: 'Locate', jump: '定位',
editAssociation: 'Edit Association', editAssociation: '编辑关联',
UnbindAssociation: '取消关联',
}, },
pendingPayment: { pendingPayment: {
title: 'Title', title: 'Title',
@@ -525,7 +529,22 @@ const zh = {
state193: '参考文献JCR1区比例', state193: '参考文献JCR1区比例',
state194: '参考文献JCR2区比例', state194: '参考文献JCR2区比例',
state21: '文章被引用概率', state21: '文章被引用概率',
},
imageTask: {
"title": "图片任务中心",
"completed": "所有任务处理完毕",
"preparing": "正在准备 {total} 张图片...",
"progress": "处理进度: {current} / {total}",
"allDone": "已成功处理全部 {total} 张任务",
"manualClose": "任务已结束,请手动关闭窗口",
"parsing": "正在解析...",
"uploading": "正在上传...",
"success": "成功",
"tooLarge": "过大跳过(>1MB)",
"error": "失败: {msg}",
"imgLabel": "图"
} }
} }

View File

@@ -16,7 +16,7 @@
<div <div
class="right-side" class="right-side"
style=" style="
width: 285px; width: 260px;
float: left; float: left;
height: 100%; height: 100%;
background-color: #fff; background-color: #fff;
@@ -67,28 +67,30 @@
@handlePaperclip="handlePaperclip" @handlePaperclip="handlePaperclip"
@addComment="addCommentSetting" @addComment="addCommentSetting"
@goToComment="goToComment" @goToComment="goToComment"
@edit="handleImageEdit" @edit="handleFigureAndTableEdit"
@delete="handleFigureAndTableDelete"
@goToListComment="goToListComment" @goToListComment="goToListComment"
style="width: 100%; height: 100%; padding: 0 0px; box-sizing: border-box; background-color: #fff" style="width: 100%; height: 100%; padding: 0 0px; box-sizing: border-box; background-color: #fff"
> >
<template slot="catalogue1">
<catalogue
v-if="Main_List.length > 0"
:content="Main_List"
:articleId="articleId"
ref="catalogue"
@goToListComment="goToListComment"
style="width: 100%; height: 100%; padding: 0 0px; box-sizing: border-box; background-color: #fff"
>
</catalogue>
</template>
</common-word-html-type-setting> </common-word-html-type-setting>
<input type="file" ref="fileInput" style="display: none" @change="handleFileChange" /> <input type="file" ref="fileInput" style="display: none" @change="handleFileChange" />
</div> </div>
</div> </div>
<div style="width: 100%; width: calc(100% - 285px); float: right; height: calc(100% - 0px); background-color: #e4e9ed"> <div style="width: 100%; width: calc(100% - 260px); float: right; height: calc(100% - 0px); background-color: #e4e9ed">
<!-- <div class="toolbar">
<div class="toolbar_item" @click="handleImageAdd('img')">
<img src="@/assets/img/upload.png" style="object-fit: contain" />
<span>Add Figure </span>
</div>
<div class="toolbar_item" @click="handleTableAdd('table')">
<img src="@/assets/img/uploadTable.png" style="object-fit: contain" />
<span>Add Table </span>
</div>
</div> -->
<common-word <common-word
:articleId="articleId" :articleId="articleId"
v-if="htmlContent" v-if="htmlContent"
ref="commonWord" ref="commonWord"
:value="htmlContent" :value="htmlContent"
@@ -96,13 +98,13 @@
:comments="comments" :comments="comments"
:wordStyle="wordStyle" :wordStyle="wordStyle"
@onDrop="onDrop" @onDrop="onDrop"
@onLinkUnbind="handleUnbindLink"
@onLinkConfirm="handleConfirmLink" @onLinkConfirm="handleConfirmLink"
@saveContent="saveContent" @saveContent="saveContent"
@editComment="editComment" @editComment="editComment"
@loaded="loadedWord" @loaded="loadedWord"
@onEdit="onEdit" @onEdit="onEdit"
@addContent="onAddContent" @addContent="onAddContent"
@changeSort="changeSort" @changeSort="changeSort"
@onDelete="onDelete" @onDelete="onDelete"
@onDeletes="onDeletes" @onDeletes="onDeletes"
@@ -234,7 +236,7 @@
<font style="color: #f56c6c; margin-right: 5px">*</font> <font style="color: #f56c6c; margin-right: 5px">*</font>
Table: Table:
</span> </span>
<common-table <common-table
:articleId="articleId" :articleId="articleId"
@getContent="getContent" @getContent="getContent"
@@ -415,7 +417,7 @@
</el-button> </el-button>
</span> </span>
</el-dialog> </el-dialog>
<common-late-x v-if="showLateX" @close="showLateX = false" @save="saveLateX" :LateXInfo="LateXInfo"></common-late-x> <common-late-x v-if="showLateX" @close="showLateX = false" @save="saveLateX" :LateXInfo="LateXInfo"></common-late-x>
</div> </div>
</template> </template>
@@ -427,6 +429,7 @@ import Tiff from 'tiff.js';
import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑 import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑
import Tinymce from '@/components/page/components/Tinymce'; import Tinymce from '@/components/page/components/Tinymce';
import bottomTinymce from '@/components/page/components/Tinymce'; import bottomTinymce from '@/components/page/components/Tinymce';
import catalogue from '@/components/page/components/table/catalogue.vue';
export default { export default {
data() { data() {
return { return {
@@ -465,9 +468,11 @@ export default {
setPositioningImage: 'api/Preaccept/positioningImage', setPositioningImage: 'api/Preaccept/positioningImage',
removePositioningImage: 'api/Preaccept/removeImage', removePositioningImage: 'api/Preaccept/removeImage',
addImage: 'api/Preaccept/addMainImage', addImage: 'api/Preaccept/addMainImage',
editImage: 'api/Preaccept/editMainImage',
addTable: 'api/Preaccept/addMainTable', addTable: 'api/Preaccept/addMainTable',
editTable: 'api/Preaccept/editMainTable' editImage: 'api/Preaccept/editMainImage',
editTable: 'api/Preaccept/editMainTable',
deleteImage: 'api/Articlemain/removeMainImage',
deleteTable: 'api/Articlemain/removeMainTable'
}, },
wordStyle: ` wordStyle: `
// p { // p {
@@ -552,7 +557,8 @@ export default {
}, },
components: { components: {
Tinymce, Tinymce,
bottomTinymce bottomTinymce,
catalogue
}, },
computed: { computed: {
combinedValue() { combinedValue() {
@@ -582,20 +588,16 @@ export default {
} }
}, },
async created() { async created() {
// await this.$api.post('api/Proofread/proofRead', {
// article_id: this.$route.query.id
// });
localStorage.removeItem('scrollPosition'); localStorage.removeItem('scrollPosition');
this.isShowEditComment(); this.isShowEditComment();
this.getDate(); this.getDate();
this.getCommentList(); this.getCommentList();
// this.loadDictionary().catch(console.error);
}, },
mounted() {}, mounted() {},
async activated() { async activated() {
// await this.$api.post('api/Proofread/proofRead', {
// article_id: this.$route.query.id
// });
this.isShowEditComment(); this.isShowEditComment();
this.getDate(); this.getDate();
this.getCommentList(); this.getCommentList();
@@ -705,38 +707,55 @@ export default {
this.addComment(content); this.addComment(content);
} }
}, },
handleUnbindLink(data) {
handleConfirmLink(selectedMedia) { const { label, mainId, index, content } = data;
// selectedMedia 是你从 el-radio 列表中选中的那一项(包含 ami_id 或 amt_id
const targetId = selectedMedia.select.amt_id || selectedMedia.select.ami_id;
const type = selectedMedia.type; // 'table' 或 'figure'
const tagName = `my${type}`; // 生成 mytable 或 myfigure
// 获取当前段落原本的完整 HTML 内容
let originalContent = selectedMedia.linkData.content;
// 获取用户选中的纯文本片段
const label = selectedMedia.linkData.label;
if (!label || !originalContent) return; const unwrapTag = (str) => {
return str.replace(/<(myfigure|mytable)[^>]*>([\s\S]*?)<\/\1>/gi, '$2');
};
// --- 核心替换逻辑 --- // 2. 执行替换
// 使用正则匹配选中的文字,并用自定义标签包裹它 // 我们只针对传入的这个特定的 label 进行剥壳
// 注意:这里需要考虑 label 在 HTML 中可能被拆分的情况,简单处理可直接 replace const strippedLabel = unwrapTag(label);
const replacement = `<${tagName} data-id="${targetId}">${label}</${tagName}>`;
// 执行替换(仅替换第一次匹配到的,防止全篇误伤)
const newContent = originalContent.replace(label, replacement);
// 3. 将 content 中的原标签替换为剥壳后的文字
console.log('newContent at line 592:', newContent); // 使用 split/join 或者是精准 replace确保只替换这一处
this.saveContent(newContent, selectedMedia.linkData.mainId); const newContent = content.replace(label, strippedLabel);
this.saveContent(newContent, mainId);
},
handleConfirmLink(selectedMedia) {
const targetId = selectedMedia.select.amt_id || selectedMedia.select.ami_id;
const type = selectedMedia.type;
const tagName = `my${type}`;
let originalContent = selectedMedia.linkData.content;
const label = selectedMedia.linkData.label;
if (!label || !originalContent) return;
const isAlreadyTagged = label.startsWith('<my') && label.endsWith('>');
let newContent = '';
if (isAlreadyTagged) {
const updatedTag = label
.replace(/<my(figure|table)/, `<${tagName}`)
.replace(/data-id=".*?"/, `data-id="${targetId}"`)
.replace(/<\/my(figure|table)>$/, `</${tagName}>`);
newContent = originalContent.replace(label, updatedTag);
} else {
const replacement = `<${tagName} data-id="${targetId}">${label}</${tagName}>`;
newContent = originalContent.replace(label, replacement);
}
this.saveContent(newContent, selectedMedia.linkData.mainId);
}, },
async saveContent(content, am_id) { async saveContent(content, am_id) {
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
var that = this; var that = this;
var str = content.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1').trim(); var str = content.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1').trim();
str = await that.$commonJS.decodeHtml(str); str = await that.$commonJS.decodeHtml(str);
await that.$api await that.$api
@@ -746,10 +765,18 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
}) })
.then(async (res) => { .then(async (res) => {
if (res.code == 0) { if (res.code == 0) {
loading.close();
this.editVisible = false; this.editVisible = false;
this.getDate(); this.refreshCurrentContent('content', am_id, res.data);
this.getCommentList(); this.getCommentList();
} else {
loading.close();
this.$message.error(res.msg);
} }
})
.catch((err) => {
loading.close();
this.$message.error(err);
}); });
}, },
async saveContentList(content, am_id) { async saveContentList(content, am_id) {
@@ -759,7 +786,12 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
message: 'Please enter the content!' message: 'Please enter the content!'
}); });
} }
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
await this.$api await this.$api
.post('api/Preaccept/addMoreRow', { .post('api/Preaccept/addMoreRow', {
article_id: this.articleId, article_id: this.articleId,
@@ -768,10 +800,18 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
}) })
.then(async (res) => { .then(async (res) => {
if (res.code == 0) { if (res.code == 0) {
loading.close();
this.addContentVisible = false; this.addContentVisible = false;
this.getDate(); this.refreshCurrentContent('addMoreRow', am_id, res.data);
this.getCommentList(); this.getCommentList();
} else {
loading.close();
this.$message.error(res.msg);
} }
})
.catch((err) => {
loading.close();
this.$message.error(err);
}); });
}, },
isHeaderRow(rowIndex, table) { isHeaderRow(rowIndex, table) {
@@ -877,8 +917,10 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
var am_id; var am_id;
if (type == 'img') { if (type == 'img') {
am_id = this.Main_List.find((item) => item.ami_id == id).am_id; am_id = this.Main_List.find((item) => item.ami_id == id).am_id;
} else { } else if (type == 'table') {
am_id = this.Main_List.find((item) => item.amt_id == id).am_id; am_id = this.Main_List.find((item) => item.amt_id == id).am_id;
} else {
am_id = id;
} }
if (am_id) { if (am_id) {
this.goToComment(am_id); this.goToComment(am_id);
@@ -903,8 +945,10 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
} }
}, },
async onDelete(dataId) { async onDelete(dataId) {
var that = this;
var dataInfo = this.Main_List.find((item) => item.am_id == dataId); var dataInfo = this.Main_List.find((item) => item.am_id == dataId);
var dataIndex = this.Main_List.indexOf(dataInfo);
var type = ''; var type = '';
if (dataInfo.type == 1) { if (dataInfo.type == 1) {
type = 'img'; type = 'img';
@@ -913,53 +957,70 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
} else if (dataInfo.type == 0) { } else if (dataInfo.type == 0) {
type = 'content'; type = 'content';
} }
var url = '';
switch (type) {
case 'table':
url = that.urlList.removePositioningTable;
break;
case 'img':
url = that.urlList.removePositioningImage;
break;
case 'content':
url = that.urlList.delete;
break;
}
if (dataInfo.type == 0 && dataInfo.content == '') {
await that.$api
.post(url, {
am_id: dataId
})
.then(async (res) => {
if (res.code == 0) {
setTimeout(() => {
that.Main_List.splice(dataIndex, 1);
await this.$confirm(this.$t('commonTable.removeContent'), 'Prompt', { that.getCommentList();
confirmButtonText: 'Submit', that.$forceUpdate();
cancelButtonText: 'Cancel', });
type: 'warning' } else {
}) this.$message.error(res.msg);
.then(async () => { }
var that = this; })
var url = ''; .catch((err) => {
switch (type) { this.$message.error(err);
case 'table': });
url = that.urlList.removePositioningTable; } else {
break; await this.$confirm(this.$t('commonTable.removeContent'), 'Prompt', {
case 'img': confirmButtonText: 'Submit',
url = that.urlList.removePositioningImage; cancelButtonText: 'Cancel',
break; type: 'warning'
case 'content':
url = that.urlList.delete;
break;
}
await that.$api
.post(url, {
am_id: dataId
})
.then(async (res) => {
if (res.code == 0) {
setTimeout(() => {
that.getDate();
that.getCommentList();
if (type == 'img') {
that.$refs.commonWordHtmlTypeSetting.refresh('img');
} else {
that.$refs.commonWordHtmlTypeSetting.refresh('table');
}
that.$forceUpdate();
});
}
});
// this.Main_List.splice(
// this.Main_List.findIndex((item) => item.p_main_id == dataId),
// 1
// );
}) })
.catch((err) => { .then(async () => {
console.log('err at line 466:', err); await that.$api
}); .post(url, {
am_id: dataId
})
.then(async (res) => {
if (res.code == 0) {
setTimeout(() => {
that.Main_List.splice(dataIndex, 1);
that.$forceUpdate();
if (type == 'img') {
that.$refs.commonWordHtmlTypeSetting.refresh('removeImg', { ami_id: dataInfo.ami_id });
} else if (type == 'table') {
that.$refs.commonWordHtmlTypeSetting.refresh('removeTable', { amt_id: dataInfo.amt_id });
}
that.getCommentList();
that.$forceUpdate();
});
}
});
})
.catch((err) => {
console.log('err at line 466:', err);
});
}
}, },
async onDeletes(dataId) { async onDeletes(dataId) {
await this.$confirm(this.$t('commonTable.removeContent'), 'Prompt', { await this.$confirm(this.$t('commonTable.removeContent'), 'Prompt', {
@@ -978,8 +1039,8 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
setTimeout(() => { setTimeout(() => {
that.getDate(); that.getDate();
that.getCommentList(); that.getCommentList();
that.$refs.commonWordHtmlTypeSetting.refresh('img'); that.$refs.commonWordHtmlTypeSetting.reload();
that.$refs.commonWordHtmlTypeSetting.refresh('table');
that.$forceUpdate(); that.$forceUpdate();
}); });
} }
@@ -994,20 +1055,40 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
}); });
}, },
async changeSort(type, id) { async changeSort(type, id) {
var that = this; var that = this;
const index = this.Main_List.findIndex((item) => item.am_id == id);
if(type=='up'&&index==0){
return
}
if(type=='down'&&index==this.Main_List.length-1){
return
}
const load = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
await that.$api await that.$api
.post(type == 'up' ? '/api/Preaccept/upArticleMain' : '/api/Preaccept/downArticleMain', { .post(type == 'up' ? '/api/Preaccept/upArticleMain' : '/api/Preaccept/downArticleMain', {
am_id: id am_id: id
}) })
.then(async (res) => { .then(async (res) => {
if (res.code == 0) { if (res.code == 0) {
setTimeout(() => { load.close();
that.getDate(); await this.refreshCurrentContent(`${type}ArticleMain`, id);
that.getCommentList(); that.$forceUpdate();
} else {
that.$forceUpdate(); load.close();
}); this.$message.error(res.msg);
} }
})
.catch((err) => {
load.close();
this.$message.error(err);
}); });
}, },
async addCommentSetting(content) { async addCommentSetting(content) {
@@ -1103,7 +1184,7 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
if (data && data.remark) { if (data && data.remark) {
this.$alert( this.$alert(
`<p style="display:flex;"><img src="${this.remarkImageUrl}" alt="" style="width:20px;height:20px;margin-right:10px;"/><span style=" overflow-wrap: break-word;width:calc(100% - 50px)"> ${data.remark}</span></p>`, `<p style="display:flex;"><img src="${this.remarkImageUrl}" alt="" style="width:20px;height:20px;margin-right:10px;"/><span style=" overflow-wrap: break-word;width:calc(100% - 50px)"> ${data.remark}</span></p>`,
'Annotations', 'Comments',
{ {
confirmButtonText: 'OK', confirmButtonText: 'OK',
dangerouslyUseHTMLString: true, // 启用 HTML 渲染 dangerouslyUseHTMLString: true, // 启用 HTML 渲染
@@ -1146,6 +1227,12 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
this.commentVisible = true; this.commentVisible = true;
}, },
async onEditTitle(data) { async onEditTitle(data) {
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
var url; var url;
switch (data.value) { switch (data.value) {
case 0: case 0:
@@ -1168,13 +1255,64 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
am_id: data.mainId am_id: data.mainId
}) })
.then(async (res) => { .then(async (res) => {
this.getDate(); if (res.code == 0) {
this.getCommentList(); await this.refreshCurrentContent('content', data.mainId, res.data);
loading.close();
this.getCommentList();
}
}) })
.catch((err) => { .catch((err) => {
loading.close();
this.$message.error(err.msg); this.$message.error(err.msg);
}); });
}, },
async refreshCurrentContent(type, mainId, resData) {
const index = this.Main_List.findIndex((item) => item.am_id == mainId);
if (type == 'upArticleMain') {
if (index == 0) {
return;
}
const item = this.Main_List[index];
// 2. 从原位置删除
this.Main_List.splice(index, 1);
// 3. 插入到前一个位置
this.Main_List.splice(index - 1, 0, item);
return;
}
if (type == 'downArticleMain') {
if (index == this.Main_List.length - 1) {
return;
}
const item = this.Main_List[index];
// 2. 从原位置删除
this.Main_List.splice(index, 1);
// 3. 插入到前一个位置
this.Main_List.splice(index + 1, 0, item);
return;
}
if (index !== -1 && resData) {
var newData = Array.isArray(resData) ? resData : (resData ? [resData] : []);
const updatedItems= newData.map((item) => ({
...item,
checked: false,
getnum: 0,
}));
if (type == 'addRow') {
this.Main_List.splice(index + 1, 0, updatedItems[0]);
} else if (type == 'addMoreRow') {
this.Main_List.splice(index + 1, 0, ...updatedItems);
}else if(type=='positioningImg'||type=='positioningTable'){
this.Main_List.splice(index + 1, 0, ...updatedItems);
}
else {
this.$set(this.Main_List, index, updatedItems[0]);
}
} else {
console.warn(`Item with am_id ${mainId} not found.`);
}
this.$forceUpdate();
},
handlePaperclip() { handlePaperclip() {
this.uploadWordTables = []; this.uploadWordTables = [];
this.$refs.fileInput.click(); this.$refs.fileInput.click();
@@ -1231,21 +1369,18 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
}, },
async onAddRow(mainId) { async onAddRow(mainId) {
await this.$api await this.$api
.post(this.urlList.addRow, { .post(this.urlList.addRow, {
am_id: mainId, am_id: mainId,
article_id: this.articleId article_id: this.articleId
}) })
.then(async (res) => { .then(async (res) => {
if(res.code == 0){ if (res.code == 0) {
this.getDate(); this.refreshCurrentContent('addRow', mainId, res.data);
this.getCommentList();
this.getCommentList(); } else {
}else{
this.$message.error(res.msg); this.$message.error(res.msg);
} }
}) })
.catch((err) => { .catch((err) => {
this.$message.error(err.msg); this.$message.error(err.msg);
@@ -1270,8 +1405,6 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
}, },
async executeProofreading(data) { async executeProofreading(data) {
console.log('data at line 1225:', data);
await this.$api await this.$api
.post(this.urlList.executeProofreading, { .post(this.urlList.executeProofreading, {
am_id: data.am_id, am_id: data.am_id,
@@ -1292,8 +1425,6 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
}); });
}, },
async revokeProofreading(data) { async revokeProofreading(data) {
console.log('data at line 1225:', data);
await this.$api await this.$api
.post(this.urlList.executeProofreading, { .post(this.urlList.executeProofreading, {
am_id: data.am_id, am_id: data.am_id,
@@ -1313,13 +1444,8 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
this.$message.error(err.msg); this.$message.error(err.msg);
}); });
}, },
async editProofreading(data) { async editProofreading(data) {},
console.log('data at line 1225:', data); deleteProofreading(data, index) {},
},
deleteProofreading(data, index) {
console.log('comment at line 480:', data);
},
async cancelSolveComment(data) { async cancelSolveComment(data) {
await this.$api await this.$api
@@ -1363,7 +1489,69 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
this.lineStyle.visiTitle = 'Add Table'; this.lineStyle.visiTitle = 'Add Table';
this.threeVisible = true; this.threeVisible = true;
}, },
handleImageEdit(data, type) {
async handleFigureAndTableDelete(data, type) {
await this.$confirm(this.$t('commonTable.remove' + type), 'Prompt', {
confirmButtonText: 'Submit',
cancelButtonText: 'Cancel',
type: 'warning'
})
.then(async (res) => {
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
if (type == 'img') {
this.$api
.post(this.urlList.deleteImage, {
ami_id: data.ami_id,
article_id: this.articleId
})
.then(async (res) => {
if (res.status == 1) {
loading.close();
this.$message.success(res.msg);
this.$refs.commonWordHtmlTypeSetting.replacement('img', data.ami_id);
} else {
loading.close();
this.$message.error(res.msg);
}
})
.catch((err) => {
loading.close();
this.$message.error(err.msg);
});
}
if (type == 'table') {
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('table', 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);
});
},
handleFigureAndTableEdit(data, type) {
if (type == 'img') { if (type == 'img') {
var extension = data.url.split('.').pop().toLowerCase(); var extension = data.url.split('.').pop().toLowerCase();
this.picStyle = {}; this.picStyle = {};
@@ -1373,17 +1561,15 @@ this.saveContent(newContent, selectedMedia.linkData.mainId);
} else if (type == 'table') { } else if (type == 'table') {
this.lineStyle = {}; this.lineStyle = {};
this.lineStyle1 = {}; this.lineStyle1 = {};
// 1. 提取处理逻辑 // 1. 提取处理逻辑
const formattedData = { const formattedData = {
...data, ...data,
table: JSON.parse(data.table_data), table: JSON.parse(data.table_data)
// 如果 data 中已经包含了 html_data, note, title且不需要特殊处理 };
// 解构赋值 (...data) 其实已经把它们带进来了。
};
// 2. 统一赋值 // 2. 统一赋值
this.lineStyle = formattedData; this.lineStyle = formattedData;
this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指向不同引用(如果需要独立修改) this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指向不同引用(如果需要独立修改)
this.lineStyle.visiTitle = 'Edit Table'; this.lineStyle.visiTitle = 'Edit Table';
this.threeVisible = true; this.threeVisible = true;
} }
@@ -1448,36 +1634,40 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
this.currentId = dataId; this.currentId = dataId;
}, },
async onDrop(event, dataId) { async onDrop(event, dataId) {
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
if (event.dataTransfer.getData('image')) { if (event.dataTransfer.getData('image')) {
const draggedImage = JSON.parse(event.dataTransfer.getData('image')); const draggedImage = JSON.parse(event.dataTransfer.getData('image'));
const draggedImageIndex = JSON.parse(event.dataTransfer.getData('imageIndex')); const draggedImageIndex = JSON.parse(event.dataTransfer.getData('imageIndex'));
this.$nextTick(async () => { this.$nextTick(async () => {
await this.$api await this.$api
.post(this.urlList.setPositioningImage, { .post(this.urlList.setPositioningImage, {
am_id: dataId, am_id: dataId,
ami_id: draggedImage.ami_id ami_id: draggedImage.ami_id
}) })
.then(async (res) => { .then(async (res) => {
if (res.code == 0) { if (res.code == 0) {loading.close()
this.getDate(); this.refreshCurrentContent('positioningImg',dataId, res.data);
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.commonWordHtmlTypeSetting.refresh('img'); this.$refs.commonWordHtmlTypeSetting.refresh('positioningImg', res.data);
}); });
this.$forceUpdate(); this.$forceUpdate();
} else { } else {loading.close()
this.$message.error(res.msg); this.$message.error(res.msg);
} }
}) })
.catch((err) => { .catch((err) => {loading.close()
this.$message.error(err.msg); this.$message.error(err.msg);
}); });
}); });
// this.Main_List.splice(index + 1, 0, draggedImage);
// this.$nextTick(() => {
// this.$refs.commonWordHtmlTypeSetting.changeIsHidden(draggedImageIndex, true, 'img');
// });
} else { } else {
const draggedtable = JSON.parse(event.dataTransfer.getData('table')); const draggedtable = JSON.parse(event.dataTransfer.getData('table'));
@@ -1488,28 +1678,27 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
amt_id: draggedtable.amt_id amt_id: draggedtable.amt_id
}) })
.then(async (res) => { .then(async (res) => {
if (res.code == 0) { if (res.code == 0) {loading.close()
this.getDate(); this.refreshCurrentContent('positioningTable',dataId, res.data);
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.commonWordHtmlTypeSetting.refresh('table'); this.$refs.commonWordHtmlTypeSetting.refresh('positioningTable', res.data);
}); });
this.$forceUpdate(); this.$forceUpdate();
} else { } else {
loading.close()
this.$message.error(res.msg); this.$message.error(res.msg);
} }
}) })
.catch((err) => { .catch((err) => {
loading.close()
this.$message.error(err.msg); this.$message.error(err.msg);
}); });
}); });
// const draggedtableIndex = JSON.parse(event.dataTransfer.getData('tableIndex'));
// this.Main_List.splice(index + 1, 0, draggedtable);
// console.log('this.Main_List.splice at line 447:', this.Main_List);
} }
// this.getWord();
}, },
getCommentList() { getCommentList() {
this.$api this.$api
@@ -1522,13 +1711,10 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
}); });
}, },
getWord() { getWord() {
this.htmlContent = 'true'; this.htmlContent = 'true';
}, },
// 获取数据 // 获取数据
async getDate() { async getDate() {
this.imagesList = []; this.imagesList = [];
let urlLInk = ''; let urlLInk = '';
let urlTask = {}; let urlTask = {};
@@ -1548,7 +1734,7 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
.post(urlLInk, urlTask) .post(urlLInk, urlTask)
.then(async (res) => { .then(async (res) => {
if (res.code == 0) { if (res.code == 0) {
this.Main_List = res.data.list.map(e=>{ this.Main_List = res.data.list.map((e) => {
e.checked = false; e.checked = false;
return e; return e;
}); });
@@ -1556,14 +1742,12 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
for (let i = 0; i < this.Main_List.length; i++) { for (let i = 0; i < this.Main_List.length; i++) {
this.Main_List[i].text = this.Main_List[i].content; this.Main_List[i].text = this.Main_List[i].content;
this.Main_List[i].getnum = 0; this.Main_List[i].getnum = 0;
// this.Main_List[i].checked = false;
} }
// setTimeout(async () => {
this.$nextTick(async () => { this.$nextTick(async () => {
await this.getWord(); await this.getWord();
if (this.$refs.catalogue) {
await this.$refs.catalogue.getCatalogueList();
}
loading.close(); loading.close();
}); });
// }, 1000); // }, 1000);
@@ -1659,8 +1843,6 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
// 确定保存图片 // 确定保存图片
async savePic() { async savePic() {
var str = this.picStyle1.note ? await this.$commonJS.decodeHtml(this.picStyle1.note) : ''; var str = this.picStyle1.note ? await this.$commonJS.decodeHtml(this.picStyle1.note) : '';
var titleStr = this.picStyle1.title ? await this.$commonJS.decodeHtml(this.picStyle1.title) : ''; var titleStr = this.picStyle1.title ? await this.$commonJS.decodeHtml(this.picStyle1.title) : '';
@@ -1672,6 +1854,12 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
this.$message.error('Please enter a title'); this.$message.error('Please enter a title');
return; return;
} }
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
if (this.picStyle.visiTitle == 'Edit Figure') { if (this.picStyle.visiTitle == 'Edit Figure') {
this.$api this.$api
.post(this.urlList.editImage, { .post(this.urlList.editImage, {
@@ -1682,20 +1870,22 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
}) })
.then((res) => { .then((res) => {
if (res.code == 0) { if (res.code == 0) {
loading.close();
this.$message.success('Successfully edit Figure!'); this.$message.success('Successfully edit Figure!');
this.pictVisible = false; this.pictVisible = false;
this.$refs.commonWordHtmlTypeSetting.refresh('editImg', res.data.image?{...res.data.image,has_selected:1}:res.data);
this.refreshCurrentContent('editImg',res.data.am_id, res.data);
this.$nextTick(() => { this.$nextTick(() => {
this.getDate();
this.getCommentList(); this.getCommentList();
this.$forceUpdate(); this.$forceUpdate();
}); });
this.$refs.commonWordHtmlTypeSetting.refresh('img');
} else { } else {
loading.close();
this.$message.error(res.msg); this.$message.error(res.msg);
} }
}) })
.catch((err) => { .catch((err) => {
loading.close();
this.$message.error(err); this.$message.error(err);
}); });
} else { } else {
@@ -1708,15 +1898,19 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
}) })
.then((res) => { .then((res) => {
if (res.code == 0) { if (res.code == 0) {
loading.close();
this.$message.success('Successfully Add Figure!'); this.$message.success('Successfully Add Figure!');
this.pictVisible = false; this.pictVisible = false;
this.getDate();
this.$refs.commonWordHtmlTypeSetting.refresh('img'); this.$refs.commonWordHtmlTypeSetting.refresh('addImg', res.data);
} else { } else {
loading.close();
this.$message.error(res.msg); this.$message.error(res.msg);
} }
}) })
.catch((err) => { .catch((err) => {
loading.close();
this.$message.error(err); this.$message.error(err);
}); });
} }
@@ -1766,18 +1960,18 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
} }
if (strTitle != '') { if (strTitle != '') {
strTitle = await this.$commonJS.decodeHtml(strTitle); strTitle = await this.$commonJS.decodeHtml(strTitle);
}else{ } else {
this.$message.error('Please enter a title'); this.$message.error('Please enter a title');
return; return;
} }
if (content && cleanedTableList && cleanedTableList.length > 0) { if (content && cleanedTableList && cleanedTableList.length > 0) {
const loading = this.$loading({ const loading = this.$loading({
lock: true, lock: true,
text: 'Loading...', text: 'Loading...',
spinner: 'el-icon-loading', spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}); });
if (this.lineStyle.visiTitle == 'Edit Table') { if (this.lineStyle.visiTitle == 'Edit Table') {
this.$api this.$api
.post(this.urlList.editTable, { .post(this.urlList.editTable, {
@@ -1789,21 +1983,22 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
}) })
.then((res) => { .then((res) => {
if (res.code == 0) { if (res.code == 0) {
loading.close();
this.$message.success('Successfully edit Table!'); this.$message.success('Successfully edit Table!');
this.threeVisible = false; this.threeVisible = false;
setTimeout(() => { setTimeout(() => {
this.getDate(); this.$refs.commonWordHtmlTypeSetting.refresh('editTable', res.data.table?{...res.data.table,has_selected:1}:res.data);
this.refreshCurrentContent('editTable',res.data.am_id, res.data);
this.getCommentList(); this.getCommentList();
this.$refs.commonWordHtmlTypeSetting.refresh('table');
}); });
} else { } else {
this.$message.error(res.msg);
loading.close(); loading.close();
this.$message.error(res.msg);
} }
}) })
.catch((err) => { .catch((err) => {
this.$message.error(err);
loading.close(); loading.close();
this.$message.error(err);
}); });
} else { } else {
this.$api this.$api
@@ -1816,14 +2011,14 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
}) })
.then((res) => { .then((res) => {
if (res.code == 0) { if (res.code == 0) {
loading.close();
this.$message.success('Successfully Add Table!'); this.$message.success('Successfully Add Table!');
this.threeVisible = false; this.threeVisible = false;
this.getDate();
loading.close(); this.$refs.commonWordHtmlTypeSetting.refresh('addTable', res.data);
this.$refs.commonWordHtmlTypeSetting.refresh('table');
} else { } else {
this.$message.error(res.msg);
loading.close(); loading.close();
this.$message.error(res.msg);
} }
}) })
.catch((err) => { .catch((err) => {
@@ -1940,7 +2135,7 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
this.$message.error('Picture size cannot exceed 20M!'); this.$message.error('Picture size cannot exceed 20M!');
return false; return false;
} }
const isValidFormat = ['image/jpeg', 'image/png', 'image/tiff'].includes(file.type); const isValidFormat = ['image/jpeg', 'image/png', 'image/tiff'].includes(file.type);
if (!isValidFormat) { if (!isValidFormat) {
this.$message.error(this.$t('commonTable.uploadImageInfo')); this.$message.error(this.$t('commonTable.uploadImageInfo'));
@@ -2337,5 +2532,4 @@ this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指
background-color: #0066990d; background-color: #0066990d;
/* display: block !important; */ /* display: block !important; */
} }
</style> </style>

View File

@@ -1103,7 +1103,7 @@ export default {
if (data && data.remark) { if (data && data.remark) {
this.$alert( this.$alert(
`<p style="display:flex;"><img src="${this.remarkImageUrl}" alt="" style="width:20px;height:20px;margin-right:10px;"/><span style=" overflow-wrap: break-word;width:calc(100% - 50px)"> ${data.remark}</span></p>`, `<p style="display:flex;"><img src="${this.remarkImageUrl}" alt="" style="width:20px;height:20px;margin-right:10px;"/><span style=" overflow-wrap: break-word;width:calc(100% - 50px)"> ${data.remark}</span></p>`,
'Annotations', 'Comments',
{ {
confirmButtonText: 'OK', confirmButtonText: 'OK',
dangerouslyUseHTMLString: true, // 启用 HTML 渲染 dangerouslyUseHTMLString: true, // 启用 HTML 渲染

View File

@@ -124,7 +124,7 @@
:value="tradition" :value="tradition"
class="paste-area text-container" class="paste-area text-container"
:toolbar="[ :toolbar="[
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line|subscript superscript|clearButton' 'bold italic |customBlue removeBlue|myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton'
]" ]"
style=" style="
line-height: 12px; line-height: 12px;
@@ -155,7 +155,7 @@
:value="mhooStr" :value="mhooStr"
class="paste-area text-container" class="paste-area text-container"
:toolbar="[ :toolbar="[
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line|subscript superscript|clearButton' 'bold italic |customBlue removeBlue|myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton'
]" ]"
style=" style="
line-height: 12px; line-height: 12px;
@@ -199,7 +199,7 @@
:value="abstract" :value="abstract"
class="paste-area text-container" class="paste-area text-container"
:toolbar="[ :toolbar="[
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line|subscript superscript|clearButton' 'bold italic |customBlue removeBlue|myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton'
]" ]"
style=" style="
line-height: 12px; line-height: 12px;
@@ -688,39 +688,92 @@
<div class="bor_style_onli"> <div class="bor_style_onli">
<h4>{{ tabsList[5].name }}</h4> <h4>{{ tabsList[5].name }}</h4>
<div style="font-size: 14px"> <div style="font-size: 14px">
<div style="margin: 30px 0 40px 0"> <div style="margin: 30px 0px 0 0">
Choose Template : <h5 style="font-size: 16px; margin: 0 0 30px 0; letter-spacing: -0.5px">Improve information</h5>
<el-select <!-- 时间Doi简介信息 -->
v-model="shuTter.board" <el-form ref="Abs_Form" :model="detailMes" :rules="rules" label-width="150px" style="margin-top: 30px">
placeholder="Please select a template..." <el-form-item label="Doi :" prop="doi">
@change="select_tem($event)" <span>https://doi.org/10.53388/</span>
style="width: 225px" <el-input v-model="detailMes.doi" style="margin-left: 10px; width: 325px"> </el-input>
> </el-form-item>
<el-option v-for="item in fol_low" :key="item.value" :label="item.label" :value="item.value"> </el-option> <el-form-item label="Date of publication :" prop="pubDate">
</el-select> <el-date-picker
<el-button type="primary" plain style="width: 150px; margin: 0 5px 0 15px" @click="EstaBlish"> v-model="detailMes.pubDate"
<i class="el-icon-document"></i> value-format="yyyy-MM-dd"
Create Manuscript type="date"
</el-button> placeholder=""
</div> @blur="dateBlur(detailMes.pubDate)"
<div style="margin: 30px 0 40px 0"> style="width: 100%"
<h5 style="font-size: 16px; margin: 0 0 30px 0; letter-spacing: -0.5px">Download list</h5> >
<p v-if="DLfileList == ''" style="color: #666; font-size: 14px">No Manuscript</p> </el-date-picker>
<div v-for="(item, index) in DLfileList" target="_blank" class="load_pdf"> </el-form-item>
<img src="../../assets/img/icon_0.png" /> </el-form>
<span style="color: #333">Typesetting {{ index + 1 }}</span> <div style="text-align: center; margin: 0px 0 0 0">
<span style="margin-left: 40px; color: #888; font-size: 13px" <el-button type="primary" @click="ZsSaveAbs" class="save-btn" style="margin-top: 10px;">
>Time : {{ modifDate(item.ctime * 1000) }}</span <i class="el-icon-check"></i>
> Save Essential Information
<a :href="'/tsfile/' + item.url" target="_blank" style="margin-left: 40px"> </el-button>
<i class="el-icon-download" style="color: #66b1ff; font-weight: bold"></i>
</a>
</div> </div>
<!-- <p @click="allLoad()">
<i class="el-icon-download" style="color: #66b1ff;font-weight: bold;"></i>123
</p> -->
</div> </div>
<div style="height: 1px; background-color: #c5e1f1; width: 100%; margin: 80px 0 40px"></div>
<div style="display: flex; /* 开启 flex 布局 */
justify-content: space-between;
padding: 20px;">
<div class="left-panel">
<div style="margin: 0 0 40px 0">
Choose Template :
<el-select v-model="shuTter.board" placeholder="Please select a template..." @change="select_tem($event)" style="width: 225px">
<el-option v-for="item in fol_low" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
<el-button type="primary" plain style="width: 150px; margin: 0 5px 0 15px" @click="EstaBlish">
<i class="el-icon-document"></i> Create Manuscript
</el-button>
</div>
<div style="margin: 30px 0 40px 0">
<h5 style="font-size: 16px; margin: 0 0 30px 0; letter-spacing: -0.5px">Download list</h5>
<p v-if="DLfileList.length == 0" style="color: #666; font-size: 14px">No Manuscript</p>
<div v-for="(item, index) in DLfileList" :key="index" class="load_pdf">
<img src="../../assets/img/icon_0.png" />
<span style="color: #333">Typesetting {{ index + 1 }}</span>
<span style="margin-left: 40px; color: #888; font-size: 13px">Time : {{ modifDate(item.ctime * 1000) }}</span>
<a :href="'/tsfile/' + item.url" target="_blank" style="margin-left: 40px">
<i class="el-icon-download" style="color: #66b1ff; font-weight: bold"></i>
</a>
</div>
</div>
</div>
<div class="right-panel">
<h5 style="font-size: 14px; margin: 0 0 40px 0;">
<el-button
type="success"
icon="el-icon-printer"
style="width: 150px; height: 32px; font-size: 12px;margin-right: 10px;"
:loading="isGenerating"
@click="generatePDF"
>
{{ isGenerating ? 'Generating PDF...' : 'Generate PDF' }}
</el-button>
<span>Click the button below to start high-quality PDF typesetting.</span>
</h5>
<div style="margin: 30px 0 40px 0">
<h5 style="font-size: 16px; margin: 0 0 30px 0; letter-spacing: -0.5px;font-weight: bold;">Download list</h5>
<p v-if="DLfileList.length == 0" style="color: #666; font-size: 14px">No Manuscript</p>
<div v-for="(item, index) in DLfileList" :key="index" class="load_pdf">
<img src="../../assets/img/icon_0.png" />
<span style="color: #333">Typesetting {{ index + 1 }}</span>
<span style="margin-left: 40px; color: #888; font-size: 13px">Time : {{ modifDate(item.ctime * 1000) }}</span>
<a :href="'/tsfile/' + item.url" target="_blank" style="margin-left: 40px">
<i class="el-icon-download" style="color: #66b1ff; font-weight: bold"></i>
</a>
</div>
</div>
</div>
</div>
<div style="height: 1px; background-color: #c5e1f1; width: 100%; margin: 10px 0"></div> <div style="height: 1px; background-color: #c5e1f1; width: 100%; margin: 10px 0"></div>
<div style="margin: 30px 0 30px 0"> <div style="margin: 30px 0 30px 0">
<h5 style="font-size: 16px; margin: 0 0 30px 0; letter-spacing: -0.5px">Upload the final typeset file</h5> <h5 style="font-size: 16px; margin: 0 0 30px 0; letter-spacing: -0.5px">Upload the final typeset file</h5>
@@ -767,34 +820,7 @@
</el-upload> </el-upload>
</div> </div>
</div> </div>
<div style="height: 1px; background-color: #c5e1f1; width: 100%; margin: 10px 0"></div>
<div style="margin: 30px 0px 0 0">
<h5 style="font-size: 16px; margin: 0 0 30px 0; letter-spacing: -0.5px">Improve information</h5>
<!-- 时间Doi简介信息 -->
<el-form ref="Abs_Form" :model="detailMes" :rules="rules" label-width="150px" style="margin-top: 30px">
<el-form-item label="Doi :" prop="doi">
<span>https://doi.org/10.53388/</span>
<el-input v-model="detailMes.doi" style="margin-left: 10px; width: 325px"> </el-input>
</el-form-item>
<el-form-item label="Date of publication :" prop="pubDate">
<el-date-picker
v-model="detailMes.pubDate"
value-format="yyyy-MM-dd"
type="date"
placeholder=""
@blur="dateBlur(detailMes.pubDate)"
style="width: 100%"
>
</el-date-picker>
</el-form-item>
</el-form>
<div style="text-align: center; margin: 25px 0 0 0">
<el-button type="primary" @click="ZsSaveAbs" class="save-btn">
<i class="el-icon-check"></i>
Save Essential Information
</el-button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -994,6 +1020,8 @@ import Tinymce from '@/components/page/components/Tinymce';
export default { export default {
data() { data() {
return { return {
isGenerating: false, // 按钮加载状态
percentage: 0, // 进度条
finalReview: null, finalReview: null,
baseUrl: this.Common.baseUrl, baseUrl: this.Common.baseUrl,
mediaUrl: this.Common.mediaUrl, mediaUrl: this.Common.mediaUrl,
@@ -1452,6 +1480,42 @@ export default {
this.getWorldPdf(); this.getWorldPdf();
}, },
methods: { methods: {
async generatePDF() {
// 1. 验证是否选择了模板
if (!this.shuTter.board) {
this.$message.warning("Please select a template first!");
return;
}
this.isGenerating = true;
this.percentage = 0;
try {
// 模拟进度条逻辑(如果是真实接口,可以根据后端返回或者固定时长模拟)
let timer = setInterval(() => {
if (this.percentage < 90) this.percentage += 10;
}, 500);
// 调用你的生成接口 (假设是 Establish 的升级版)
// const res = await api.createPDF({ board: this.shuTter.board });
// 模拟接口请求耗时
await new Promise(resolve => setTimeout(resolve, 3000));
this.percentage = 100;
clearInterval(timer);
this.$message.success("PDF Generated Successfully!");
// 自动刷新左侧的下载列表
// this.getDLFileList();
} catch (error) {
this.$message.error("Generation failed, please try again.");
} finally {
this.isGenerating = false;
}
},
getArticleFinal(id) { getArticleFinal(id) {
// api/Finalreview/getRecord // api/Finalreview/getRecord
this.$api this.$api
@@ -3071,6 +3135,37 @@ export default {
height: 6vh; height: 6vh;
min-height: 60px; min-height: 60px;
} }
.left-panel {
flex: 1; /* 左侧占据剩余空间 */
margin-right: 40px;
border-right: 1px dashed #eee; /* 加一条虚线分隔线 */
padding-right: 20px;
}
.right-panel {
width: 45%; /* 右侧固定宽度 */
}
.right-panel h5 {
font-weight: normal;
}
.console-card {
background: #f9f9f9;
border-radius: 8px;
padding: 20px;
border: 1px solid #ebeef5;
}
.load_pdf {
display: flex;
align-items: center;
margin-bottom: 15px;
padding: 10px;
transition: background 0.3s;
}
.load_pdf:hover {
background: #f5f7fa;
}
</style> </style>
<style> <style>
.handle-box { .handle-box {
@@ -3660,4 +3755,5 @@ export default {
padding: 20px 20px 0 !important; padding: 20px 20px 0 !important;
font-weight: bold !important; font-weight: bold !important;
} }
</style> </style>

View File

@@ -592,7 +592,7 @@ export default {
if (data && data.remark) { if (data && data.remark) {
this.$alert( this.$alert(
`<p style="display:flex;"><img src="${this.remarkImageUrl}" alt="" style="width:20px;height:20px;margin-right:10px;"/><span style=" overflow-wrap: break-word;width:calc(100% - 50px)"> ${data.remark}</span></p>`, `<p style="display:flex;"><img src="${this.remarkImageUrl}" alt="" style="width:20px;height:20px;margin-right:10px;"/><span style=" overflow-wrap: break-word;width:calc(100% - 50px)"> ${data.remark}</span></p>`,
'Annotations', 'Comments',
{ {
confirmButtonText: 'OK', confirmButtonText: 'OK',
dangerouslyUseHTMLString: true, // 启用 HTML 渲染 dangerouslyUseHTMLString: true, // 启用 HTML 渲染

View File

@@ -67,6 +67,10 @@ export default {
}, },
data() { data() {
return { return {
uploadNotifications: {},
totalUploadImages: 0,
uploadedImageCount: 0,
uploadNotificationInstance: null, // 全局通知实例
baseUrl: this.Common.baseUrl, baseUrl: this.Common.baseUrl,
mediaUrl: this.Common.mediaUrl, mediaUrl: this.Common.mediaUrl,
typesettingType: 1, typesettingType: 1,
@@ -136,6 +140,15 @@ export default {
mounted() { mounted() {
this.typesettingType = 1; this.typesettingType = 1;
this.initTinymce(); this.initTinymce();
const style = document.createElement('style');
style.innerHTML = `
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes slideIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.status-spinning { display: inline-block; animation: spin 1s linear infinite; }
.upload-item-animate { animation: slideIn 0.3s ease-out forwards; }
.el-progress-bar__inner { transition: width 0.4s ease-in-out !important; }
`;
document.head.appendChild(style);
}, },
activated() { activated() {
this.typesettingType = 1; this.typesettingType = 1;
@@ -145,9 +158,139 @@ export default {
this.destroyTinymce(); this.destroyTinymce();
}, },
methods: { methods: {
onClear() {
if (this.uploadNotificationInstance) {
this.uploadNotificationInstance.close(); this.uploadNotificationInstance = null;
this.uploadedImageCount = 0;
this.totalUploadImages = 0;}
},
updateUploadProgressNotification(imgIndex, status = 'processing', message = '') {
// 快捷调用 $t
const t = (key, params) => this.$t(`imageTask.${key}`, params);
if (!this.uploadNotificationInstance) {
this.uploadNotificationInstance = this.$notify({
title: t('title'),
dangerouslyUseHTMLString: true,
message: `
<div id="image-upload-container" style="width: 300px;">
<p id="total-status-text" style="margin: 0 0 12px 0; font-size: 14px; color: #606266; font-weight: 500;">
${t('preparing', { total: this.totalUploadImages })}
</p>
<div class="el-progress el-progress--line" style="margin-bottom: 15px;">
<div class="el-progress-bar">
<div class="el-progress-bar__outer" style="height: 10px; background: #ebeef5; border-radius: 5px;">
<div class="el-progress-bar__inner" style="width: 0%; background: #409EFF; transition: width 0.4s ease-out;"></div>
</div>
</div>
<div class="el-progress__text" style="font-size: 12px; font-weight: bold; margin-top: 2px;">0%</div>
</div>
<ul id="image-individual-status" style="max-height: 400px; overflow-y: auto; padding: 0; margin: 0; list-style: none; border-top: 1px solid #f0f0f0; padding-top: 10px;"></ul>
<p id="manual-close-tip" style="display:none; margin-top:15px; font-size:12px; color:#909399; text-align:right; border-top: 1px dashed #eee; padding-top: 8px;">
${t('manualClose')}
</p>
</div>`,
duration: 0,
position: 'bottom-left',
onClose: () => {
this.uploadNotificationInstance = null;
this.uploadedImageCount = 0;
this.totalUploadImages = 0;
}
});
}
const listContainer = document.getElementById('image-individual-status');
if (!listContainer) return;
let itemEl = document.getElementById(`img-status-${imgIndex}`);
if (!itemEl) {
itemEl = document.createElement('li');
itemEl.id = `img-status-${imgIndex}`;
itemEl.className = 'upload-item-animate';
itemEl.style.cssText =
'font-size: 13px; padding: 8px 5px; display: flex; align-items: center; border-bottom: 1px solid #fafafa;';
itemEl.innerHTML = `
<span style="width: 55px; color: #909399; font-weight: bold;">${t('imgLabel')} ${imgIndex + 1}:</span>
<span class="status-icon" style="margin: 0 10px;"></span>
<span class="status-text" style="flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #666;">...</span>
`;
listContainer.appendChild(itemEl);
listContainer.scrollTop = listContainer.scrollHeight;
}
const statusIcon = itemEl.querySelector('.status-icon');
const statusText = itemEl.querySelector('.status-text');
let isFinished = false;
if (statusIcon && statusText) {
switch (status) {
case 'processing':
statusIcon.innerHTML = '<span class="status-spinning">🔄</span>';
statusText.innerText = t('parsing');
break;
case 'uploading':
statusIcon.innerHTML = '<span class="status-spinning">⬆️</span>';
statusText.innerText = t('uploading');
break;
case 'tooLarge':
statusIcon.innerHTML = '⚠️';
statusText.innerText = t('tooLarge');
itemEl.style.color = '#E6A23C';
isFinished = true;
break;
case 'success':
statusIcon.innerHTML = '✅';
statusText.innerText = t('success');
itemEl.style.color = '#67C23A';
isFinished = true;
break;
case 'fail':
statusIcon.innerHTML = '❌';
statusText.innerText = t('error', { msg: message });
itemEl.style.color = '#F56C6C';
isFinished = true;
break;
}
}
if (isFinished) {
this.uploadedImageCount++;
const progressPercent = Math.min(100, Math.round((this.uploadedImageCount / this.totalUploadImages) * 100));
const barInner = this.uploadNotificationInstance.$el.querySelector('.el-progress-bar__inner');
const barText = this.uploadNotificationInstance.$el.querySelector('.el-progress__text');
const statusTotalText = document.getElementById('total-status-text');
if (barInner) barInner.style.width = `${progressPercent}%`;
if (barText) barText.innerText = `${progressPercent}%`;
if (statusTotalText) {
statusTotalText.innerText = t('progress', { current: this.uploadedImageCount, total: this.totalUploadImages });
}
if (this.uploadedImageCount >= this.totalUploadImages) {
// 更新标题为完成状态
const titleEl = this.uploadNotificationInstance.$el.querySelector('.el-notification__title');
if (titleEl) titleEl.innerText = t('completed');
if (barInner) barInner.style.background = '#67C23A';
const tipEl = document.getElementById('manual-close-tip');
if (tipEl) tipEl.style.display = 'block';
if (statusTotalText) {
statusTotalText.innerText = t('allDone', { total: this.totalUploadImages });
}
}
}
},
openLatexEditor(data) { openLatexEditor(data) {
this.$emit('openLatexEditor', data); this.$emit('openLatexEditor', data);
console.log('at line 254:', '打开数字公式');
}, },
handleSubmit() { handleSubmit() {
this.$refs.uploadImage.handleSubmit(); this.$refs.uploadImage.handleSubmit();
@@ -210,10 +353,10 @@ export default {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
const formData = new FormData(); const formData = new FormData();
// 按照你截图中的参数名,这里假设是 'file' // 按照你截图中的参数名,这里假设是 'file'
formData.append('mainImage', blob, `word_img_${index}.png`); formData.append('file_name', blob, `word_img_${index}.png`);
formData.append('article_id', this.articleId); formData.append('article_id', this.articleId);
xhr.withCredentials = false; xhr.withCredentials = false;
xhr.open('POST', _this.baseUrl + 'api/Preaccept/up_img_mainImage'); xhr.open('POST', _this.baseUrl + 'api/Articlemain/uploadTableImage');
xhr.onload = function () { xhr.onload = function () {
if (xhr.status !== 200) { if (xhr.status !== 200) {
console.error('HTTP Error: ' + xhr.status); console.error('HTTP Error: ' + xhr.status);
@@ -221,9 +364,9 @@ export default {
} }
try { try {
const json = JSON.parse(xhr.responseText); const json = JSON.parse(xhr.responseText);
if (json.code === 0) { if (json.status == 1) {
// 2. 拼接服务器返回的 URL // 2. 拼接服务器返回的 URL
const finalUrl = _this.mediaUrl + 'articleImage/' + json.data.upurl; const finalUrl = _this.mediaUrl + 'articleTableImage/' + json.data;
// 3. 找到对应的加载中占位图并替换 // 3. 找到对应的加载中占位图并替换
const doc = tinymce.activeEditor.getDoc(); const doc = tinymce.activeEditor.getDoc();
const placeholder = doc.querySelector(`img[data-idx="${index}"]`); const placeholder = doc.querySelector(`img[data-idx="${index}"]`);
@@ -231,6 +374,8 @@ export default {
placeholder.src = finalUrl; placeholder.src = finalUrl;
placeholder.removeAttribute('data-idx'); // 任务完成,移除标记 placeholder.removeAttribute('data-idx'); // 任务完成,移除标记
} }
} else {
_this.removePlaceholder(index);
} }
} catch (e) { } catch (e) {
console.error('解析响应失败', e); console.error('解析响应失败', e);
@@ -240,6 +385,14 @@ export default {
xhr.send(formData); xhr.send(formData);
}, },
removePlaceholder(idx) {
const doc = tinymce.activeEditor.getDoc();
const placeholder = doc.querySelector(`img[data-idx="${idx}"]`);
if (placeholder) {
placeholder.remove(); // 直接从 DOM 中删除这个 img 标签
this.$message.error('Upload failed. Removing temporary placeholder.');
}
},
// 辅助工具Base64 转 Blob // 辅助工具Base64 转 Blob
dataURLtoBlob(dataurl) { dataURLtoBlob(dataurl) {
const arr = dataurl.split(','), const arr = dataurl.split(','),
@@ -258,6 +411,10 @@ export default {
var _this = this; var _this = this;
window.tinymce.init({ window.tinymce.init({
..._this.tinymceOtherInit, ..._this.tinymceOtherInit,
extended_valid_elements: 'blue[*]',
custom_elements: 'blue',
valid_children: '+blue[#text|i|em|b|strong|span],+body[blue],+p[blue]',
inline: false, // 使用 iframe 模式 inline: false, // 使用 iframe 模式
selector: `#${this.tinymceId}`, selector: `#${this.tinymceId}`,
// noneditable_regexp: "/<wmath>.*?<\/wmath>/g", // noneditable_regexp: "/<wmath>.*?<\/wmath>/g",
@@ -271,8 +428,41 @@ export default {
noneditable_editable_class: 'MathJax', noneditable_editable_class: 'MathJax',
height: this.height, height: this.height,
content_style: ` content_style: `
*{
font-size: 14px;
}
${tableStyle} ${tableStyle}
${_this.wordStyle} ${_this.wordStyle}
blue{
display: inline;
}
myfigure,
mytable {
pointer-events: auto !important; /* 强制允许鼠标点击 */
display: inline-block;
cursor: pointer;
color: rgb(0, 130, 170) !important;
text-shadow: 0 0 3px #09c2fb, 0 0 4px rgba(0, 130, 170, 0.3);
}
myfigure *,
mytable * {
color: inherit !important;
text-shadow: inherit !important;
background: transparent !important; /* 防止内部标签背景干扰 */
}
@keyframes blueGlow {
0%,
100% {
transform: scale(1);
opacity: 0.9;
}
50% {
transform: scale(1.02);
opacity: 1;
}
}
`, `,
formats: { formats: {
bold: { inline: 'b' }, bold: { inline: 'b' },
@@ -304,12 +494,12 @@ export default {
// 1. 处理文件名:优先使用原始文件名,没有则生成 // 1. 处理文件名:优先使用原始文件名,没有则生成
let filename = blobInfo.filename() || `upload_${Date.now()}.png`; let filename = blobInfo.filename() || `upload_${Date.now()}.png`;
// 2. 构造符合你后端要求的参数 // 2. 构造符合你后端要求的参数
formData.append('mainImage', file, filename); formData.append('file_name', file, filename);
// 优先从路由取 id其次取 data 里的 articleId // 优先从路由取 id其次取 data 里的 articleId
formData.append('article_id', _this.articleId); formData.append('article_id', _this.articleId);
xhr.withCredentials = false; xhr.withCredentials = false;
// 拼接你的 baseUrl // 拼接你的 baseUrl
xhr.open('POST', _this.baseUrl + '/api/Preaccept/up_img_mainImage'); xhr.open('POST', _this.baseUrl + 'api/Articlemain/uploadTableImage');
// 上传进度(可选) // 上传进度(可选)
xhr.upload.onprogress = (e) => { xhr.upload.onprogress = (e) => {
progress((e.loaded / e.total) * 100); progress((e.loaded / e.total) * 100);
@@ -321,9 +511,9 @@ export default {
} }
try { try {
const json = JSON.parse(xhr.responseText); const json = JSON.parse(xhr.responseText);
if (json.code === 0) { if (json.status == 1) {
// 3. 按照你的逻辑返回拼接后的完整 URL // 3. 按照你的逻辑返回拼接后的完整 URL
const finalUrl = _this.mediaUrl + 'articleImage/' + json.data.upurl; const finalUrl = _this.mediaUrl + 'articleTableImage/' + json.data;
resolve(finalUrl); resolve(finalUrl);
console.log('手动上传成功:', finalUrl); console.log('手动上传成功:', finalUrl);
} else { } else {
@@ -374,15 +564,42 @@ export default {
); );
} }
}); });
// 修改 paste 事件中的逻辑
ed.on('paste', (event) => { ed.on('paste', async (event) => {
const rtf = event.clipboardData.getData('text/rtf'); const rtf = event.clipboardData.getData('text/rtf');
if (rtf && rtf.includes('\\pict')) { if (rtf && rtf.includes('\\pict')) {
const extracted = extractHexImagesFromRTF(rtf); const extracted = extractHexImagesFromRTF(rtf);
extracted.forEach((img, i) => { _this.totalUploadImages = extracted.length; // 设置总数
const base64 = _this.hexToBase64Sync(img.hex, img.mimeType); _this.uploadedImageCount = 0; // 重置已上传数
_this.uploadSingleImage(base64, i);
if (_this.totalUploadImages === 0) return;
// 初始化主通知框
_this.updateUploadProgressNotification(0, 'init'); // 这里的参数只是为了触发通知框的创建
const uploadPromises = extracted.map(async (img, i) => {
const isTooLarge = img.hex.length > 2 * 1024 * 1024; // 1MB 限制
_this.updateUploadProgressNotification(i, 'processing'); // 更新单张图片状态
if (isTooLarge) {
_this.updateUploadProgressNotification(i, 'tooLarge');
return; // 跳过此图片
}
try {
const base64 = _this.hexToBase64Sync(img.hex, img.mimeType);
_this.updateUploadProgressNotification(i, 'uploading'); // 更新为上传中
await _this.uploadSingleImage(base64, i);
_this.updateUploadProgressNotification(i, 'success'); // 上传成功
} catch (err) {
console.error('Upload failed:', err);
_this.updateUploadProgressNotification(i, 'fail', err.message || 'Network error'); // 上传失败
}
}); });
await Promise.all(uploadPromises);
// Promise.all 完成后, updateUploadProgressNotification 会自动计算 totalCount === uploadedCount 并关闭
} }
}); });
@@ -484,15 +701,16 @@ export default {
}, },
paste_preprocess: function (plugin, args) { paste_preprocess: function (plugin, args) {
let imgIdx = 0; let imgIdx = 0;
const silentPlaceholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; const silentPlaceholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
let content = args.content.replace(/(<img[^>]*?)src="file:\/\/\/[^" ]*"/gi, (match, p1) => { let content = args.content.replace(/(<img[^>]*?)src="file:\/\/\/[^" ]*"/gi, (match, p1) => {
// 保留 data-idx Word 里的尺寸 // 保留 data-idx Word 里的尺寸
return `${p1}src="${silentPlaceholder}" class="word-img-placeholder" data-idx="${imgIdx++}"`; return `${p1}src="${silentPlaceholder}" class="word-img-placeholder" data-idx="${imgIdx++}"`;
}); });
let tempDiv = document.createElement('div'); let tempDiv = document.createElement('div');
tempDiv.innerHTML = content; tempDiv.innerHTML = content;
if (tempDiv.querySelector('table')) { if (tempDiv.querySelector('table')) {
console.log('粘贴的内容包含表格'); console.log('粘贴的内容包含表格');
if (_this.type == 'table') { if (_this.type == 'table') {
@@ -567,11 +785,14 @@ export default {
window.renderMathJax(_this.tinymceId); window.renderMathJax(_this.tinymceId);
}, 10); }, 10);
}, },
clear_custom_action: (editor, vm) => {
vm.onClear();
},
init_instance_callback: (editor) => { init_instance_callback: (editor) => {
if (_this.value) { if (_this.value) {
editor.setContent(_this.value); editor.setContent('<p>' + _this.value + '</p>');
// console.log('at line 489:', ' 页面');
setTimeout(() => { setTimeout(() => {
window.renderMathJax(_this.tinymceId); // 初始化时渲染 MathJax window.renderMathJax(_this.tinymceId); // 初始化时渲染 MathJax
}, 10); }, 10);
@@ -648,10 +869,11 @@ export default {
editor.focus(); // 聚焦到编辑器// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式 editor.focus(); // 聚焦到编辑器// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
}, },
//销毁富文本 //销毁富文本
destroyTinymce() { destroyTinymce() {this.onClear();
if (window.tinymce.get(this.tinymceId)) { if (window.tinymce.get(this.tinymceId)) {
window.tinymce.get(this.tinymceId).destroy(); window.tinymce.get(this.tinymceId).destroy();
} }
}, },
//设置内容 //设置内容
setContent(value) { setContent(value) {
@@ -777,5 +999,4 @@ export default {
::v-deep .tox:not(.tox-tinymce-inline) .tox-editor-header { ::v-deep .tox:not(.tox-tinymce-inline) .tox-editor-header {
padding: 0 !important; padding: 0 !important;
} }
</style> </style>

View File

@@ -0,0 +1,243 @@
<template>
<transition name="table-fade">
<div v-if="visible" class="modal-mask" @click.self="close">
<div :class="['modal-container', { 'is-img-preview': type === 'img' }]" @click.self="close">
<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 class="tableTitle font">
<span v-html="renderText(processedItem.title)"></span>
</div>
<table border="1" class="custom-table-core">
<tbody>
<tr v-for="(row, rIdx) in processedItem.table.tableHeader" :key="'h-' + rIdx" class="table-header-row">
<td v-for="(cell, cIdx) in row" :key="'hc-'+cIdx" :rowspan="cell.rowspan" :colspan="cell.colspan">
<span v-html="cell.text"></span>
</td>
</tr>
<tr v-for="(row, rIdx) in processedItem.table.tableContent"
:key="'c-' + rIdx"
class="table-content-row"
:class="{ oddColor: processedItem.table.oddRowIds.includes(row.rowId) }">
<td v-for="(cell, cIdx) in row" :key="'cc-'+cIdx" :rowspan="cell.rowspan" :colspan="cell.colspan">
<span v-html="cell.text"></span>
</td>
</tr>
</tbody>
</table>
<div class="tableNote font">
<span v-html="renderText(processedItem.note)"></span>
</div>
</div>
<div v-if="type === 'img' && processedItem" class="img-viewer-content">
<div class="img-wrapper">
<img :src="mediaUrl + processedItem.url" class="full-screen-img" @click="close" title="点击图片关闭">
</div>
<div class="imageTitle font" style="margin-top: 0px;">
<span v-html="processedItem.title"></span>
</div>
<div class="imageNote font" style="margin-top: 0px;">
<span v-html="processedItem.note"></span>
</div>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
import { TableUtils } from '@/common/js/TableUtils';
import { mediaUrl } from '@/common/js/commonJS.js';
export default {
name: 'TablePreviewer',
data() {
return {
visible: false,
loading: false,
processedItem: null,
type: null,
mediaUrl,
};
},
methods: {
async open(type, item) {
this.visible = true;
this.type = type;
if (type === 'table') {
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: processed.oddRowIds
}
});
} catch (err) {
console.error("解析表格失败", err);
} finally {
this.loading = false;
}
}, 50);
} else {
this.processedItem = item;
}
// 数学公式渲染
setTimeout(() => {
if (window.renderMathJax) window.renderMathJax();
}, 500);
},
processTableData(rawContent) {
try {
const tableList = typeof rawContent === 'string' ? JSON.parse(rawContent) : rawContent;
const { header, content } = TableUtils.splitTable(tableList);
const { rowData, rowIds } = TableUtils.addRowIdToData(content);
return { tableHeader: header, tableContent: rowData, oddRowIds: rowIds };
} catch (e) {
return { tableHeader: [], tableContent: [], oddRowIds: [] };
}
},
renderText(text) {
if (this.$parent && typeof this.$parent.highlightText === 'function') {
return this.$parent.highlightText(
{ am_id: this.processedItem.am_id, text: text || '' },
this.processedItem.checks || []
);
}
return text || '';
},
close() {
this.visible = false;
this.processedItem = null;
}
}
};
</script>
<style scoped>
.modal-mask {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.75);
z-index: 9999;
overflow-y: auto;
display: block;
padding: 60px 0;
cursor: pointer;
box-sizing: border-box;
}
.modal-container {
display: flex;
justify-content: center;
align-items: flex-start;
width: 100%;
min-height: 100%;
cursor: pointer;
}
/* 统一的白色“纸张”边框容器 */
.table-paper-view {
position: relative;
min-width: 794px; /* A4 宽度参考 */
max-width: 90vw;
background-color: #fff;
padding: 10px 20px;
box-sizing: border-box;
box-shadow: 0 12px 40px rgba(0,0,0,0.5);
border-radius: 4px;
cursor: default;
margin-bottom: 20px;
}
/* 当预览图片时,如果想让框居中,可以给父级加这个 */
.is-img-preview {
align-items: center;
}
.img-wrapper {
display: flex;
justify-content: center;
margin: 15px 0 5px;
}
.full-screen-img {
max-width: 100%; /* 限制在白色框内 */
max-height: 70vh;
display: block;
cursor: zoom-out;
border: 1px solid #eee; /* 给图片本身加个微弱边框 */
}
.close-icon-top {
position: absolute;
right: -50px;
top: 0;
font-size: 40px;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
line-height: 1;
}
.close-icon-top:hover {
color: #fff;
}
.custom-table-core {
width: 100%;
border-collapse: collapse;
}
.font {
font-size: 14px;
line-height: 1.6;
display: block;
margin: 10px auto;
}
.tableTitle, .imageTitle {
text-align: center;
font-weight: bold;
color: rgb(210, 90, 90);
font-size: 14px;
}
.tableNote, .imageNote {
color: #333;
margin-top: 15px;
}
.imageNote {
text-align: center;
}
.oddColor td {
background: rgb(250, 231, 232) !important;
}
.table-fade-enter-active, .table-fade-leave-active { transition: opacity 0.3s ease; }
.table-fade-enter, .table-fade-leave-to { opacity: 0; }
</style>

View File

@@ -152,11 +152,11 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
setTimeout(() => { setTimeout(() => {
const el = document.querySelector('.math-only'); const el = document.querySelector('.math-only');
console.log('el at line 121:', el);
if (window.MathJax && el) { if (window.MathJax && el) {
// MathJax.typesetClear([el]); // 清除旧渲染 // MathJax.typesetClear([el]); // 清除旧渲染
window.MathJax.typesetPromise([el]).catch((err) => { window.MathJax.typesetPromise([el]).catch((err) => {
console.warn('MathJax 渲染失败:', err);
}); });
} }
}, 500); }, 500);

View File

@@ -11,23 +11,23 @@
<div class="list-container"> <div class="list-container">
<el-radio-group v-model="selectedId" class="full-width-radio"> <el-radio-group v-model="selectedId" class="full-width-radio">
<div <div
v-for="item in displayList" v-for="(item, index) in displayList"
:key="item.amt_id || item.ami_id" :key="item.amt_id || item.ami_id"
:class="['item-card', { active: selectedId === item.amt_id || selectedId === item.ami_id }]" :class="['item-card', { active: selectedId === item.amt_id || selectedId === item.ami_id }]"
> >
<el-radio :label="item.amt_id || item.ami_id" <el-radio :label="item.amt_id || item.ami_id"
><div ><div
v-html="item.title" v-html="item.title || `${currentTypeText} ${index+1}`"
class="custom-radio-title" class="custom-radio-title"
@click.stop="handleClick(item)" @click.stop="handleClick(item)"
:title="stripHtml(item.title)" :title="stripHtml(item.title || `${currentTypeText} ${index+1}`)"
></div ></div
></el-radio> ></el-radio>
</div> </div>
<el-empty <el-empty
v-if="displayList.length === 0" v-if="displayList.length === 0"
:description="`No ${currentTypeText.toLowerCase()}s found in document`" :description="`No ${currentTypeText.toLowerCase()}s found`"
:image-size="60" :image-size="60"
></el-empty> ></el-empty>
</el-radio-group> </el-radio-group>
@@ -63,6 +63,7 @@ export default {
}, },
show(data) { show(data) {
this.selectLinkData = data; this.selectLinkData = data;
console.log('show at line 34:', data);
// 1. 获取传入的 ID // 1. 获取传入的 ID
const inputId = data.selectedId ? Number(data.selectedId) : null; const inputId = data.selectedId ? Number(data.selectedId) : null;
const exists = this.displayList.some((item) => item.ami_id === inputId || item.amt_id === inputId); const exists = this.displayList.some((item) => item.ami_id === inputId || item.amt_id === inputId);

View File

@@ -0,0 +1,262 @@
<template>
<div class="ManuscirptList">
<div class="arrlist">
<ul class="catalogue-ul">
<li class="catalogue-li">
<div
@click="goToListComment(item.am_id, 'content')"
v-for="(item, index) in catalogueList"
:key="index"
:class="['catalogue-item', 'level-' + item.level]"
>
<div class="title-content-wrapper">
<div class="title-content" v-html="item.content"></div>
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑
export default {
props: ['articleId', 'imgWidth', 'imgHeight', 'scale', 'isEdit', 'isShowEdit', 'urlList', 'content'],
watch: {
content: {
handler(newVal, oldVal) {
if (this.content.length > 0) this.getCatalogueList();
},
deep: true
}
},
data() {
return {
catalogueList: [],
isShowComment: false,
isCollapse: false,
isEditComment: false,
isEditing: null, // 用于跟踪当前正在编辑的批注索引
selectedComment: null, // 存储当前选择的批注内容
comments: [], // 存储所有批注
isFresh: false,
currentSelectType: '0',
currentMenu: 1,
mediaUrl,
statusList: [
{ title: 'ALL', type: '0' },
{ title: 'Typed', type: '1' },
{ title: 'Unfettered', type: '2' }
],
images: [],
tables: [],
imagesList: [],
tablesList: [],
tablesHtml: [],
imagesHtml: [],
activeNames: ['images', 'tables']
};
},
directives: {
// 注册一个局部的自定义指令 v-focus
focus: {
// 指令的定义
inserted: function (el) {
console.log('el at line 263:', el);
// 聚焦元素
el.querySelector('textarea').focus();
}
}
},
methods: {
getCatalogueList() {
this.catalogueList = this.content
.filter((item) => {
return item.is_h1 == 1 || item.is_h2 == 1;
})
.map((item) => {
// 增加一个 level 字段进行标记
let level = 1;
if (item.is_h2 == 1) level = 2;
return {
...item,
level: level // 1, 2, 或 3
};
});
this.catalogueList = [
...this.catalogueList
// ,{
// am_id:'References',
// content:'References',
// level:1,
// }
];
this.$forceUpdate();
// this.catalogueList = res.data;
},
goToListComment(id, type) {
this.$emit('goToListComment', id, type);
}
},
async created() {},
mounted() {
this.getCatalogueList();
},
async activated() {
this.getCatalogueList();
}
};
</script>
<style scoped>
.ManuscirptList {
}
/* 模拟 Word 导航标题 */
.word-navigation-header {
padding: 12px 16px 0 16px;
font-size: 14px;
color: #333;
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
}
.word-tabs {
display: flex;
margin-top: 10px;
border-bottom: 1px solid #e1e1e1;
}
.word-tabs span {
padding: 4px 12px;
font-size: 12px;
color: #666;
cursor: pointer;
position: relative;
}
.word-tabs span.active {
color: #2b579a; /* Word 经典蓝色 */
font-weight: bold;
}
.word-tabs span.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background-color: #2b579a;
}
/* 列表容器 */
.arrlist {
flex: 1;
overflow-y: auto;
/* padding-top: 8px; */
}
.catalogue-ul {
width: 100%;
list-style: none;
padding: 0;
margin: 0;
}
/* 基础条目样式 - 模拟 Word 悬浮效果 */
.catalogue-item {
position: relative;
width: 100%;
/* padding: 6px 16px; */
cursor: pointer;
display: flex;
align-items: flex-start;
box-sizing: border-box;
transition: background-color 0.1s;
}
.catalogue-item:hover {
background-color: #eff3f9; /* Word 淡淡的选中蓝 */
}
/* Word 样式的左侧小箭头 */
.word-icon-arrow {
width: 0;
height: 0;
border-left: 5px solid #666;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
margin-right: 8px;
margin-top: 6px;
}
.title-content-wrapper {
flex: 1;
overflow: hidden;
}
/* 重点Word 常用排版字体 */
.title-content {
font-weight: bold !important;
font-style: normal !important;
font-size: 14px !important;
line-height: 1.3;
white-space: nowrap;
padding: 4px 0px;
overflow: hidden;
text-overflow: ellipsis;
/* Times New Roman 增加学术感 */
font-family: 'Charis SIL' !important;
color: #333;
}
/* 保持 v-html 内部样式 */
::v-deep .title-content b {
font-weight: bold;
}
::v-deep .title-content i {
font-style: italic;
}
/* --- Word 风格的层级缩进 --- */
/* 1级标题 */
.level-1 {
padding-left: 0px;
}
.level-1 .title-content {
font-style: italic !important;
}
/* 2级标题 */
.level-2 {
padding-left: 20px;
}
.level-2 .title-content {
font-size: 14px;
color: #888;
}
/* 3级标题 */
.level-3 {
padding-left: 56px;
}
.level-3 .title-content {
font-size: 14px;
color: #666;
}
/* 隐藏滚动条 */
.arrlist::-webkit-scrollbar {
width: 5px;
}
.arrlist::-webkit-scrollbar-thumb {
background: #cdcdcd;
}
</style>

View File

@@ -16,7 +16,7 @@
:value="value" :value="value"
:typesettingType="typesettingType" :typesettingType="typesettingType"
class="paste-area text-container" class="paste-area text-container"
:toolbar="!isAutomaticUpdate?['bold italic |customBlue removeBlue|LateX| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']:['bold italic |customBlue removeBlue| myuppercase myuppercasea Line|subscript superscript|clearButton|searchreplace']" :toolbar="!isAutomaticUpdate?['bold italic |customBlue removeBlue|LateX| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']:['bold italic |customBlue removeBlue| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']"
style=" style="
/* white-space: pre-line; */ /* white-space: pre-line; */
line-height: 12px; line-height: 12px;

View File

@@ -1,40 +1,28 @@
<template> <template>
<div> <div>
<tinymce
<!--uploadWord |customButtonExportWord |customButtonExportImg --> type="table"
<!-- image --> :articleId="articleId"
<tinymce ref="tinymceChild1"
type="table" :wordStyle="wordStyle"
:articleId="articleId" @getContent="getContent"
ref="tinymceChild1" @openLatexEditor="openLatexEditor"
:wordStyle="wordStyle" :height="calcDynamicWidth()"
@getContent="getContent" :value="updatedHtml"
@openLatexEditor="openLatexEditor" :typesettingType="typesettingType"
:height="calcDynamicWidth()" class="paste-area text-container"
:value="updatedHtml" :toolbar="[
:typesettingType="typesettingType" 'bold italic|customBlue removeBlue|LateX |myuppercase myuppercasea Line MoreSymbols|subscript superscript|table | searchreplace |clearButton'
class="paste-area text-container" ]"
:toolbar="['bold italic|customBlue removeBlue|LateX |myuppercase myuppercasea Line|subscript superscript|table | searchreplace |clearButton']" style="line-height: 12px; overflow: auto; font-size: 14px; margin-top: 0px; margin-bottom: 0px"
style=" ></tinymce>
/* white-space: pre-line; */
line-height: 12px;
/* max-height: 60vh; */
overflow: auto;
font-size: 12px;
font-size: 14px; /* 字体大小 */
margin-top: 0pt; /* 段前间距 */
margin-bottom: 0pt; /* 段后间距 */
"
></tinymce>
</div> </div>
</template> </template>
<script> <script>
import Tinymce from '@/components/page/components/Tinymce'; import Tinymce from '@/components/page/components/Tinymce';
export default { export default {
props: ['lineStyle','articleId'], props: ['lineStyle', 'articleId'],
components: { components: {
Tinymce Tinymce
}, },
@@ -79,8 +67,8 @@ export default {
text-align: center; text-align: center;
table-layout: auto;" table-layout: auto;"
>`; >`;
this.tableData.forEach((row,i) => { this.tableData.forEach((row, i) => {
modalContent += `<tr class="${this.isHeaderRow(i,this.tableData)?'table-header-row':''}">`; modalContent += `<tr class="${this.isHeaderRow(i, this.tableData) ? 'table-header-row' : ''}">`;
row.forEach((cell) => { row.forEach((cell) => {
modalContent += ` modalContent += `
<td <td
@@ -94,42 +82,34 @@ export default {
modalContent += `</tr>`; modalContent += `</tr>`;
}); });
modalContent += `</table></div>`; modalContent += `</table></div>`;
// console.log('modalContent at line 91:', modalContent);
this.updatedHtml = modalContent; this.updatedHtml = modalContent;
} }
// this.updatedHtml = newVal.html_data;
} else { } else {
this.updatedHtml = ''; this.updatedHtml = '';
} }
}, },
methods: { methods: {
openLatexEditor(data) { openLatexEditor(data) {
this.$emit('openLatexEditor',data) this.$emit('openLatexEditor', data);
console.log('at line 254:', '打开数字公式'); },
calcDynamicWidth() {
const parentWidth = window.innerHeight;
const result = parentWidth - 420;
return result;
},
isHeaderRow(rowIndex, table) {
var head = table[0];
return rowIndex < head[0].rowspan;
}, },
calcDynamicWidth() {
const parentWidth = window.innerHeight; // 获取窗口宽度
const result = parentWidth - 420; // 计算 `100% - 200px`
return result ;
},
isHeaderRow(rowIndex,table) {
var head=table[0]
return rowIndex < head[0].rowspan; // 假设前两行是表头
},
getTableContent(type) { getTableContent(type) {
this.$refs.tinymceChild1.getContent(type); this.$refs.tinymceChild1.getContent(type);
}, },
getContent(type, content) { getContent(type, content) {
if (content) { if (content) {
console.log('content at line 121:', content)
const container = document.createElement('div'); const container = document.createElement('div');
container.innerHTML = content; container.innerHTML = content;
this.$commonJS.parseTableToArray(content, (table) => { this.$commonJS.parseTableToArray(content, (table) => {
this.$emit('getContent', type, { html_data: content, table: table }); this.$emit('getContent', type, { html_data: content, table: table });
}); });
} else { } else {

View File

@@ -1914,7 +1914,7 @@ export default {
}, },
// 接收组件传回的关联数据 // 接收组件传回的关联数据
onLinkConfirm(selectedItem) { onLinkConfirm(selectedItem) {
console.log('关联的对象:', selectedItem);
// 在这里处理后续逻辑,比如保存绑定关系 // 在这里处理后续逻辑,比如保存绑定关系
}, },
handleOpenLink() { handleOpenLink() {

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,12 @@
<!-- v-show="tables.length > 0 || images.length > 0" --> <!-- v-show="tables.length > 0 || images.length > 0" -->
<div <div
style="background-color: transparent !important; margin-top: 0px !important; height: 100%; padding: 0 !important; overflow: hidden" style="background-color: transparent !important; margin-top: 0px !important; height: 100%; padding: 0 !important; overflow: hidden"
class="ManuscirptList" class="ManuscirptList no-select"
> >
<!-- 图片缩略图区域 --> <!-- 图片缩略图区域 -->
<el-menu <el-menu
default-active="1" default-active="4"
style="border: none; height: 100%; float: left" style="border: none; height: 100%; float: left"
background-color="#f8f8f9" background-color="#f8f8f9"
active-text-color="#006699" active-text-color="#006699"
@@ -19,6 +19,10 @@
<i class="el-icon-message-solid" style="margin: 0 auto; color: #fc625d"></i> <i class="el-icon-message-solid" style="margin: 0 auto; color: #fc625d"></i>
<span slot="title" style="line-height: 30px">Annotations</span> <span slot="title" style="line-height: 30px">Annotations</span>
</el-menu-item> --> </el-menu-item> -->
<el-menu-item index="4">
<i class="el-icon-document" color="#333639" style="margin: 0 auto"></i>
<span slot="title" style="line-height: 20px">Outline</span>
</el-menu-item>
<el-menu-item index="1"> <el-menu-item index="1">
<i class="el-icon-picture" color="#333639" style="margin: 0 auto"></i> <i class="el-icon-picture" color="#333639" style="margin: 0 auto"></i>
<span slot="title" style="line-height: 20px">Figures</span> <span slot="title" style="line-height: 20px">Figures</span>
@@ -28,10 +32,10 @@
<i class="el-icon-s-grid" color="#333639" style="margin: 0 auto"></i> <i class="el-icon-s-grid" color="#333639" style="margin: 0 auto"></i>
<span slot="title" style="line-height: 30px">Tables</span> <span slot="title" style="line-height: 30px">Tables</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="3"> <!-- <el-menu-item index="3">
<i class="el-icon-delete-solid" color="#333639" style="margin: 0 auto"></i> <i class="el-icon-delete-solid" color="#333639" style="margin: 0 auto"></i>
<span slot="title" style="line-height: 30px">Recycle Bin</span> <span slot="title" style="line-height: 30px">Recycle Bin</span>
</el-menu-item> </el-menu-item> -->
</el-menu> </el-menu>
<!-- <ul style="width: 80px; height: 100%; float: left; padding: 6px; box-sizing: border-box" v-if="currentMenu != 3"> <!-- <ul style="width: 80px; height: 100%; float: left; padding: 6px; box-sizing: border-box" v-if="currentMenu != 3">
<li <li
@@ -51,7 +55,7 @@
<p>{{ v.title }}</p> <p>{{ v.title }}</p>
</li> </li>
</ul> --> </ul> -->
<div class="go-mt"> <div class="go-mt" v-show="currentMenu != 4">
<el-radio-group v-model="isCollapse" style="float: right" size="mini" @change="changeIsCollapse"> <el-radio-group v-model="isCollapse" style="float: right" size="mini" @change="changeIsCollapse">
<el-radio-button :label="false">{{ $t('commonTable.singleRow') }}</el-radio-button> <el-radio-button :label="false">{{ $t('commonTable.singleRow') }}</el-radio-button>
<el-radio-button :label="true">{{ $t('commonTable.Multicolumn') }}</el-radio-button> <el-radio-button :label="true">{{ $t('commonTable.Multicolumn') }}</el-radio-button>
@@ -62,77 +66,8 @@
class="arrlist" class="arrlist"
> >
<ul style="width: 100%; height: auto"> <ul style="width: 100%; height: auto">
<!-- <li v-show="currentMenu == 1">
<div style="display: flex; flex-wrap: wrap; gap: 10px; justify-content: start">
<li v-for="(comment, index) in comments" :key="index" class="comment-item" style="width: 100%; padding: 6px 0">
<div @click.prevent="goToComment(comment.am_id)">
<p style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px">
<el-link class="pizhu">
<span style="color: #fc625d; display: flex; align-items: center">
<img
class="isRemark"
src="@/assets/img/isRemark.png"
alt=""
style="width: 15px; height: 15px; margin-right: 6px"
/>
{{ comment.am_id }}</span
></el-link
>
<span style="color: #b8b7b7;font-size: 12px">{{ getTime(comment.ctime) }}</span>
</p>
<div style="display: flex; align-items: center; justify-content: space-between">
<p :style="isEditComment?'width: calc(100% - 40px)':'width: calc(100%)'" style="line-height:18px;min-height: 20px; width: calc(100%);white-space: normal;" v-html="comment.remark">
<span @click="editComment(index)" style="cursor: pointer; color: #007bff; margin-left: 10px;">Edit</span>
</p>
<i
class="el-icon-edit"
v-if="isEditComment"
@click="onAddComment(comment,index)"
style="color: #006699 ;margin-right: 10px;"
></i>
<i
class="el-icon-delete"
v-if="isEditComment"
@click="deleteComment(comment, index)"
style="color: rgb(252, 98, 93)"
></i>
</div>
</div>
</li>
</div>
</li> -->
<li v-show="currentMenu == 1"> <li v-show="currentMenu == 1">
<div style="" class="go-content-charts-item-box" :class="isCollapse ? 'double' : 'single'"> <div style="" class="go-content-charts-item-box" :class="isCollapse ? 'double' : 'single'">
<!-- <div class="item_box" style="width: 100%; height: auto; position: relative" >
<el-collapse v-if="isShowEdit">
<el-collapse-item name="1" style="margin-top: 4px">
<template slot="title">
<div style="width: 100%; display: flex; align-items: center; justify-content: space-between">
<span style="color: #777">Tables {{ index + 1 }}</span>
<span v-if="isShowEdit"><i class="el-icon-edit" v-if="isEdit"></i>Comments/ Suggestions</span>
</div>
</template>
<el-input
v-if="isShowEdit"
type="textarea"
placeholder="please input content"
:readonly="!isEdit"
:rows="4"
></el-input>
</el-collapse-item>
</el-collapse>
</div> -->
<div class="item_box" style="width: 100%; height: auto; position: relative"> <div class="item_box" style="width: 100%; height: auto; position: relative">
<div class="list-center go-flex-center go-transition" style="width: 100%" @click="addImage"> <div class="list-center go-flex-center go-transition" style="width: 100%" @click="addImage">
<div class="title"> <div class="title">
@@ -142,7 +77,6 @@
<div <div
class="thumbnailBox image list-img" class="thumbnailBox image list-img"
:style="` :style="`
border: 1px solid #ccccccb5; border: 1px solid #ccccccb5;
width: 100%; width: 100%;
height: auto; height: auto;
@@ -178,13 +112,6 @@
></el-input> ></el-input>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
<!-- <p
style="color: #777; margin-top: 6px"
v-else
@click="img.has_selected == 1 ? goToListComment(img.article_image_id, 'img') : ''"
>
<el-link> Figures {{ index + 1 }} </el-link>
</p> -->
</div> </div>
<div <div
class="item_box" class="item_box"
@@ -198,10 +125,10 @@
>Figure {{ index + 1 }} >Figure {{ index + 1 }}
<span <span
class="previewbox" class="previewbox"
@click="openPreview(index, 'img')" @click="openPreview(index, 'img',img)"
v-if="['jpg', 'jpeg', 'png'].includes(img.image.split('.').pop().toLowerCase())" v-if="['jpg', 'jpeg', 'png'].includes(img.image.split('.').pop().toLowerCase())"
> >
<i class="el-icon-view" style="font-size: 16px"></i> <i class="el-icon-view" style="font-size: 12px"></i>
</span> </span>
<a :href="mediaUrl + img.image.url" style="color: #000" v-else> <a :href="mediaUrl + img.image.url" style="color: #000" v-else>
<!-- <el-tooltip class="item" effect="dark" :content="$t('commonTable.preview')" placement="top"> --> <!-- <el-tooltip class="item" effect="dark" :content="$t('commonTable.preview')" placement="top"> -->
@@ -211,7 +138,10 @@
</span> </span>
<p> <p>
<span style="" @click="edit(img, 'img')"> <span style="" @click="edit(img, 'img')">
<i class="el-icon-edit" style="color: #409eff; font-size: 16px"></i> <i class="el-icon-edit" style="color: #409eff; font-size: 12px"></i>
</span>
<span style="margin-left: 6px" @click="handledelete(img, 'img')">
<i class="el-icon-delete" style="color: #f56c6c; font-size: 12px"></i>
</span> </span>
</p> </p>
</div> </div>
@@ -259,25 +189,14 @@
@click=" @click="
img.has_selected == 1 img.has_selected == 1
? goToListComment(img.article_image_id, 'img') ? goToListComment(img.article_image_id, 'img')
: openPreview(index, 'img') : openPreview(index, 'img',img)
" "
:data-img-id="img.article_image_id" :data-img-id="img.article_image_id"
:src="mediaUrl + img.image" :src="mediaUrl + img.image"
style="width: 140px; height: 100%; object-fit: cover" style="width: 140px; height: 100%; object-fit: cover"
/> />
</template> </template>
<template v-else-if="img.image.split('.').pop().toLowerCase() === 'tif'">
<img
@click="
img.has_selected == 1
? goToListComment(img.article_image_id, 'img')
: openPreview(index, 'img')
"
:data-img-id="img.article_image_id"
:src="img.dataUrl"
style="width: 140px; height: 100%; object-fit: cover"
/>
</template>
<template v-else> <template v-else>
<div <div
style=" style="
@@ -404,14 +323,17 @@
<div class="title"> <div class="title">
<span <span
>Table {{ index + 1 }} >Table {{ index + 1 }}
<span class="previewbox" @click="openPreview(index, 'table')"> <span class="previewbox" @click="openPreview(index, 'table',table)">
<el-tooltip class="item" effect="dark" :content="$t('commonTable.preview')" placement="top"> <el-tooltip class="item" effect="dark" :content="$t('commonTable.preview')" placement="top">
<i class="el-icon-view" style="font-size: 16px"></i> <i class="el-icon-view" style="font-size: 12px"></i>
</el-tooltip> </span </el-tooltip> </span
></span> ></span>
<p> <p>
<span style="" @click="edit(table, 'table')"> <span style="" @click="edit(table, 'table')">
<i class="el-icon-edit" style="color: #409eff; font-size: 16px"></i> <i class="el-icon-edit" style="color: #409eff; font-size: 12px"></i>
</span>
<span style="margin-left: 6px" @click="handledelete(table, 'table')">
<i class="el-icon-delete" style="color: #f56c6c; font-size: 12px"></i>
</span> </span>
</p> </p>
</div> </div>
@@ -454,7 +376,7 @@
@click=" @click="
table.has_selected == 1 table.has_selected == 1
? goToListComment(table.article_table_id, 'table') ? goToListComment(table.article_table_id, 'table')
: openPreview(index, 'table') : openPreview(index, 'table', table)
" "
style="width: 100%; height: 80px; display: flex; justify-content: center; align-items: center" style="width: 100%; height: 80px; display: flex; justify-content: center; align-items: center"
> >
@@ -537,13 +459,22 @@
</div> </div>
</div> </div>
</li> </li>
<li v-show="currentMenu == 4">
<slot name="catalogue1"></slot>
</li>
</ul> </ul>
</div> </div>
<DynamicTable
ref="myTableModal"
/>
</div> </div>
</template> </template>
<script> <script>
import DynamicTable from './DynamicTable.vue';
import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑 import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑
export default { export default {
props: ['articleId', 'imgWidth', 'imgHeight', 'scale', 'isEdit', 'isShowEdit', 'urlList', 'content'], props: ['articleId', 'imgWidth', 'imgHeight', 'scale', 'isEdit', 'isShowEdit', 'urlList', 'content'],
data() { data() {
@@ -556,7 +487,7 @@ export default {
comments: [], // 存储所有批注 comments: [], // 存储所有批注
isFresh: false, isFresh: false,
currentSelectType: '0', currentSelectType: '0',
currentMenu: 1, currentMenu: 4,
mediaUrl, mediaUrl,
@@ -574,6 +505,9 @@ export default {
activeNames: ['images', 'tables'] activeNames: ['images', 'tables']
}; };
}, },
components: {
DynamicTable
},
directives: { directives: {
// 注册一个局部的自定义指令 v-focus // 注册一个局部的自定义指令 v-focus
focus: { focus: {
@@ -695,6 +629,9 @@ export default {
edit(data, type) { edit(data, type) {
this.$emit('edit', data, type); this.$emit('edit', data, type);
}, },
handledelete(data, type) {
this.$emit('delete', data, type);
},
huifu(id) { huifu(id) {
this.$emit('huifu', id); this.$emit('huifu', id);
}, },
@@ -711,6 +648,23 @@ export default {
this.currentSelectType = v.type; this.currentSelectType = v.type;
this.filterData(); this.filterData();
}, },
replacement(type, id) {
if (type == 'img') {
const indexInList = this.images.findIndex((img) => img.ami_id == id);
if (indexInList !== -1) {
this.images.splice(indexInList, 1); // 从数组中彻底删除
}
this.filterData('img');
} else if (type == 'table') {
const indexInList = this.tables.findIndex((img) => img.amt_id == id);
if (indexInList !== -1) {
this.tables.splice(indexInList, 1); // 从数组中彻底删除
}
this.filterData('table');
}
},
filterData(type) { filterData(type) {
if (type) { if (type) {
if (type == 'img') { if (type == 'img') {
@@ -751,20 +705,113 @@ export default {
goToListComment(id, type) { goToListComment(id, type) {
this.$emit('goToListComment', id, type); this.$emit('goToListComment', id, type);
}, },
async refresh(type) { async reload(){
this.isFresh = false;
this.$nextTick(async () => { this.$nextTick(async () => {
if (type == 'img') {
await this.getWordimgList(); await this.getWordimgList();
await this.filterData();
} else if (type == 'table') {
await this.getWordTablesList(); await this.getWordTablesList();
await this.filterData(); await this.filterData();
})
},
async refresh(type, newData) {
var tableIndex;
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):[];
}
if(newData.ami_id) {
imgIndex = this.images.findIndex((img) => img.ami_id == newData.ami_id);
}
this.isFresh = false;
this.$nextTick(async () => {
switch (type) {
case 'addImg':
this.images.push({
...newData,
image: newData.url,
article_image_id: newData.ami_id
});
break;
case 'editImg':
if (imgIndex !== -1) {
this.images[imgIndex] = {
...newData,
image: newData.url,
article_image_id: newData.ami_id
};
}
break;
case 'addTable':
this.tables.push({
...newData,
article_table_id: newData.amt_id,
table: tableData
});
case 'editTable':
if (tableIndex !== -1) {
this.tables[tableIndex] = {
...newData,
article_table_id: newData.amt_id,
table: tableData
};
}
break;
case 'removeImg':
if (imgIndex !== -1) {
this.images[imgIndex].has_selected = 0;
}
break;
case 'positioningImg':
if (imgIndex !== -1) {
this.images[imgIndex].has_selected = 1;
}
break;
case 'removeTable':
if (tableIndex !== -1) {
this.tables[tableIndex].has_selected = 0;
}
break;
case 'positioningTable':
if (tableIndex !== -1) {
this.tables[tableIndex].has_selected = 1;
}
break;
} }
await this.filterData();
this.$forceUpdate(); this.$forceUpdate();
this.isFresh = true; this.isFresh = true;
// this.$emit('loaded', loadedthis.images);
}); });
}, },
changeDataIsHidden(val, status, type) { changeDataIsHidden(val, status, type) {
@@ -811,10 +858,9 @@ export default {
}); });
} }
}, },
openPreview(index, type) { openPreview(index, type,data) {
this.$nextTick(() => { this.$refs.myTableModal.open(type,data);
document.getElementById(type + '-modal-' + index).style.display = 'flex';
});
}, },
async getWordimgList() { async getWordimgList() {
var that = this; var that = this;
@@ -836,22 +882,13 @@ export default {
list = res.data.list; list = res.data.list;
} }
if (list.length > 0) {
list.forEach((img, index) => {
var extension = img.image.split('.').pop().toLowerCase();
// if (extension === 'tif' || extension === 'jpg' || extension === 'jpeg' || extension === 'png') {
const modalContent = `<img src="${this.mediaUrl + img.image}" alt="Image ${index}" style="width:100%;" >`;
this.$commonJS.createImageModal(index, modalContent, 'img');
// }
});
}
this.images = list; this.images = list;
this.$emit('loaded', this.images); this.$emit('loaded', this.images);
}); });
}, },
isHeaderRow(rowIndex, table) { isHeaderRow(rowIndex, table) {
var table =table; var table = table;
var head = table[0]; var head = table[0];
@@ -887,40 +924,7 @@ export default {
: []; : [];
} }
await this.filterData('table'); await this.filterData('table');
if (this.tables.length > 0) {
this.tables.forEach((table, index) => {
var modalContent = `
<div class="wordTableHtml" style="background:#FFF;padding:20px">
<table
border="1"
style="
border-collapse: collapse;
width: 100%;
text-align: center;
table-layout: auto;"
>`;
if (table.table && table.table.length > 0) {
table.table.forEach((row,i) => {
modalContent += `<tr class="${this.isHeaderRow(i, table.table)? 'table-header-row':'' }">`;
row.forEach((cell) => {
modalContent += `
<td
colspan="${cell.colspan || 1}"
rowspan="${cell.rowspan || 1}"
style=""
>
<span>${cell.text}</span>
</td>`;
});
modalContent += `</tr>`;
});
}
modalContent += `</table></div>`;
this.$commonJS.createImageModal(index, modalContent, 'table', '');
});
}
}); });
} }
}, },
@@ -968,7 +972,7 @@ export default {
border-bottom: 1px solid #000 !important; border-bottom: 1px solid #000 !important;
} }
.el-menu-vertical-demo { .el-menu-vertical-demo {
width: 85px; width: 60px;
} }
.el-menu-vertical-demo li { .el-menu-vertical-demo li {
display: flex; display: flex;
@@ -1000,7 +1004,7 @@ li {
.go-transition .title { .go-transition .title {
background-color: #e5e6eb; background-color: #e5e6eb;
color: #767c82; color: #767c82;
font-size: 13px; font-size: 12px;
padding: 2px 10px; padding: 2px 10px;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -1132,7 +1136,7 @@ li {
width: auto !important; width: auto !important;
} }
.double .go-transition .title { .double .go-transition .title {
font-size: 12px !important; font-size: 11px !important;
padding: 0 2px !important; padding: 0 2px !important;
line-height: 20px !important; line-height: 20px !important;
} }

View File

@@ -361,16 +361,16 @@ export default {
this.add_apply = 0; this.add_apply = 0;
break; break;
case 7||16: case 7:
// 7文章状态不在审稿中 16同意审稿后14天未进行审稿 // 7文章状态不在审稿中
this.add_apply = 1; this.add_apply = 1;
break; break;
case 8: case 8:
//拒绝审稿 //拒绝审稿
this.add_apply = -1; this.add_apply = -1;
break; break;
case 13: case 13||16:
//13 邀请审稿超过5天未同意邀请 //13 邀请审稿超过5天未同意邀请16同意审稿后14天未进行审稿
this.add_apply = 2; this.add_apply = 2;
break; break;
default: default: