H3+审稿人编委机构

This commit is contained in:
2026-02-04 14:57:54 +08:00
parent beee2ec54b
commit 9f108e7c81
18 changed files with 462 additions and 238 deletions

BIN
dist.zip

Binary file not shown.

BIN
node_modules.zip Normal file

Binary file not shown.

View File

@@ -19,8 +19,8 @@ const service = axios.create({
// baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换
// baseURL: 'http://www.tougao.com/', //测试本地 记得切换
// baseURL: 'http://192.168.110.110/tougao/public/index.php/',
// baseURL: '/api', //本地
baseURL: '/', //正式
baseURL: '/api', //本地
// baseURL: '/', //正式
});

View File

@@ -241,25 +241,29 @@ export default {
str = capitalizeFirstLetter(str);
// 添加蓝色标签
const regex = /\[(\d+(?:\d+)?(?:, ?\d+(?:\d+)?)*)\]/g;
// 1. 修改正则,只匹配不在 <blue> 标签内的 [数字]
// 这里的思路是:匹配 [内容],但通过逻辑过滤掉已经被包裹的情况
const regex = /\[(\d+(?:\d+)?(?:, ?\d+(?:\d+)?)*)\]/g;
str = str.replace(/<blue>/g, '').replace(/<\/blue>/g, ''); // 先去掉所有的 <blue> 标签
// 注意:不要在最上面执行 str.replace(/<blue>/g, ''),否则之前的标记就全白做了
if (regex.test(str)) {
str = str.replace(regex, function (match) {
// 提取出方括号中的内容,并进行匹配
const content = match.slice(1, match.length - 1); // 去掉方括号
// 这个需要程序去判断所以需要告诉我满足哪些条件的标蓝
// 上标中 只有 * # & 纯数字 纯数字逗号 纯数字逗号和空格 ỻ 标蓝
str = str.replace(regex, function (match, content, offset, fullString) {
// 【关键判断】:检查匹配位置的前后,是否已经存在 <blue> 和 </blue>
const prefix = fullString.substring(offset - 6, offset); // <blue> 是 6 位
const suffix = fullString.substring(offset + match.length, offset + match.length + 7); // </blue> 是 7 位
// 判断是否符合条件,纯数字、逗号后有空格、连字符
if (prefix === '<blue>' && suffix === '</blue>') {
return match; // 如果已经有标签了,原样返回,不重复标记
}
// 判断逻辑:纯数字、逗号空格、连字符
if (/^\d+$/.test(content) || /, ?/.test(content) || //.test(content)) {
return `<blue>${match}</blue>`; // 如果符合条件则加上蓝色标签
}
return match; // 如果不符合条件,则保持原样
});
return `<blue>${match}</blue>`;
}
return match;
});
@@ -907,9 +911,9 @@ export default {
});
// 2. 删除所有不需要的标签 (除 `strong`, `em`, `sub`, `sup`, `b`, `i` 外的所有标签)
if (type == 'table') {
inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|img|myfigure|mytable))[^>]+>/g, ''); // 删除不需要的标签
inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|img|myfigure|mytable|myh3))[^>]+>/g, ''); // 删除不需要的标签
} else {
inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|myfigure|mytable))[^>]+>/g, ''); // 删除不需要的标签
inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|myfigure|mytable|myh3))[^>]+>/g, ''); // 删除不需要的标签
}
@@ -963,14 +967,11 @@ export default {
return parsedData;
}
,
},
async parseTableToArray(tableString, callback) {
const parser = new DOMParser();
const doc = parser.parseFromString(tableString, 'text/html');
const rows = doc.querySelectorAll('table tr'); // 获取所有的行(<tr>
@@ -993,6 +994,8 @@ export default {
);
callback(result)
// 返回处理后的数组
},

View File

@@ -426,6 +426,7 @@ const en = {
jump: 'Locate',
editAssociation: 'Edit Association',
UnbindAssociation: 'Unbind Association',
selectContent: 'Please select the content to operate first',
},
pendingPayment: {
title: 'Title',

View File

@@ -412,6 +412,7 @@ const zh = {
jump: '定位',
editAssociation: '编辑关联',
UnbindAssociation: '取消关联',
selectContent: '请先选择需要操作的内容',
},
pendingPayment: {
title: 'Title',

View File

@@ -88,7 +88,6 @@
</div>
</div>
<div style="width: 100%; width: calc(100% - 260px); float: right; height: calc(100% - 0px); background-color: #e4e9ed">
<common-word
:articleId="articleId"
v-if="htmlContent"
@@ -369,6 +368,7 @@
<font style="color: #f56c6c; margin-right: 5px">*</font>
Content :
</span>
<common-content
type="content"
@openAddTable="openAddTable"
@@ -595,12 +595,10 @@ export default {
}
},
async created() {
localStorage.removeItem('scrollPosition');
this.isShowEditComment();
this.getDate();
this.getCommentList();
},
mounted() {
document.addEventListener('copy', (event) => {
@@ -613,10 +611,9 @@ export default {
console.log('用户复制了内容:', selection);
// 阻止默认行为(如果需要自定义复制逻辑的话)
// event.preventDefault();
});
});
},
async activated() {
this.isShowEditComment();
this.getDate();
this.getCommentList();
@@ -690,6 +687,7 @@ export default {
async getContent(type, content) {
if (type == 'content') {
content = this.$commonJS.transformHtmlString(content);
var div = document.createElement('div');
div.innerHTML = content; // 将 HTML 字符串加载到 div 中
// 替换所有 <strong> 为 <b>
@@ -722,7 +720,7 @@ export default {
return false;
}
var list = this.$commonJS.cleanAndParseWordContent(content);
console.log('list at line 569:', list);
this.saveContentList(list, this.currentId);
} else if (type == 'table') {
@@ -771,6 +769,8 @@ export default {
},
async saveContent(content, am_id) {
const loading = this.$loading({
lock: true,
text: 'Loading...',
@@ -780,6 +780,9 @@ export default {
var that = this;
var str = content.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1').trim();
if (str.replace(/<br\s*\/?>/gi, '').trim() === '') {
str = '';
}
str = await that.$commonJS.decodeHtml(str);
await that.$api
@@ -816,6 +819,14 @@ export default {
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
content = content.map((str) => {
// 逻辑:去掉所有换行符后,如果剩下的内容是空的
if (str.replace(/<br\s*\/?>/gi, '').trim() === '') {
return ''; // 变成真正的空字符串
}
return str; // 如果有文字,保留原样
});
await this.$api
.post('api/Preaccept/addMoreRow', {
article_id: this.articleId,
@@ -846,7 +857,6 @@ export default {
return rowIndex < head[0].rowspan; // 假设前两行是表头
},
deleteComment(comment, index) {
console.log('comment at line 480:', comment);
if (this.isEditComment) {
this.$confirm(this.$t('commonTable.removeAnnotations'), 'Prompt', {
confirmButtonText: 'Submit',
@@ -872,7 +882,7 @@ export default {
}
},
saveLateX(data) {
console.log('data at line 735:', data);
// 1. 从 data 中解构出 wrap (或者你命名的模式变量)
const { editorId, wmathId, latex, wrap } = data;
const newLatex = latex ? latex.trim() : '';
@@ -910,7 +920,7 @@ export default {
}, 10);
}
}
},
},
async huifu(id) {
var that = this;
await this.$confirm(this.$t('commonTable.reContent'), 'Prompt', {
@@ -1094,16 +1104,13 @@ export default {
});
},
async changeSort(type, id) {
var that = this;
const index = this.Main_List.findIndex((item) => item.am_id == id);
if(type=='up'&&index==0){
return
if (type == 'up' && index == 0) {
return;
}
if(type=='down'&&index==this.Main_List.length-1){
return
if (type == 'down' && index == this.Main_List.length - 1) {
return;
}
const load = this.$loading({
lock: true,
@@ -1131,7 +1138,7 @@ export default {
});
},
async addCommentSetting(content) {
console.log('content at line 602:', content);
await this.$api
.post(this.urlList.addComment, {
am_id: content.am_id,
@@ -1154,7 +1161,7 @@ export default {
});
},
async addComment(content) {
console.log('content at line 603:', this.commentForm);
var str = content.replace(/^<p>(.*?)<\/p>$/, '$1') ? content.replace(/^<p>(.*?)<\/p>$/, '$1') : '';
if (str == '') {
this.$message({
@@ -1237,13 +1244,13 @@ export default {
this.getCommentList();
},
editComment(comment, type) {
console.log('comment at line 813:', comment);
this.commentForm = {
...comment,
type: type,
remark: type == 'user' ? comment.author_remark : comment.remark
};
console.log('this.commentForm at line 815:', this.commentForm);
this.commentVisible = true;
},
async onAddComment(data) {
@@ -1288,7 +1295,6 @@ export default {
break;
}
console.log('data at line 739:', data);
await this.$api
.post(url, {
am_id: data.mainId
@@ -1330,21 +1336,19 @@ export default {
return;
}
if (index !== -1 && resData) {
var newData = Array.isArray(resData) ? resData : (resData ? [resData] : []);
const updatedItems= newData.map((item) => ({
var newData = Array.isArray(resData) ? resData : resData ? [resData] : [];
const updatedItems = newData.map((item) => ({
...item,
checked: false,
getnum: 0,
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'){
} else if (type == 'positioningImg' || type == 'positioningTable') {
this.Main_List.splice(index + 1, 0, ...updatedItems);
}
else {
} else {
this.$set(this.Main_List, index, updatedItems[0]);
}
} else {
@@ -1366,7 +1370,7 @@ export default {
});
// 处理文件上传并传递回调函数
this.$commonJS.handleFileUpload(event, function (tables) {
console.log('tables at line 1138:', tables);
if (tables.length == 0) {
loading.close();
that.$message({
@@ -1614,8 +1618,7 @@ export default {
}
},
updateChange(content, type) {
// console.log('content at line 1154:', content);
// console.log('content at line 976:', content);
var str = this.$commonJS.transformHtmlString(content);
if (type == 'imgNote') {
this.picStyle1.note = str;
@@ -1685,28 +1688,29 @@ export default {
const draggedImage = JSON.parse(event.dataTransfer.getData('image'));
const draggedImageIndex = JSON.parse(event.dataTransfer.getData('imageIndex'));
this.$nextTick(async () => {
await this.$api
.post(this.urlList.setPositioningImage, {
am_id: dataId,
ami_id: draggedImage.ami_id
})
.then(async (res) => {
if (res.code == 0) {loading.close()
this.refreshCurrentContent('positioningImg',dataId, res.data);
if (res.code == 0) {
loading.close();
this.refreshCurrentContent('positioningImg', dataId, res.data);
this.$nextTick(() => {
this.$refs.commonWordHtmlTypeSetting.refresh('positioningImg', res.data);
});
this.$forceUpdate();
} else {loading.close()
} else {
loading.close();
this.$message.error(res.msg);
}
})
.catch((err) => {loading.close()
.catch((err) => {
loading.close();
this.$message.error(err.msg);
});
});
} else {
const draggedtable = JSON.parse(event.dataTransfer.getData('table'));
@@ -1717,27 +1721,24 @@ export default {
amt_id: draggedtable.amt_id
})
.then(async (res) => {
if (res.code == 0) {loading.close()
this.refreshCurrentContent('positioningTable',dataId, res.data);
if (res.code == 0) {
loading.close();
this.refreshCurrentContent('positioningTable', dataId, res.data);
this.$nextTick(() => {
this.$refs.commonWordHtmlTypeSetting.refresh('positioningTable', res.data);
});
this.$forceUpdate();
} else {
loading.close()
loading.close();
this.$message.error(res.msg);
}
})
.catch((err) => {
loading.close()
loading.close();
this.$message.error(err.msg);
});
});
}
},
getCommentList() {
this.$api
@@ -1912,8 +1913,11 @@ export default {
loading.close();
this.$message.success('Successfully edit Figure!');
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.$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.getCommentList();
this.$forceUpdate();
@@ -1942,7 +1946,6 @@ export default {
this.pictVisible = false;
this.$refs.commonWordHtmlTypeSetting.refresh('addImg', res.data);
} else {
loading.close();
this.$message.error(res.msg);
@@ -2026,8 +2029,11 @@ export default {
this.$message.success('Successfully edit Table!');
this.threeVisible = false;
setTimeout(() => {
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.$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();
});
} else {
@@ -2571,9 +2577,8 @@ export default {
background-color: #0066990d;
/* display: block !important; */
}
wmath[data-wrap="inline"] {
wmath[data-wrap='inline'] {
display: inline-block !important;
width: auto !important;
}
</style>

View File

@@ -721,7 +721,7 @@ export default {
return false;
}
var list = this.$commonJS.cleanAndParseWordContent(content);
console.log('list at line 569:', list);
this.saveContentList(list, this.currentId);
} else if (type == 'table') {
@@ -1162,7 +1162,7 @@ export default {
break;
}
console.log('data at line 739:', data);
await this.$api
.post(url, {
am_id: data.mainId

View File

@@ -1881,8 +1881,37 @@ export default {
.catch(() => {});
},
handleClickFinal(data) {
this.finalDecisionData = { ...data };
console.log("🚀 ~ handleClickFinal ~ data:", data);
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)',
zIndex: 9999 // 设置一个足够高的层级
});
this.$api
.post('api/Finalreview/getById', {
record_id: data.id,
reviewer_id:data.reviewer_id,
})
.then((res) => {
if (res.status == 1) {
loading.close()
this.finalDecisionData = { ...res.data,reviewer_company : res.data.company,reviewer_email:res.data.email}
this.FinalDecisionVisible = true;
} else {
loading.close()
this.$message.error(res.msg);
}
})
.catch((err) => {
loading.close()
console.log(err);
});
},
handleClickRemark() {
this.expanded = !this.expanded;

View File

@@ -2982,11 +2982,12 @@ export default {
// 获取文章信息
this.$api
.post('api/Preaccept/getArticleMains', {
p_article_id: this.p_article_id
article_id: this.detailMes.article_id
})
.then((res) => {
if (res.code == 0) {
this.deMesYul.htmlList = res.data.mains;
this.deMesYul.htmlList = res.data.list;
this.preArtVisible = true;
} else {
this.$message.error(res.msg);

View File

@@ -648,7 +648,7 @@ export default {
break;
}
console.log('data at line 739:', data);
await this.$api
.post(url, {
am_id: data.mainId

View File

@@ -5,13 +5,25 @@
<el-row :gutter="24">
<el-col :span="24">
<div class="form-box" style="width: 100%">
<el-form ref="articleform" :model="detailDate" label-width="200px">
<el-form ref="articleform" :model="detailDate" label-width="220px">
<!-- <el-form-item label="Article : ">
<span>{{ detailDate.article }}</span>
</el-form-item> -->
<el-form-item label="Editorial Board Member : ">
<span>{{ detailDate.reviewer }}</span>
</el-form-item>
<el-form-item label="Editorial Board Member Email : ">
<span>{{ detailDate.article_final.reviewer_email }}</span>
</el-form-item>
<el-form-item label="Affiliation : " v-if="detailDate.article_final.reviewer_company">
<span
>
{{ detailDate.article_final.reviewer_company }}
</span>
</el-form-item>
<el-form-item label="Final decision time : ">
<span>{{ detailDate.ctime }}</span>
</el-form-item>

View File

@@ -1,6 +1,7 @@
<template>
<div class="tinymce-container editor-container">
<textarea class="tinymce-textarea" :id="tinymceId"></textarea>
</div>
</template>
@@ -69,6 +70,7 @@ export default {
data() {
return {
uploadNotifications: {},
content: '',
totalUploadImages: 0,
uploadedImageCount: 0,
uploadNotificationInstance: null, // 全局通知实例
@@ -132,6 +134,8 @@ export default {
// console.log('val at line 208:', val);
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => {
window.tinymce.get(this.tinymceId).setContent(val);
// window.renderMathJax(); // 主动触发 MathJax 渲染
});
@@ -409,11 +413,38 @@ uploadSingleImage(blob, index) {
return new Blob([u8arr], { type: mime });
},
initTinymce() {
if (this.value.includes('wordTableHtml')) {
const parser = new DOMParser();
const doc = parser.parseFromString(this.value, 'text/html');
// 1. 只针对单元格 (td, th) 进行处理
const cells = doc.querySelectorAll('td, th');
cells.forEach(cell => {
let html = cell.innerHTML;
// 执行你原来的清理逻辑,但仅限单元格内部
html = html.replace(/<([a-zA-Z1-6]+)\b[^>]*><\/\1>/g, '') // 清理空标签
.replace(/\s+(?=<)|(?<=>)\s+/g, '&nbsp;'); // 补全空格
cell.innerHTML = html;
});
// 2. 将处理后的完整 HTML 重新赋回,此时 table 外部的换行符保持原样(纯文本)
this.content = doc.body.innerHTML;
} else {
// 非表格模式,维持你原有的全局逻辑
this.content = this.value
.replace(/<([a-zA-Z1-6]+)\b[^>]*><\/\1>/g, '')
.replace(/\s+(?=<)|(?<=>)\s+/g, '&nbsp;');
}
let globalImgCounter = 0; // 放在 init 闭包内,确保计数器持续增长
let currentPasteBase64Images = [];
var _this = this;
window.tinymce.init({
..._this.tinymceOtherInit,
trim_span_elements: false, // 禁止修剪内联标签周围的空格
extended_valid_elements: 'blue[*]',
custom_elements: 'blue',
valid_children: '+blue[#text|i|em|b|strong|span],+body[blue],+p[blue]',
@@ -453,6 +484,9 @@ myfigure *,
color: inherit !important;
text-shadow: inherit !important;
background: transparent !important; /* 防止内部标签背景干扰 */
}
myh3 ,myh3 *{
font-weight: bold !important;
}
@keyframes blueGlow {
@@ -572,6 +606,8 @@ myfigure *,
});
ed.on('paste', async (event) => {
const rtf = event.clipboardData.getData('text/rtf');
if (rtf && rtf.includes('\\pict')) {
@@ -604,7 +640,7 @@ myfigure *,
await _this.uploadSingleImage(imageBlob, i);
_this.updateUploadProgressNotification(i, 'success');
} catch (err) {
} catch (err) {
console.error('Upload failed:', err);
_this.updateUploadProgressNotification(i, 'fail', err.message || 'Network error'); // 上传失败
}
@@ -644,7 +680,9 @@ myfigure *,
const editorBody = ed.getBody();
const observer = new MutationObserver(() => {
const currentContent = ed.getContent();
const currentContent = ed.getContent({ format: 'raw' });
if (_this.isAutomaticUpdate) {
_this.$emit('updateChange', currentContent);
// _this.$emit('updateChange', _this.$commonJS.decodeHtml(currentContent));
@@ -674,7 +712,7 @@ myfigure *,
text: _this.$t('commonTable.exportWord'),
onAction: () => {
// 插入自定义表格到编辑器中
let content = ed.getContent(); // 获取内容
let content = ed.getContent({ format: 'raw' }); // 获取内容
content = content.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
content = content.replace(/<em>/g, '<i>').replace(/<\/em>/g, '</i>');
const container = document.createElement('div');
@@ -688,7 +726,7 @@ myfigure *,
text: _this.$t('commonTable.exportImg'),
onAction: () => {
// 插入自定义表格到编辑器中
_this.export('image', ed.getContent());
_this.export('image', ed.getContent({ format: 'raw' }));
}
});
ed.ui.registry.addContextToolbar('spacer', {
@@ -705,6 +743,7 @@ myfigure *,
});
e.content = e.content.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
e.content = e.content.replace(/<em>/g, '<i>').replace(/<\/em>/g, '</i>');
});
ed.on('GetContent', function (e) {
e.content = e.content.replace(/<b>/g, '<strong>').replace(/<\/b>/g, '</strong>');
@@ -818,22 +857,30 @@ console.log('Processed content:', content); // 输出处理后的内容
vm.onClear();
},
init_instance_callback: (editor) => {
if (_this.value) {
editor.setContent('<p>' + _this.value + '</p>');
if (_this.content) {
let finalContent = _this.content;
if (finalContent.includes('wordTableHtml')) {
editor.setContent(finalContent);
} else {
editor.setContent('<p>' + finalContent + '</p>');
}
setTimeout(() => {
window.renderMathJax(_this.tinymceId); // 初始化时渲染 MathJax
}, 10);
if (window.renderMathJax) {
window.renderMathJax(_this.tinymceId);
}
}, 10);
}
_this.hasInit = true;
editor.on('NodeChange Change KeyUp SetContent', () => {
this.hasChange = true;
this.$emit('input', editor.getContent());
this.$emit('input', editor.getContent({ format: 'raw' }));
// console.log('at line 518:', '改变');
// setTimeout(() => {
// window.renderMathJax(); // 初始化时渲染 MathJax
// }, 50);
});
}
});
@@ -932,11 +979,14 @@ console.log('Processed content:', content); // 输出处理后的内容
},
//获取内容
async getContent(type) {
var content = window.tinymce.get(this.tinymceId).getContent();
var content = window.tinymce.get(this.tinymceId).getContent({ format: 'raw' });
content = content.replace(/<span[^>]*>/g, '').replace(/<\/span>/g, ''); // 去除span标签
content = content.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
content = content.replace(/<em>/g, '<i>').replace(/<\/em>/g, '</i>');
content = content.replace(/&nbsp;/g, ' '); // 将所有 &nbsp; 替换为空格
this.$emit('getContent', type, content);
},

View File

@@ -20,20 +20,27 @@
<el-row :gutter="10">
<el-col :span="24">
<div class="form-box" style="width: 100%">
<el-form ref="articleform" :model="detailDate" label-width="140px">
<el-form-item label="Title">
<el-form ref="articleform" :model="detailDate" label-width="160px">
<el-form-item label="Title : ">
<span style="font-weight: bold;color: #333;">{{ detailDate.article }}</span>
</el-form-item>
<el-form-item label="Reviewer">
<el-form-item label="Reviewer : ">
<span>{{ detailDate.reviewer }}</span>
</el-form-item>
<el-form-item label="Reviewer email">
<el-form-item label="Reviewer email : ">
<span>{{ detailDate.reviewer_email }}</span>
</el-form-item>
<el-form-item label="CreateTime">
<el-form-item label="Affiliation : ">
<p>
{{ detailDate.reviewer_company }}
</p>
</el-form-item>
<el-form-item label="CreateTime : ">
<span>{{ formatDate(detailDate.ctime) }}</span>
</el-form-item>
<el-form-item label="Disclose name or anonymous" label-width="200px" v-if="reviewerDetail.state != 0">
<el-form-item label="Disclose name or anonymous : " label-width="210px" v-if="reviewerDetail.state != 0">
<span v-if="detailDate.is_anonymous == 0">Disclose name</span>
<span v-if="detailDate.is_anonymous == 1">Remain anonymous</span>
</el-form-item>
@@ -144,6 +151,7 @@ export default {
article: '',
reviewer: '',
reviewer_email: '',
reviewer_company:'',
articlefile: '',
articlezip: '',
ctime: '',
@@ -331,6 +339,7 @@ export default {
this.detailDate.article = res.data.article.title;
this.detailDate.reviewer = res.data.article_reviewer.realname;
this.detailDate.reviewer_email = res.data.article_reviewer.email;
this.detailDate.reviewer_company = res.data.article_reviewer.company;
this.detailDate.ctime = res.data.article_reviewer.ctime;
this.detailDate.is_anonymous = res.data.article_reviewer.is_anonymous;

View File

@@ -58,7 +58,7 @@ export default {
if (this.lineStyle.table) {
this.tableData = [...this.lineStyle.table];
var modalContent = `
<div class="wordTableHtml" >
<div class="wordTableHtml">
<table
border="1"
style="
@@ -84,6 +84,10 @@ export default {
modalContent += `</table></div>`;
this.updatedHtml = modalContent;
this.$nextTick(() => {
this.$refs.tinymceChild1.initTinymce()
})
}
} else {
this.updatedHtml = '';
@@ -106,10 +110,15 @@ export default {
this.$refs.tinymceChild1.getContent(type);
},
getContent(type, content) {
if (content) {
const container = document.createElement('div');
container.innerHTML = content;
this.$commonJS.parseTableToArray(content, (table) => {
this.$emit('getContent', type, { html_data: content, table: table });
});
} else {

View File

@@ -1,7 +1,7 @@
<template>
<div
@click="handleGlobalClick"
class="tinymce-container editor-container word-container"
class="tinymce-container editor-container word-container" :class="{'preview-container': isPreview}"
:style="!isPreview ? 'padding:10px 20px 40px 24px;' : 'padding:0px;'"
ref="scrollDiv"
>
@@ -181,24 +181,23 @@
</span>
<div
:class="
!isPreview
? item.is_h1
item.is_h1
? 'isTitleH1 pMainH1'
: item.is_h2
? 'isTitleH2'
: item.is_h3
? 'isTitleH3'
: ''
: item.is_h1
? 'Ptitle'
: ''
"
style="line-height: 24px"
>
<template v-if="!isPreview">
<span class="Htitle Htitle1" v-if="item.is_h1 == 1">H1</span>
<span class="Htitle Htitle2" v-else-if="item.is_h2 == 1">H2</span>
<span class="Htitle Htitle3" v-else-if="item.is_h3 == 1">H3</span></template
<span class="Htitle Htitle3" v-else-if="item.is_h3 == 1">H3</span>
</template
>
<div :class="currentId == item.am_id ? 'glowing-border' : ''" style="position: relative">
@@ -967,7 +966,7 @@
<div v-if="currentData.type == 0" class="h-group">
<span :class="['h-item', { active: currentData.is_h1 == 1 }]" @click="changeTitle(currentData.is_h1 ? 0 : 1)">H1</span>
<span :class="['h-item', { active: currentData.is_h2 == 1 }]" @click="changeTitle(currentData.is_h2 ? 0 : 2)">H2</span>
<span :class="['h-item', { active: currentData.is_h3 == 1 }]" @click="changeTitle(currentData.is_h3 ? 0 : 3)">H3</span>
<span :class="['h-item', { active: currentData.is_h3 == 1||currentTag == 'h3' }]" @click="changeTitle3(currentData.is_h3||currentTag == 'h3' ? 0 : 3)">H3</span>
</div>
<div class="row-divider" v-if="currentData.type == 0"></div>
<div v-if="isEditComment && currentData.type == 0" class="menu-item ai-feat" @click="menuAction('proofreading')">
@@ -978,19 +977,19 @@
</div>
<div class="row-divider" v-if="isEditComment && currentData.type == 0"></div>
<div class="menu-item menu-link" v-if="currentData.type == 0 && !currentTag" @click="menuAction('link')">
<div class="menu-item menu-link" v-if="currentData.type == 0 && !['figure', 'table'].includes(currentTag)" @click="menuAction('link')">
<i class="el-icon-link"></i><span>{{ $t('commonTable.Association') }}</span>
</div>
<div class="menu-item menu-link menu-jump" v-if="currentData.type == 0 && currentTag" @click="menuAction('jump')">
<div class="menu-item menu-link menu-jump" v-if="currentData.type == 0 && ['figure', 'table'].includes(currentTag)" @click="menuAction('jump')">
<i class="el-icon-location-outline"></i><span style="margin-right: 10px">{{ $t('commonTable.jump') }}</span>
</div>
<div class="menu-item menu-link" v-if="currentData.type == 0 && currentTag" @click="menuAction('editLink')">
<div class="menu-item menu-link" v-if="currentData.type == 0 && ['figure', 'table'].includes(currentTag)" @click="menuAction('editLink')">
<i class="el-icon-link"></i><span>{{ $t('commonTable.editAssociation') }}</span>
</div>
<div
class="menu-item menu-link"
style="color: #f56c6c"
v-if="currentData.type == 0 && currentTag"
v-if="currentData.type == 0 && ['figure', 'table'].includes(currentTag)"
@click="menuAction('unbindLink')"
>
<i class="el-icon-link"></i><span>{{ $t('commonTable.UnbindAssociation') }}</span>
@@ -1275,6 +1274,7 @@ export default {
this.$refs.scrollDiv.addEventListener('scroll', this.divOnScroll, { passive: true });
document.addEventListener('selectionchange', () => {
if(this.isPreview)return;
const selection = window.getSelection();
if (selection.rangeCount === 0) return;
@@ -1475,6 +1475,7 @@ export default {
this.$forceUpdate();
},
handleGlobalClick(e) {
if(this.isPreview)return;
// 如果点在气泡内,不关
if (e.target.closest('.bubble-container')) return;
@@ -1787,7 +1788,6 @@ export default {
const el = Array.isArray(r) ? r[0] : r;
if (!sc || !el) return;
const top = this.topInScroll(el, sc) - 80;
console.log('top at line 1491:', top);
sc.scrollTo({ top, behavior: 'smooth' });
},
@@ -1817,7 +1817,6 @@ export default {
return doc.body.innerHTML;
},
async editProofreadingContent(data) {
console.log('data at line 1276:', data);
data.revised_content = await this.convertSpacesToNbsp(data.revised_content);
this.proofreadingContent = {
@@ -1898,7 +1897,7 @@ export default {
container.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
},
handleClick(tab, event) {
console.log(tab, event);
if (this.isEditComment) {
if (tab.index == 0) {
this.activeName == 'proofreading';
@@ -2001,7 +2000,7 @@ export default {
},
changeSort(type) {
console.log('type at line 782:', type);
if (this.currentId) {
this.$emit('changeSort', type, this.currentId);
}
@@ -2130,6 +2129,7 @@ export default {
// 3. 核心调用逻辑
async handleOpenLink() {
// 1. 校验是否选中内容
const selectedText = this.currentSelection.label || '';
if (!selectedText) {
@@ -2234,12 +2234,81 @@ export default {
}
},
changeTitle3(value) {
if (this.currentData.type !== 0) return;
if (value == 3) {
const label = this.currentSelection.label || '';
if (!label) {
this.$message.error(this.$t('commonTable.selectContent'));
return;
}
let originalContent = this.currentSelection.content || '';
// 1. 【清洗阶段】:先彻底清除 content 里的所有 myh3 痕迹(修复孤儿标签)
let workingContent = originalContent.replace(/<\/?myh3[^>]*>/gi, '');
// 2. 【归一化匹配】:处理转义字符冲突 (&lt; vs <)
// 我们不生成复杂的穿透正则,而是将内容和标签都转为纯文本比对
const decodeHTML = (str) => {
const txt = document.createElement("textarea");
txt.innerHTML = str;
return txt.value;
};
const plainLabel = decodeHTML(label);
const plainContent = decodeHTML(workingContent);
// 寻找文字在纯文本中的起始位置
const startIndex = plainContent.indexOf(plainLabel);
if (startIndex !== -1) {
/**
* 3. 【精准爆破】:
* 因为 HTML 标签和实体字符会改变字符串长度,我们不能直接按索引切。
* 我们用一个简单的正则,只处理 label 里的特殊符号和中间可能存在的标签。
*/
const safeLabelRegexStr = plainLabel
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
.replace(/\s+/g, '[\\s\\S]*?'); // 将空格替换为模糊匹配,允许中间夹杂任何内容(标签)
const finalRegex = new RegExp(safeLabelRegexStr, 'g');
// 执行替换
const newContent = workingContent.replace(finalRegex, (match) => {
// 如果已经有标签包着,直接包在最外面
return `<myh3>${match}</myh3>`;
});
this.$emit('saveContent', newContent, this.currentSelection.mainId);
} else {
console.warn('匹配失败,尝试直接字符串替换');
// 最后的兜底:如果正则还是不行,直接强行替换 label
const fallbackContent = workingContent.split(label).join(`<myh3>${label}</myh3>`);
this.$emit('saveContent', fallbackContent, this.currentSelection.mainId);
}
} else {
// 删除逻辑:简单直接
const rootItem = this.wordList.find((item) => item.am_id == this.currentTagData.main_id);
if (rootItem) {
const finalContent = rootItem.content.replace(/<\/?myh3[^>]*>/gi, '');
this.$emit('saveContent', finalContent, this.currentTagData.main_id);
}
}
},
changeTitle(value) {
if (this.currentData.type == 0) {
this.$emit('onEditTitle', {
mainId: this.currentId,
value: value
});
}
},
clearHighlight1() {
@@ -2319,7 +2388,7 @@ export default {
revokeProofreading(data) {
// this.$emit('revokeProofreading', data);
var content = this.wordList.find((e) => e.am_id == data.am_id).content;
console.log('content at line 1890:', content);
this.$api
.post('api/Proofread/change', {
am_id: data.am_id,
@@ -2344,10 +2413,10 @@ export default {
});
},
executeProofreading(data) {
console.log('data at line 1416:', data);
// this.$emit('executeProofreading', data);
var content = this.wordList.find((e) => e.am_id == data.am_id).content;
console.log('content at line 1890:', content);
this.$api
.post('api/Proofread/change', {
am_id: data.am_id,
@@ -2370,7 +2439,7 @@ export default {
});
},
refreshCurrentData(am_id, newData, type) {
console.log('newData at line 2106:', newData);
var refreshData = { ...this.wordList.find((e) => e.am_id == am_id) };
var refreshDataIndex = this.wordList.findIndex((e) => e.am_id == am_id);
if (refreshData.type == 0) {
@@ -2391,10 +2460,10 @@ export default {
}
//图片的注解 表格标题和注解 还有表格要单独处理
console.log('refreshData at line 1293:', refreshData);
},
deleteProofreading(data, index) {
console.log('data at line 1599:', data);
// this.$emit('deleteProofreading', data);
if (this.isEditComment) {
this.$confirm(this.$t('commonTable.removeProofread'), 'Prompt', {
@@ -2470,7 +2539,7 @@ export default {
this.$emit('onDrop', e, targetId.slice(6)); // 阻止默认的行为
},
clearEditor(editorId) {
console.log('editorId at line 462:', editorId);
for (const key in this.editors) {
if (this.editors[key]) {
// 确保销毁所有编辑器实例
@@ -2732,20 +2801,11 @@ export default {
if (event && event.stopPropagation) {
event.stopPropagation();
}
if (!this.isPreview) {
this.clearHighlight();
this.selectedIds = [];
this.menuType = type;
this.currentTag = '';
this.currentTagData = null; // 必须重置,防止带入旧数据
this.$set(this, 'currentId', id);
this.currentIndex = index;
this.currentData = data;
const clickedTag = event.target.closest('myfigure, mytable');
const clickedTag = event.target.closest('myfigure, mytable, myh3');
if (clickedTag) {
this.currentTag = clickedTag;
this.currentTag = clickedTag.tagName.toLowerCase().replace('my', '');
this.currentTagData = {
link_id: clickedTag.getAttribute('data-id'),
link_type: clickedTag.tagName.toLowerCase().replace('my', ''),
@@ -2753,7 +2813,20 @@ export default {
fullHtml: clickedTag.outerHTML,
main_id: id
};
console.log('6666666666666666',this.currentTagData);
}
if (!this.isPreview) {
this.clearHighlight();
this.selectedIds = [];
this.menuType = type;
this.$set(this, 'currentId', id);
this.currentIndex = index;
this.currentData = data;
this.updateBubblePosition(event);
this.isMenuVisible = true;
@@ -2761,6 +2834,11 @@ export default {
await this.getProofreadingList([id]); // 建议 await 异步操作
this.getPList(id);
}
}else{
if (clickedTag) {
this.handleClickJump();
}
}
setTimeout(() => {
this.isInternalAction = false;
@@ -2786,12 +2864,12 @@ export default {
this.$emit('editComment', data, type);
},
getCommentRemark(data) {
console.log('data at line 426:', data);
const items = this.comments[data.am_id];
// 如果找到了对应的数据项
if (items) {
console.log(items);
return items;
} else {
}
@@ -2816,7 +2894,7 @@ export default {
return formattedDate;
},
async goToListComment(data, fn) {
console.log('data at line 993:', data);
await this.goToComment(data.am_id, fn);
},
divOnScroll() {
@@ -3359,11 +3437,10 @@ export default {
changeTable() {
// 获取所有表格
const tables = window.tinymce.get(this.tinymceId).getBody().querySelectorAll('table');
console.log('tables at line 110:', tables);
// 遍历并设置样式
tables.forEach((table) => {
console.log('table at line 360:', table);
const editor = window.tinymce.get(this.tinymceId);
editor.dom.setStyles(table, {
width: this.typesettingType == 1 ? '17.18cm' : '25.88cm'
@@ -3405,11 +3482,11 @@ export default {
this.selectedComment = null;
},
replacePlaceholderImage(ami_id, realImgSrc) {
console.log('realImgSrc at line 638:', ami_id, realImgSrc);
const iframeDocument = window.tinymce.get(this.tinymceId).getDoc();
console.log('iframeDocument at line 639:', iframeDocument);
const placeholderImg = iframeDocument.querySelector(`img[data-img-id="${ami_id}"]`);
console.log('placeholderImg at line 640:', placeholderImg);
if (placeholderImg) {
placeholderImg.src = realImgSrc; // 替换图片 URL
@@ -3443,7 +3520,7 @@ export default {
//获取内容
getContent1(type, content) {
console.log('content at line 2986:', content);
content = this.$commonJS.transformHtmlString(content);
var div = document.createElement('div');
@@ -3465,7 +3542,7 @@ export default {
// 获取最终修改后的 HTML
content = div.innerHTML;
console.log('content at line 3007:', content);
this.$api
.post('api/Proofread/modify', {
am_id: this.proofreadingContent.am_id,
@@ -4318,7 +4395,12 @@ wmath {
text-shadow: inherit !important;
background: transparent !important; /* 防止内部标签背景干扰 */
}
::v-deep myh3 {
font-weight: bold !important;
}
::v-deep myh3 *{
font-weight: bold !important;
}
@keyframes blueGlow {
0%,
100% {
@@ -4347,4 +4429,11 @@ wmath {
display: inline-block !important;
width: auto !important;
}
.preview-container{
::v-deep myfigure,
::v-deep mytable {
text-shadow: none !important
}
}
</style>

View File

@@ -154,8 +154,10 @@
class="status ok"
:class="scope.row.refer_type == 'journal' ? getJournalDateno(scope.row.dateno, 'status') : ''"
v-if="
(scope.row.refer_type == 'journal' && scope.row.doilink != ''&&scope.row.cs==1) ||
(scope.row.refer_type == 'book' && scope.row.isbn != ''&&scope.row.cs==1)
(
(scope.row.refer_type == 'journal' && scope.row.doilink != '' && scope.row.cs == 1) ||
(scope.row.refer_type == 'book' && scope.row.isbn != '' && scope.row.cs == 1)
) && scope.row.retract == 0
"
>
<i class="el-icon-circle-check"></i>
@@ -171,7 +173,7 @@
<!-- journal 形式 -->
<div style="text-align: left" v-if="scope.row.refer_type == 'journal'" class="reference-item">
<p>
{{ scope.row.author }}&nbsp;{{ scope.row.title }}. &nbsp;<em>{{ scope.row.joura }}</em
{{ scope.row.author }}&nbsp;<span v-html="formatTitle(scope.row.title)"></span>. &nbsp;<em>{{ scope.row.joura }}</em
>.&nbsp;<span :class="getJournalDateno(scope.row.dateno, 'title')">{{ scope.row.dateno }}</span
>.<br />
</p>
@@ -179,12 +181,13 @@
</div>
<!-- book 形式 -->
<div style="text-align: left" v-if="scope.row.refer_type == 'book'" class="reference-item">
<p>{{ scope.row.author }}&nbsp;{{ scope.row.title }}.&nbsp;{{ scope.row.dateno }}.&nbsp;<br /></p>
<p>{{ scope.row.author }}&nbsp;<span v-html="formatTitle(scope.row.title)"></span>.&nbsp;{{ scope.row.dateno }}.&nbsp;<br /></p>
<a class="doiLink" :href="scope.row.isbn" target="_blank">{{ scope.row.isbn }}</a>
</div>
<!-- other 形式 -->
<p class="wrongLine reference-item" style="text-align: left" v-if="scope.row.refer_type == 'other'">
{{ scope.row.refer_frag }}
<span v-html="formatTitle(scope.row.refer_frag)"></span>
</p>
</template>
</el-table-column>
@@ -569,6 +572,18 @@ export default {
}
},
methods: {
formatTitle(title) {
if (!title) return '';
// 使用正则匹配,'gi' 表示全局匹配且不区分大小写
// \b 确保是完整单词匹配,防止误伤含有这些字母的其他单词
const reg = /\b(Retracted|Retraction)\b/gi;
return title.replace(reg, (match) => {
return `<span style="color: red; font-weight: bold;">${match}</span>`;
});
}
,
getJournalDateno(dateno, type) {
if (dateno && typeof dateno === 'string') {
const hasInvalidColon = !dateno.includes(':') || (dateno.includes(':') && dateno.split(':').pop().trim() === '');

View File

@@ -71,9 +71,9 @@ module.exports = {
proxy: {
'/api': {
// target: 'http://zmzm.tougao.dev.com/',//晓玲本地
target: 'https://submission.tmrjournals.com/',//正式
// target: 'http://tougaotest.tmrjournals.com/public/index.php/',//测试环境
// target: 'http://mytest.tmrjournals.com/public/index.php/',//新正式环境
// target: 'https://submission.tmrjournals.com/',//正式
target: 'http://tougaotest.tmrjournals.com/public/index.php/',//测试环境
// target: 'http://mytest.tmrjournals.com/public/index.php/',//新测试环境
changeOrigin: true,
pathRewrite: {
'^/api': ''