3 Commits

Author SHA1 Message Date
d05ffdf9c6 修改produce标蓝显示问题 2026-02-10 13:45:02 +08:00
5edf128cfb 1、审稿人14天超时 重新审稿邀请
2、nbsp 转换
3、H3标记为myh3
4、文章详情审稿和编委邮箱机构显示
5、稿号开头为Draft 状态为Reject->Awaiting Submission
6、作者端稿件重复(标题)
7、produce 增加 通讯作者详细地址
8、produce 增加 是否显示图文摘要1
2026-02-10 09:36:35 +08:00
9f108e7c81 H3+审稿人编委机构 2026-02-04 14:57:54 +08:00
24 changed files with 1070 additions and 702 deletions

BIN
dist.zip

Binary file not shown.

BIN
node_modules.zip Normal file

Binary file not shown.

View File

@@ -241,24 +241,28 @@ 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 (/^\d+$/.test(content) || /, ?/.test(content) || //.test(content)) {
return `<blue>${match}</blue>`; // 如果符合条件则加上蓝色标签
}
return match; // 如果不符合条件,则保持原样
});
}
if (prefix === '<blue>' && suffix === '</blue>') {
return match; // 如果已经有标签了,原样返回,不重复标记
}
// 判断逻辑:纯数字、逗号空格、连字符
if (/^\d+$/.test(content) || /, ?/.test(content) || //.test(content)) {
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

@@ -13,9 +13,7 @@ const baseUrl = '/';
//测试环境
// const mediaUrl = 'http://tougaotest.tmrjournals.com/public/';
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
// const baseUrl = '/api';
////新正式环境
// const mediaUrl = 'http://mytest.tmrjournals.com/public/';

View File

@@ -290,12 +290,13 @@ const en = {
state1: 'With editor',
state2: 'Under review',
state3: 'Reject',
stateDraft: 'Awaiting Submission',
state4: 'Revision',
state5: 'Accept',
state6: 'Pre-accept',
state8: 'Final Decision',
act1: 'Dealing',
act2: 'Finished'
act2: 'Finished',
},
evaluationsis: {
EmploymentRate: 'Employment Rate',
@@ -426,6 +427,7 @@ const en = {
jump: 'Locate',
editAssociation: 'Edit Association',
UnbindAssociation: 'Unbind Association',
selectContent: 'Please select the content to operate first',
},
pendingPayment: {
title: 'Title',

View File

@@ -276,6 +276,7 @@ const zh = {
state1: '已受理',
state2: '送审中',
state3: '拒稿',
stateDraft: '待提交',
state4: '退修',
state5: '接收',
state6: '预接收',
@@ -412,6 +413,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,28 +595,25 @@ export default {
}
},
async created() {
localStorage.removeItem('scrollPosition');
this.isShowEditComment();
this.getDate();
this.getCommentList();
},
mounted() {
document.addEventListener('copy', (event) => {
// 获取用户选中的文本
const selection = document.getSelection().toString();
// 你可以修改剪贴板内容
// event.clipboardData.setData('text/plain', selection + '\n---来自我的系统---');
console.log('用户复制了内容:', selection);
// 阻止默认行为(如果需要自定义复制逻辑的话)
// event.preventDefault();
});
// 获取用户选中的文本
const selection = document.getSelection().toString();
// 你可以修改剪贴板内容
// event.clipboardData.setData('text/plain', selection + '\n---来自我的系统---');
console.log('用户复制了内容:', selection);
// 阻止默认行为(如果需要自定义复制逻辑的话)
// event.preventDefault();
});
},
async activated() {
this.isShowEditComment();
this.getDate();
this.getCommentList();
@@ -624,9 +621,9 @@ export default {
methods: {
openAddTable(content) {
this.editVisible = false;
this.threeVisible = true;
this.$forceUpdate();
this.editVisible = false;
this.threeVisible = true;
this.$forceUpdate();
},
async copyArray(data) {
try {
@@ -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>
@@ -709,7 +707,7 @@ export default {
// 获取最终修改后的 HTML
content = div.innerHTML;
this.saveContent(content, this.currentContent.am_id);
} else if (type == 'addcontent') {
var hasTable = /<table[\s\S]*?>[\s\S]*?<\/table>/i.test(content);
@@ -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,8 +780,10 @@ export default {
var that = this;
var str = content.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1').trim();
str = str.replace(/<br\s*\/?>/gi, '');
str = await that.$commonJS.decodeHtml(str);
await that.$api
.post(that.urlList.editContent, {
am_id: am_id,
@@ -816,6 +818,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 +856,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,45 +881,45 @@ 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() : '';
if (!editorId || !wmathId) return;
const targetEditor = tinymce.get(editorId);
if (!targetEditor) return;
// 2. 找到编辑器中现有的 wmath 标签
const targetWmath = targetEditor.dom.select(`wmath[data-id="${wmathId}"]`, targetEditor.getBody())[0];
if (targetWmath) {
if (!newLatex) {
// ❌ 删除公式
targetEditor.dom.remove(targetWmath);
} else {
// ✅ 更新公式
// 保持属性纯净:同时更新 latex 内容和 wrap 属性
targetWmath.setAttribute('data-latex', newLatex);
// 如果 data 中传了 wrap 模式就更新它,否则可以保留原样或设为默认
if (wrap) {
targetWmath.setAttribute('data-wrap', wrap);
}
// 内部只放纯 latex 文本,不包裹 $ 符号
targetWmath.innerHTML = newLatex;
// 1. 从 data 中解构出 wrap (或者你命名的模式变量)
const { editorId, wmathId, latex, wrap } = data;
const newLatex = latex ? latex.trim() : '';
setTimeout(() => {
if (typeof renderMathJax === 'function') {
// 重新渲染该编辑器内的数学公式
renderMathJax(editorId);
if (!editorId || !wmathId) return;
const targetEditor = tinymce.get(editorId);
if (!targetEditor) return;
// 2. 找到编辑器中现有的 wmath 标签
const targetWmath = targetEditor.dom.select(`wmath[data-id="${wmathId}"]`, targetEditor.getBody())[0];
if (targetWmath) {
if (!newLatex) {
// ❌ 删除公式
targetEditor.dom.remove(targetWmath);
} else {
// ✅ 更新公式
// 保持属性纯净:同时更新 latex 内容和 wrap 属性
targetWmath.setAttribute('data-latex', newLatex);
// 如果 data 中传了 wrap 模式就更新它,否则可以保留原样或设为默认
if (wrap) {
targetWmath.setAttribute('data-wrap', wrap);
}
// 内部只放纯 latex 文本,不包裹 $ 符号
targetWmath.innerHTML = newLatex;
setTimeout(() => {
if (typeof renderMathJax === 'function') {
// 重新渲染该编辑器内的数学公式
renderMathJax(editorId);
}
}, 10);
}
}, 10);
}
}
},
}
},
async huifu(id) {
var that = this;
await this.$confirm(this.$t('commonTable.reContent'), 'Prompt', {
@@ -987,7 +996,7 @@ export default {
var that = this;
var dataInfo = this.Main_List.find((item) => item.am_id == dataId);
var dataIndex = this.Main_List.indexOf(dataInfo);
var type = '';
if (dataInfo.type == 1) {
type = 'img';
@@ -1094,18 +1103,15 @@ 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=='down'&&index==this.Main_List.length-1){
return
}
const load = this.$loading({
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',
@@ -1131,7 +1137,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,8 +1160,10 @@ 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') : '';
var str= this.$commonJS.transformHtmlString(content)
str=str.replace(/<br\s*\/?>/gi, '');
console.log("🚀 ~ addComment ~ content:", str);
if (str == '') {
this.$message({
type: 'warning',
@@ -1237,13 +1245,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 +1296,6 @@ export default {
break;
}
console.log('data at line 739:', data);
await this.$api
.post(url, {
am_id: data.mainId
@@ -1330,21 +1337,19 @@ export default {
return;
}
if (index !== -1 && resData) {
var newData = Array.isArray(resData) ? resData : (resData ? [resData] : []);
const updatedItems= newData.map((item) => ({
...item,
checked: false,
getnum: 0,
}));
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'){
} 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 +1371,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 +1619,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 +1689,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 +1722,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
@@ -1883,6 +1885,9 @@ export default {
// 确定保存图片
async savePic() {
var str = this.picStyle1.note ? await this.$commonJS.decodeHtml(this.picStyle1.note) : '';
var titleStr = this.picStyle1.title ? await this.$commonJS.decodeHtml(this.picStyle1.title) : '';
if (!this.picStyle.picUrl) {
@@ -1893,6 +1898,8 @@ export default {
this.$message.error('Please enter a title');
return;
}
str = str.replace(/<br\s*\/?>/gi, '');
titleStr = titleStr.replace(/<br\s*\/?>/gi, '');
const loading = this.$loading({
lock: true,
text: 'Loading...',
@@ -1912,8 +1919,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 +1952,6 @@ export default {
this.pictVisible = false;
this.$refs.commonWordHtmlTypeSetting.refresh('addImg', res.data);
} else {
loading.close();
this.$message.error(res.msg);
@@ -1966,28 +1975,42 @@ export default {
},
async saveTable(content) {
const cleanTableData = (tableList) => {
if (tableList.length == 0) {
return [];
} else {
// 假设第一行是表头,保存表头
const header = tableList[0];
if (tableList.length == 0) {
return [];
} else {
// 定义清理函数:去掉所有 br 标签和 TinyMCE 占位符
const cleanText = (text) => {
if (!text) return "";
return text.replace(/<br\s*\/?>/gi, '').trim();
};
// 从第二行开始,移除与表头相同的行
const cleanedTable = tableList.filter((row, index) => {
// 如果是第一行,则保留
if (index === 0) return true;
// 1. 获取处理后的干净表头
const header = tableList[0].map(cell => ({
...cell,
text: cleanText(cell.text)
}));
// 比较当前行的每个单元格与表头是否相同
const isHeaderRow = row.every((cell, cellIndex) => {
return cell.text === header[cellIndex].text;
});
// 2. 过滤逻辑
const cleanedTable = tableList.map((row) => {
// 首先:把每一行里的每个 cell.text 里的 <br> 都去掉
return row.map(cell => ({
...cell,
text: cleanText(cell.text)
}));
}).filter((row, index) => {
if (index === 0) return true;
return !isHeaderRow; // 只保留与表头不同的行
});
// 3. 此时比较的就是没有 <br> 的文本了
const isHeaderRow = row.every((cell, cellIndex) => {
return cell.text === header[cellIndex].text;
});
return cleanedTable;
}
};
return !isHeaderRow;
});
return cleanedTable;
}
};
var cleanedTableList = content.table ? content.table : [];
cleanedTableList = cleanTableData(content.table);
@@ -2011,11 +2034,15 @@ export default {
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
strNote = strNote.replace(/<br\s*\/?>/gi, '');
strTitle = strTitle.replace(/<br\s*\/?>/gi, '');
var tableStr=JSON.stringify(cleanedTableList)
if (this.lineStyle.visiTitle == 'Edit Table') {
this.$api
.post(this.urlList.editTable, {
amt_id: this.lineStyle.amt_id,
table_data: JSON.stringify(cleanedTableList),
table_data: tableStr,
html_data: content.html_data,
note: strNote,
title: strTitle
@@ -2026,8 +2053,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 +2601,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

@@ -928,7 +928,7 @@
<div>
<span>Status : </span>
<b>{{ articleState }}</b>
<el-button type="text" @click="testvis" icon="el-icon-edit">Change</el-button>
<el-button type="text" @click="testvis" icon="el-icon-edit" v-if="!(form.is_draft==1&&form.state==3)">Change</el-button>
</div>
<div style="display: flex; width: 100%">
<span>Remarks :</span>
@@ -1553,6 +1553,7 @@ export default {
remarks: '',
state: '',
ctime: '',
is_draft: '',
authorList: [],
transList: []
},
@@ -1788,10 +1789,17 @@ export default {
},
articleState: function () {
let str = '';
switch (this.form.state) {
if(this.form.is_draft==1&&this.form.state==3){
str = this.$t('artstate.stateDraft');
return str;
}else{
switch (this.form.state) {
case 0:
str = this.$t('artstate.state0');
break;
case 1:
str = this.$t('artstate.state1');
break;
@@ -1814,7 +1822,9 @@ export default {
str = this.$t('artstate.state6');
break;
}
return str;
return str;
}
}
},
methods: {
@@ -1881,8 +1891,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;
@@ -2511,6 +2550,7 @@ export default {
this.form.is_use_ai = res.article.is_use_ai;
this.form.use_ai_explain = res.article.use_ai_explain;
this.form.transList = res.transfer;
this.form.is_draft = res.article.is_draft;
this.msgs = res.msg;
var alist = res.authors;
let alist_name = [];

View File

@@ -87,9 +87,13 @@
</p> -->
<div class="man_state" :style="item.state,'3' | stateChange" >
<b :style="item.state,'4' | stateChange">
<b :style="item.state,'4' | stateChange" v-if="item.is_draft==1&&item.state==3">
{{$t('artstate.stateDraft')}}
</b>
<b :style="item.state,'4' | stateChange" v-else>
{{item.state,'tst' | stateChange}}
</b>
</div>
<div class="man_btn" style="overflow: hidden;">
@@ -1058,7 +1062,7 @@ this.$router.push({
border: 1px solid #fff;
color: #fff;
text-align: center;
padding: 6px 18px;
padding: 6px 12px;
letter-spacing: -0.5px;
border-top-right-radius: 3px;
/* border-top-left-radius: 3px; */

View File

@@ -125,7 +125,7 @@
<span style="float: right">
<span class="labelTitle" style="font-weight: 500; font-size: 13px">Status :</span>
<font style="margin-right: 15px; font-size: 13px; letter-spacing: -0.5px; font-weight: 700">
{{ stateFormat(item.state) }}
{{ stateFormat(item.is_draft==1&&item.state==3?'draft':item.state) }}
</font>
</span>
</p>
@@ -2629,6 +2629,9 @@ export default {
case 0:
str = this.$t('artstate.state0');
break;
case 'draft':
str = this.$t('artstate.stateDraft');
break;
case 1:
str = this.$t('artstate.state1');
break;

View File

@@ -96,6 +96,11 @@
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="Use the Picture as Graphic Abstract :" prop="is_graphical_abstract" label-width="260px">
<el-radio v-model="detailMes.is_graphical_abstract" :label="1">Yes</el-radio>
<el-radio v-model="detailMes.is_graphical_abstract" :label="2">No</el-radio>
</el-form-item>
<el-form-item label="Type :" prop="type">
<el-select v-model="detailMes.type" filterable placeholder="Please select an article type">
<el-option v-for="item in opMedical" :key="item.value" :label="item.label" :value="item.value"> </el-option>
@@ -108,65 +113,61 @@
<el-input v-model="detailMes.tradition_tag" placeholder="eg Highlights"></el-input>
</el-form-item>
<el-form-item label="Tradition Content :">
<tinymce
type="tradition"
<div class="tinymce-container">
<div v-if="tradition==null" class="loading-status loadTinymceBox">
<i class="el-icon-loading"></i> Fetching highlights, please wait...
</div>
<tinymce v-else
type="table"
:height="160"
id="tiny-tradition"
ref="tinymceChild2"
:tinymceOtherInit="{
forced_root_block: '',
newline_behavior: 'linebreak',
newline_behavior: 'linebreak'
}"
valid_elements=",br"
:wordStyle="`p{font-size: 13px;}`"
:isAutomaticUpdate="true"
@getContent="getContent"
@updateChange="(content) => updateChange('tradition',content)"
@updateChange="(content) => updateChange('tradition', content)"
:value="tradition"
class="paste-area text-container"
:toolbar="[
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton'
]"
style="
line-height: 12px;
overflow: auto;
font-size: 13px; /* 字体大小 */
margin-top: 0pt; /* 段前间距 */
margin-bottom: 0pt; /* 段后间距 */
"
style="line-height: 12px; overflow: auto; font-size: 13px; margin-top: 0pt; margin-bottom: 0pt"
></tinymce>
</div>
<!-- <quill-editor ref="myTextEditor" v-model="detailMes.tradition" :options="editorOption"> </quill-editor> -->
</el-form-item>
<el-form-item label="Medical history of objective :">
<tinymce
type="mhooStr"
<div class="tinymce-container">
<div v-if="mhooStr==null" class="loading-status loadTinymceBox">
<i class="el-icon-loading"></i> Fetching medical history of objective, please wait...
</div>
<tinymce v-else
type="table"
:height="160"
ref="tinymceChild3"
:tinymceOtherInit="{
forced_root_block: '',
newline_behavior: 'linebreak',
newline_behavior: 'linebreak'
}"
valid_elements=",br"
:wordStyle="`p{font-size: 13px;}`"
:isAutomaticUpdate="true"
@getContent="getContent"
@updateChange="(content) => updateChange('mhooStr',content)"
id="tiny-mhoo"
@updateChange="(content) => updateChange('mhooStr', content)"
:value="mhooStr"
class="paste-area text-container"
:toolbar="[
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton'
]"
style="
line-height: 12px;
overflow: auto;
font-size: 13px; /* 字体大小 */
margin-top: 0pt; /* 段前间距 */
margin-bottom: 0pt; /* 段后间距 */
"
style="line-height: 12px; overflow: auto; font-size: 13px; margin-top: 0pt; margin-bottom: 0pt"
></tinymce>
</div>
<!-- <quill-editor ref="myTextEditor" v-model="detailMes.mhoo" :options="editorOption"> </quill-editor> -->
</el-form-item>
<el-form-item label="keywords :" prop="keywords">
@@ -188,27 +189,27 @@
</el-form-item>
<el-form-item label="Abstract :" prop="abstract">
<template slot="label"> <span style="color: #f56c6c; margin-right: 4px">*</span>Abstract : </template>
<tinymce
type="Abstract"
:height="160"
ref="tinymceChild1"
:wordStyle="`p{font-size: 13px;}`"
:isAutomaticUpdate="true"
@getContent="getContent"
@updateChange="(content) => updateChange('abstract',content)"
:value="abstract"
class="paste-area text-container"
:toolbar="[
'bold italic |customBlue removeBlue|myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton'
]"
style="
line-height: 12px;
overflow: auto;
font-size: 13px; /* 字体大小 */
margin-top: 0pt; /* 段前间距 */
margin-bottom: 0pt; /* 段后间距 */
"
></tinymce>
<div class="tinymce-container">
<div v-if="abstract==null" class="loading-status loadTinymceBox">
<i class="el-icon-loading"></i> Fetching abstract, please wait...
</div>
<tinymce
v-else
type="table"
:height="160"
ref="tinymceChild1"
id="tiny-abstract"
:wordStyle="`p{font-size: 13px;}`"
:isAutomaticUpdate="true"
@getContent="getContent"
@updateChange="(content) => updateChange('abstract', content)"
:value="abstract"
class="paste-area text-container"
:toolbar="['bold italic |customBlue removeBlue|myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton']"
style="line-height: 12px; overflow: auto; font-size: 13px; margin-top: 0pt; margin-bottom: 0pt"
></tinymce>
</div>
<!-- <quill-editor ref="myTextEditor" v-model="detailMes.abstract" :options="editorOption"> </quill-editor> -->
</el-form-item>
@@ -266,6 +267,7 @@
</el-table-column>
<el-table-column prop="author_country" label="Country" width="140px"></el-table-column>
<el-table-column prop="orcid" label="ORCID"></el-table-column>
<el-table-column prop="mailing_address" label="Corresponding author details" min-width="160px"></el-table-column>
<el-table-column label="" width="190" align="center">
<template slot-scope="scope">
<div class="operation">
@@ -292,7 +294,7 @@
</el-table>
<!-- 添加作者弹出框 -->
<el-dialog title="Add author" :visible.sync="addAuthor" width="650px" destroy-on-close :close-on-click-modal="false">
<el-form ref="add_Author" :model="addFomauthor" :rules="rules" label-width="165px">
<el-form ref="add_Author" :model="addFomauthor" :rules="rules" label-width="215px">
<el-form-item label="Author first name :" prop="first_name">
<el-input v-model="addFomauthor.first_name"></el-input>
</el-form-item>
@@ -312,7 +314,7 @@
</el-radio-group>
</el-form-item>
<el-form-item label="Email :" v-if="addFomauthor.is_report != '1'">
<el-input v-model="editFomauthor.email" type="email"></el-input>
<el-input v-model="addFomauthor.email" type="email"></el-input>
</el-form-item>
<el-form-item label="Email :" prop="email" v-if="addFomauthor.is_report == '1'">
<el-input v-model="addFomauthor.email" type="email"></el-input>
@@ -350,6 +352,17 @@
<template slot="prepend">https://orcid.org/</template>
</el-input>
</el-form-item>
<el-form-item
:required="addFomauthor.is_report == '1'"
label="Corresponding author details :"
prop="mailing_address"
>
<el-input
v-model="addFomauthor.mailing_address"
type="textarea"
placeholder="eg: Author full name. Affiliation address. Email."
></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addAuthor = false">Canel</el-button>
@@ -358,7 +371,7 @@
</el-dialog>
<!-- 编辑作者弹出框 -->
<el-dialog title="Edit Author" :visible.sync="editAuthor" width="650px" :close-on-click-modal="false">
<el-form ref="edit_Author" :model="editFomauthor" :rules="rules" label-width="165px">
<el-form ref="edit_Author" :model="editFomauthor" :rules="rules" label-width="215px">
<el-form-item label="Author first name :" prop="first_name">
<el-input v-model="editFomauthor.first_name"></el-input>
</el-form-item>
@@ -415,6 +428,18 @@
<template slot="prepend">https://orcid.org/</template>
</el-input>
</el-form-item>
<el-form-item
:required="editFomauthor.is_report == '1'"
label="Corresponding author details :"
prop="mailing_address"
>
<el-input
v-model="editFomauthor.mailing_address"
type="textarea"
placeholder="eg: Author full name. Affiliation address. Email."
></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editAuthor = false">Cancel</el-button>
@@ -688,7 +713,7 @@
<div class="bor_style_onli">
<h4>{{ tabsList[5].name }}</h4>
<div style="font-size: 14px">
<div style="margin: 30px 0px 0 0">
<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">
@@ -709,80 +734,97 @@
</el-form-item>
</el-form>
<div style="text-align: center; margin: 0px 0 0 0">
<el-button type="primary" @click="ZsSaveAbs" class="save-btn" style="margin-top: 10px;">
<el-button type="primary" @click="ZsSaveAbs" class="save-btn" style="margin-top: 10px">
<i class="el-icon-check"></i>
Save Essential Information
</el-button>
</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="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 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: 14px"
: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
<span
><i
class="el-icon-refresh"
style="color: #006699; font-weight: bold; margin-left: 6px; cursor: pointer"
@click="refreshPdfList"
></i
></span>
<span
v-if="detailMes.file_sub_table"
style="color: #006699; font-size: 13px; font-weight: normal; float: right"
>Supplementary Material :<a
:href="'/public/articleSUBTAB/' + detailMes.file_sub_table"
target="_blank"
style="margin-left: 20px"
>
<i class="el-icon-download" style="color: #006699; font-weight: bold"></i> </a
></span>
</h5>
<p v-if="PDFfileList.length == 0" style="color: #666; font-size: 14px">No Manuscript</p>
<div v-for="(item, index) in PDFfileList" :key="index" class="load_pdf">
<img src="../../assets/img/icon_pdf.png" style="width: 26px; height: 26px" />
<span style="color: #333">Typesetting {{ index + 1 }}</span>
<span style="margin-left: 40px; color: #888; font-size: 13px"
>Time : {{ modifDate(item.create_time * 1000) }}</span
>
<a :href="'/public/' + 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 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: 14px;"
: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
<span><i class="el-icon-refresh" style="color: #006699; font-weight: bold;margin-left: 6px;cursor: pointer;" @click="refreshPdfList"></i></span>
<span v-if="detailMes.file_sub_table" style="color: #006699; font-size: 13px;font-weight: normal;float: right;">Supplementary Material :<a :href="'/public/articleSUBTAB/' +detailMes.file_sub_table" target="_blank" style="margin-left: 20px">
<i class="el-icon-download" style="color: #006699; font-weight: bold"></i>
</a></span>
</h5>
<p v-if="PDFfileList.length == 0" style="color: #666; font-size: 14px">No Manuscript</p>
<div v-for="(item, index) in PDFfileList" :key="index" class="load_pdf">
<img src="../../assets/img/icon_pdf.png" style="width: 26px; height: 26px;" />
<span style="color: #333">Typesetting {{ index + 1 }}</span>
<span style="margin-left: 40px; color: #888; font-size: 13px">Time : {{ modifDate(item.create_time * 1000) }}</span>
<a :href="'/public/' + 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="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>
@@ -829,7 +871,6 @@
</el-upload>
</div>
</div>
</div>
</div>
</div>
@@ -1030,7 +1071,7 @@ export default {
data() {
return {
isGenerating: false, // 按钮加载状态
percentage: 0, // 进度条
percentage: 0, // 进度条
finalReview: null,
baseUrl: this.Common.baseUrl,
mediaUrl: this.Common.mediaUrl,
@@ -1096,11 +1137,13 @@ export default {
],
detailMes: {
type: '',
is_graphical_abstract: '3',
journal_special_id: 'None'
},
abstract: '',
tradition: '',
mhooStr: '',
abstract: null,
tradition:null,
mhooStr: null,
opInstal: [],
// opMedical: [
// {
@@ -1475,6 +1518,40 @@ export default {
message: 'Please use English characters only',
trigger: 'blur'
}
],
is_graphical_abstract: [
{
required: true,
validator: function (rule, value, callback) {
if (value == 1 || value == 2) {
callback();
} else {
// 这里的英文提示更加符合地道的校验语境
callback(
new Error(
'Please select whether the uploaded picture should be shown as graphic abstract on the PDF title page.'
)
);
}
},
trigger: 'change' // 建议下拉框用 change失去焦点用 blur
}
],
mailing_address: [
{
validator: (rule, value, callback) => {
var formData = this.addAuthor ? this.addFomauthor : this.editFomauthor;
if (formData.is_report == '1') {
if (!value || value.trim() === '') {
// 更加正式的表达:请提供通讯作者的详细信息
return callback(new Error('Please provide the corresponding author details.'));
}
}
// 必须确保所有逻辑路径都能执行到 callback()
callback();
},
trigger: ['blur', 'change'] // 建议加上 change体验更好
}
]
}
};
@@ -1484,7 +1561,6 @@ export default {
this.getHight();
window.addEventListener('resize', this.getHight);
this.getData();
this.getAuthorJG();
this.getCount();
this.getWorldPdf();
@@ -1492,30 +1568,29 @@ export default {
},
methods: {
async generatePDF() {
// 1. 验证逻辑(保持原样)
// 1. 验证逻辑(保持原样)
try {
this.isGenerating = true; // 开启加载
try {
this.isGenerating = true; // 开启加载
// 注意:这里要加 await确保拿到结果后再往下走
const res = await this.$api.post('api/Production/createArticlePdf', {
p_article_id: this.p_article_id
});
// 注意:这里要加 await确保拿到结果后再往下走
const res = await this.$api.post('api/Production/createArticlePdf', {
p_article_id: this.p_article_id
});
if (res.status == 1) {
this.$message.success("The PDF document is being processed. Please wait and refresh the page in one minute.");
} else {
this.$message.error("Generation failed, please try again.");
}
} catch (error) {
console.error(error);
this.$message.error("Network error, please try again.");
} finally {
// 只有请求真正完成后,才会执行这里
this.isGenerating = false;
}
},
if (res.status == 1) {
this.$message.success('The PDF document is being processed. Please wait and refresh the page in one minute.');
} else {
this.$message.error('Generation failed, please try again.');
}
} catch (error) {
console.error(error);
this.$message.error('Network error, please try again.');
} finally {
// 只有请求真正完成后,才会执行这里
this.isGenerating = false;
}
},
getArticleFinal(id) {
// api/Finalreview/getRecord
this.$api
@@ -1524,7 +1599,6 @@ export default {
// article_id: 6075
})
.then((res) => {
if (res.status == 1) {
this.finalReview = res.data;
} else {
@@ -1533,17 +1607,13 @@ export default {
});
},
updateChange(type, content) {
this[type] = content;
},
getTinymceContent(type) {
this.$refs.tinymceChild1.getContent(type);
},
getContent(type, content) {
},
getContent(type, content) {},
// 跳转文章详情
showdetaileditor(data) {
this.$router.push({
@@ -1583,45 +1653,49 @@ export default {
}
}
},
wrapWithP(content){
if (!content) return '';
const trimmed = content.trim();
// 判断是否以 <p 开头(兼容带属性的 p 标签)且以 </p> 结尾
if (content.startsWith('<p>')) {
return trimmed;
}
// 如果没有包裹,则手动包裹
return `<p>${trimmed}</p>`;
},
getData() {
this.idform.p_article_id = this.p_article_id;
this.detailMes.p_article_id = this.p_article_id;
this.addFomauthor.p_article_id = this.p_article_id;
this.addFomschool.p_article_id = this.p_article_id;
this.UpTypeFile.p_article_id = this.p_article_id;
this.abstract = '';
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
this.idform.p_article_id = this.p_article_id;
this.detailMes.p_article_id = this.p_article_id;
this.detailMes.is_graphical_abstract = 3;
this.addFomauthor.p_article_id = this.p_article_id;
this.addFomschool.p_article_id = this.p_article_id;
this.UpTypeFile.p_article_id = this.p_article_id;
this.abstract = null;
this.tradition = null;
this.mhooStr = null;
// 获取文章信息
this.$api
.post('api/Production/getProductionDetail', this.idform)
.then((res) => {
if (res.code == 0) {
if (res.code == 0) {
this.abstract = res.data.production.abstract ? res.data.production.abstract : '';
this.tradition = res.data.production.tradition ? res.data.production.tradition : '';
this.mhooStr = res.data.production.mhoo ? res.data.production.mhoo : '';
this.detailMes = res.data.production;
this.abstract = res.data.production.abstract?res.data.production.abstract:'';
this.tradition = res.data.production.tradition?res.data.production.tradition:'';
this.mhooStr = res.data.production.mhoo?res.data.production.mhoo:'';
if (this.abstract != '') {
this.$nextTick(() => {
this.$refs.tinymceChild1.setContent(this.abstract);
});
}
if (this.tradition != '') {
this.$nextTick(() => {
this.$refs.tinymceChild2.setContent(this.tradition);
});
}
if (this.mhooStr != '') {
this.$nextTick(() => {
this.$refs.tinymceChild3.setContent(this.mhooStr);
});
}
this.UpTyFIle = res.data.production.file_pdf;
this.getArticleFinal(res.data.production.article_id);
if (res.data.production.icon != '') {
@@ -1785,7 +1859,8 @@ export default {
this.$message.error(err);
});
},
refreshPdfList(){ const loading = this.$loading({
refreshPdfList() {
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
@@ -1800,22 +1875,19 @@ export default {
},
getPdfList() {
// 可以下载的word列表
this.$api
.post('/api/Production/getProductionArticlePdf', {
p_article_id: this.p_article_id
})
.then((res) => {
if (res.status == 1) {
this.PDFfileList = res.data;
} else {
this.$message.error(res.msg);
}
})
.catch((err) => {
this.$message.error(err);
});
},
@@ -1944,7 +2016,7 @@ export default {
return e.stage_year + ' Vol.' + e.stage_vol + ' issue.' + e.stage_no + e.stage_pagename + e.stage_page;
},
async abstractFormat(content) {
content = this.$commonJS.transformHtmlString(content,'content',{keepBr:true});
content = this.$commonJS.transformHtmlString(content, 'content', { keepBr: true });
var div = document.createElement('div');
div.innerHTML = content;
var imgTags = div.getElementsByTagName('img');
@@ -1969,41 +2041,46 @@ export default {
}
// 处理 p 标签 + 解码
content = div.innerHTML;
content = content.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1').trim();
content = await this.$commonJS.decodeHtml(content);
content = content.replace(/&nbsp;|\u00A0/g, ' ');
return content;
},
// 1----保存稿件信息
async ZsSaveMes() {
var abstractStr = '';
var mhooStr = '';
var traditionStr = '';
if (this.detailMes.journal_stage_id == 0) {
this.$message.error('Please select an installment!');
return;
} if (this.tradition!='') {
}
if (this.tradition != '') {
traditionStr = await this.abstractFormat(this.tradition);
}
if (this.mhooStr!='') {
}
if (this.mhooStr != '') {
mhooStr = await this.abstractFormat(this.mhooStr);
}
if (this.abstract == '') {
this.$message.error('Please input abstract!');
return false;
} else {
abstractStr = await this.abstractFormat(this.abstract);
}
this.$refs.Mes_Form.validate((valid) => {
if (valid) {
this.$api
.post('api/Production/editProduction', { ...this.detailMes, abstract: abstractStr, mhoo: mhooStr, tradition: traditionStr })
.post('api/Production/editProduction', {
...this.detailMes,
abstract: abstractStr,
mhoo: mhooStr,
tradition: traditionStr
})
.then((res) => {
if (res.code == 0) {
this.$message.success(`Successfully save the article!`);
@@ -2025,6 +2102,10 @@ export default {
// 2----添加作者操作
add_Authorclick(index, row) {
if (this.schoolData.length == 0) {
this.$message.error('Please provide at least one institutional affiliation.');
return;
}
this.aid = index;
this.addFomauthor = {
is_first: '0',
@@ -2035,7 +2116,6 @@ export default {
this.addAuthor = true;
},
authorAdd(addFomauthor) {
console.log(this.addFomauthor);
this.$refs.add_Author.validate((valid) => {
if (valid) {
this.$api
@@ -2982,11 +3062,11 @@ 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);
@@ -3170,35 +3250,43 @@ export default {
min-height: 60px;
}
.left-panel {
flex: 1; /* 左侧占据剩余空间 */
margin-right: 40px;
border-right: 1px dashed #eee; /* 加一条虚线分隔线 */
padding-right: 20px;
flex: 1; /* 左侧占据剩余空间 */
margin-right: 40px;
border-right: 1px dashed #eee; /* 加一条虚线分隔线 */
padding-right: 20px;
}
.right-panel {
width: 48%; /* 右侧固定宽度 */
width: 48%; /* 右侧固定宽度 */
}
.right-panel h5 {
font-weight: normal;
}
.console-card {
background: #f9f9f9;
border-radius: 8px;
padding: 20px;
border: 1px solid #ebeef5;
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;
display: flex;
align-items: center;
margin-bottom: 15px;
padding: 10px;
transition: background 0.3s;
}
.load_pdf:hover {
background: #f5f7fa;
background: #f5f7fa;
}
.loadTinymceBox {
height: 160px; line-height: 160px; text-align: center; border: 2px solid #eee;
border-radius: 10px;
color: #c0c4cc !important;
}
.loadTinymceBox *{
color: #c0c4cc !important;
}
</style>
<style>

View File

@@ -19,7 +19,7 @@
</p>
<!-- <p style="color: #7f8790;margin: 10px 0 0 0;">Follow the progress of your submission.</p> -->
<div class="tit_head">
Status : <b style="margin: 0 50px 0 0">{{ statetostr(artMes.laststate) }}</b> Journal : <b>{{ artMes.journalname }}</b>
Status : <b style="margin: 0 50px 0 0">{{ artMes.is_draft==1&&(artMes.laststate==3||artMes.laststate==-1) ? $t('artstate.stateDraft') : statetostr(artMes.laststate)}}</b><span v-if="artMes.journalname">Journal : <b>{{ artMes.journalname }}</b></span>
</div>
<div class="tit_head" v-if="artMes.majors && artMes.majors.length > 0">
<b style="margin: 0 50px 0 0; font-size: 20px">Research areas</b>

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

@@ -441,7 +441,7 @@ export default {
},
async created() {
this.isCollapse = localStorage.getItem('isCollapse') == 'true' ? true : false;
console.log('localStorage.getItem', typeof localStorage.getItem('isCollapse'));
this.isShowEditComment();
this.isFresh = false;
this.$nextTick(async () => {

View File

@@ -1,6 +1,5 @@
<template>
<div class="tinymce-container editor-container">
<textarea class="tinymce-textarea" :id="tinymceId"></textarea>
</div>
</template>
@@ -69,6 +68,7 @@ export default {
data() {
return {
uploadNotifications: {},
content: '',
totalUploadImages: 0,
uploadedImageCount: 0,
uploadNotificationInstance: null, // 全局通知实例
@@ -123,19 +123,23 @@ export default {
formLabelWidth: '120px',
hasChange: false,
hasInit: false,
editorInstance: null,
tinymceId: this.id || 'vue-tinymce-' + +new Date()
};
},
watch: {
value(val) {
// console.log('val at line 208:', val);
value: {
handler(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => {
window.tinymce.get(this.tinymceId).setContent(val);
// window.renderMathJax(); // 主动触发 MathJax 渲染
});
}
},
immediate: true
}
},
mounted() {
@@ -159,13 +163,31 @@ export default {
this.destroyTinymce();
},
methods: {
handleSetContent(val) {
if (!this.editorInstance) return;
let finalContent = val || '';
// 你的业务逻辑:自动包裹 <p> 标签
if (!finalContent.includes('wordTableHtml') && !finalContent.startsWith('<p>')) {
finalContent = '<p>' + finalContent + '</p>';
}
this.editorInstance.setContent(finalContent);
// 渲染数学公式
this.$nextTick(() => {
if (window.renderMathJax) {
window.renderMathJax(this.tinymceId);
}
});
},
onClear() {
if (this.uploadNotificationInstance) {
this.uploadNotificationInstance.close(); this.uploadNotificationInstance = null;
this.uploadedImageCount = 0;
this.totalUploadImages = 0;}
this.uploadNotificationInstance.close();
this.uploadNotificationInstance = null;
this.uploadedImageCount = 0;
this.totalUploadImages = 0;
}
},
updateUploadProgressNotification(imgIndex, status = 'processing', message = '') {
// 快捷调用 $t
@@ -180,7 +202,7 @@ export default {
<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;">
@@ -347,46 +369,46 @@ export default {
}
},
// 按照你要求的 XMLHttpRequest 格式编写
// 直接接收已经转好的 blob
uploadSingleImage(blob, index) {
const _this = this;
const xhr = new XMLHttpRequest();
const formData = new FormData();
// 直接把传进来的 blob 丢进表单
formData.append('file_name', blob, `word_img_${index}.png`);
formData.append('article_id', this.articleId);
// 直接接收已经转好的 blob
uploadSingleImage(blob, index) {
const _this = this;
const xhr = new XMLHttpRequest();
const formData = new FormData();
xhr.withCredentials = false;
xhr.open('POST', _this.baseUrl + 'api/Articlemain/uploadTableImage');
xhr.onload = function () {
if (xhr.status !== 200) {
_this.removePlaceholder(index); // 失败清理
return;
}
try {
const json = JSON.parse(xhr.responseText);
if (json.status == 1) {
const finalUrl = _this.mediaUrl + 'articleTableImage/' + json.data;
const doc = tinymce.activeEditor.getDoc();
const placeholder = doc.querySelector(`img[data-idx="${index}"]`);
if (placeholder) {
placeholder.src = finalUrl;
placeholder.removeAttribute('data-idx');
// 直接把传进来的 blob 丢进表单
formData.append('file_name', blob, `word_img_${index}.png`);
formData.append('article_id', this.articleId);
xhr.withCredentials = false;
xhr.open('POST', _this.baseUrl + 'api/Articlemain/uploadTableImage');
xhr.onload = function () {
if (xhr.status !== 200) {
_this.removePlaceholder(index); // 失败清理
return;
}
} else {
_this.removePlaceholder(index);
}
} catch (e) {
console.error('解析响应失败', e);
_this.removePlaceholder(index);
}
};
xhr.onerror = () => _this.removePlaceholder(index);
xhr.send(formData);
},
try {
const json = JSON.parse(xhr.responseText);
if (json.status == 1) {
const finalUrl = _this.mediaUrl + 'articleTableImage/' + json.data;
const doc = tinymce.activeEditor.getDoc();
const placeholder = doc.querySelector(`img[data-idx="${index}"]`);
if (placeholder) {
placeholder.src = finalUrl;
placeholder.removeAttribute('data-idx');
}
} else {
_this.removePlaceholder(index);
}
} catch (e) {
console.error('解析响应失败', e);
_this.removePlaceholder(index);
}
};
xhr.onerror = () => _this.removePlaceholder(index);
xhr.send(formData);
},
removePlaceholder(idx) {
const doc = tinymce.activeEditor.getDoc();
@@ -408,12 +430,75 @@ uploadSingleImage(blob, index) {
}
return new Blob([u8arr], { type: mime });
},
formatHtml(val) {
const rawValue = val || ''; // 处理 null
const cleanEmptyTags = /<([a-zA-Z1-6]+)\b[^>]*><\/\1>/g;
const replaceSpaces = /\s+(?=<)|(?<=>)\s+/g;
const removeBr = /<br\s*\/?>/gi; // 移除所有 br 标签
if (rawValue.includes('wordTableHtml')) {
const parser = new DOMParser();
const doc = parser.parseFromString(rawValue, 'text/html');
const cells = doc.querySelectorAll('td, th');
cells.forEach((cell) => {
cell.innerHTML = cell.innerHTML
.replace(cleanEmptyTags, '')
.replace(removeBr, '') // 针对你“不想要br”的需求
.replace(replaceSpaces, '&nbsp;');
});
return doc.body.innerHTML;
} else {
return rawValue
.replace(cleanEmptyTags, '')
.replace(removeBr, '')
.replace(replaceSpaces, '&nbsp;');
}
},
getSafeContent(val) {
const rawValue = val || '';
const cleanEmptyTags = /<([a-zA-Z1-6]+)\b[^>]*><\/\1>/g;
const replaceSpaces = /\s+(?=<)|(?<=>)\s+/g;
const escapeIllegalLT = (str) => {
return str.replace(/<(?!(\/?(p|div|span|table|tr|td|th|b|i|strong|em|ul|ol|li|br|img)))/gi, '&lt;');
};
let processedHtml = '';
if (rawValue.includes('wordTableHtml')) {
const parser = new DOMParser();
const doc = parser.parseFromString(rawValue, 'text/html');
const cells = doc.querySelectorAll('td, th');
cells.forEach((cell) => {
// 1. 先把单元格内的非法 < 转义
let cellText = cell.innerHTML;
// let cellText = escapeIllegalLT(cell.innerHTML);
// 2. 再清理空标签和多余空格
cell.innerHTML = cellText
.replace(cleanEmptyTags, '')
.replace(replaceSpaces, '&nbsp;');
});
processedHtml = doc.body.innerHTML;
} else {
// 非表格逻辑也同样处理转义
// processedHtml = escapeIllegalLT(rawValue)
processedHtml = rawValue
.replace(cleanEmptyTags, '')
.replace(replaceSpaces, '&nbsp;');
}
return processedHtml;
},
initTinymce() {
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]',
@@ -439,34 +524,37 @@ uploadSingleImage(blob, index) {
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 {
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; /* 防止内部标签背景干扰 */
}
myfigure *,
mytable * {
color: inherit !important;
text-shadow: inherit !important;
background: transparent !important; /* 防止内部标签背景干扰 */
}
myh3 ,myh3 *{
font-weight: bold !important;
}
@keyframes blueGlow {
0%,
100% {
transform: scale(1);
opacity: 0.9;
}
50% {
transform: scale(1.02);
opacity: 1;
}
}
`,
@keyframes blueGlow {
0%,
100% {
transform: scale(1);
opacity: 0.9;
}
50% {
transform: scale(1.02);
opacity: 1;
}
}
`,
formats: {
bold: { inline: 'b' },
italic: { inline: 'i' }
@@ -541,40 +629,39 @@ myfigure *,
_this.$commonJS.initEditorButton(_this, ed);
var currentWmathElement = null;
ed.on('click', function (e) {
const wmathElement = e.target.closest('wmath');
if (wmathElement) {
currentWmathElement = wmathElement; // 保存当前点击的元素
// 1. 提取内容:获取 LaTeX
const latexContentRaw = wmathElement.getAttribute('data-latex') || '';
const latexContent = latexContentRaw.replace(/\$/g, '').trim();
const encoded = encodeURIComponent(latexContent);
const wmathElement = e.target.closest('wmath');
if (wmathElement) {
currentWmathElement = wmathElement; // 保存当前点击的元素
// 2. 提取状态:获取之前的 wrap 模式 👈 重要新增
const wrapMode = wmathElement.getAttribute('data-wrap') || 'block';
// 1. 提取内容:获取 LaTeX
const latexContentRaw = wmathElement.getAttribute('data-latex') || '';
const latexContent = latexContentRaw.replace(/\$/g, '').trim();
const encoded = encodeURIComponent(latexContent);
// 3. 处理唯一 ID
let wmathId = wmathElement.getAttribute('data-id');
if (!wmathId) {
wmathId = 'wmath-' + Math.random().toString(36).substr(2, 9);
wmathElement.setAttribute('data-id', wmathId);
}
// 2. 提取状态:获取之前的 wrap 模式 👈 重要新增
const wrapMode = wmathElement.getAttribute('data-wrap') || 'block';
const editorId = ed.id;
// 3. 处理唯一 ID
let wmathId = wmathElement.getAttribute('data-id');
if (!wmathId) {
wmathId = 'wmath-' + Math.random().toString(36).substr(2, 9);
wmathElement.setAttribute('data-id', wmathId);
}
// 4. 打开窗口:在 URL 参数中增加 wrap 模式 👈 这样弹窗就知道该默认选哪个了
window.open(
`/LateX?id=${encoded}&wmathId=${wmathId}&editorId=${editorId}&wrap=${wrapMode}`,
'_blank',
'width=1000,height=800,scrollbars=no,resizable=no'
);
}
});
const editorId = ed.id;
ed.on('paste', async (event) => {
// 4. 打开窗口:在 URL 参数中增加 wrap 模式 👈 这样弹窗就知道该默认选哪个了
window.open(
`/LateX?id=${encoded}&wmathId=${wmathId}&editorId=${editorId}&wrap=${wrapMode}`,
'_blank',
'width=1000,height=800,scrollbars=no,resizable=no'
);
}
});
ed.on('paste', async (event) => {
const rtf = event.clipboardData.getData('text/rtf');
if (rtf && rtf.includes('\\pict')) {
const extracted = extractHexImagesFromRTF(rtf);
_this.totalUploadImages = extracted.length; // 设置总数
_this.uploadedImageCount = 0; // 重置已上传数
@@ -594,22 +681,21 @@ myfigure *,
return; // 跳过此图片
}
try {
const imageBlob = _this.hexToBlob(img.hex, img.mimeType);
_this.updateUploadProgressNotification(i, 'uploading');
const imageBlob = _this.hexToBlob(img.hex, img.mimeType);
// 2. 【核心优化】直接上传这个 Blob 文件
// uploadSingleImage 内部需要改用 FormData 发送
await _this.uploadSingleImage(imageBlob, i);
_this.updateUploadProgressNotification(i, 'success');
} catch (err) {
_this.updateUploadProgressNotification(i, 'uploading');
// 2. 【核心优化】直接上传这个 Blob 文件
// uploadSingleImage 内部需要改用 FormData 发送
await _this.uploadSingleImage(imageBlob, 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 并关闭
}
@@ -640,41 +726,47 @@ myfigure *,
}
});
ed.on('init', function () {
_this.$commonJS.inTinymceButtonClass();
const editorBody = ed.getBody();
_this.editorInstance = ed;
_this.hasInit = true;
_this.$commonJS.inTinymceButtonClass();
if (_this.isAutomaticUpdate) {
_this.$emit('updateChange', _this.value);
const observer = new MutationObserver(() => {
const currentContent = ed.getContent();
if (_this.isAutomaticUpdate) {
_this.$emit('updateChange', currentContent);
// _this.$emit('updateChange', _this.$commonJS.decodeHtml(currentContent));
//暂时注释掉数字公式
// _this.$commonJS.replaceWMathContent(currentContent, (res) => {
// console.log('res at line 451:', res);
// _this.$emit('updateChange', res);
// });
}
});
// 监听子节点和内容的变化
observer.observe(editorBody, { childList: true, subtree: true, characterData: true });
});
// // 定义自定义按钮
// ed.ui.registry.addButton('clearButton', {
// text: 'Empty',
}
_this.content = _this.getSafeContent(_this.value);
// onAction: () => {
// // 插入自定义表格到编辑器中
// ed.setContent('');
// }
// });
_this.handleSetContent(_this.content || '');
// 3. 监听内容变化
ed.on('NodeChange Change KeyUp SetContent', () => {
_this.hasChange = true;
_this.$emit('input', ed.getContent({ format: 'raw' }));
});
// 4. 监听 DOM 变化
const observer = new MutationObserver(() => {
const currentContent = ed.getContent({ format: 'raw' });
if (_this.isAutomaticUpdate) {
_this.$emit('updateChange', currentContent);
}
});
observer.observe(ed.getBody(), {
childList: true,
subtree: true,
characterData: true
});
});
// 定义自定义按钮
ed.ui.registry.addButton('customButtonExportWord', {
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 +780,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', {
@@ -724,7 +816,6 @@ myfigure *,
tempDiv.innerHTML = content;
if (tempDiv.querySelector('table')) {
if (_this.type == 'table') {
// 3. 在这里直接消费外部变量 currentPasteBase64Images
// content = content.replace(new RegExp(`src="${silentPlaceholder}"`, 'gi'), () => {
@@ -765,40 +856,29 @@ myfigure *,
args.content = container.innerHTML; // 更新处理后的内容
});
} else {
// _this.$confirm('检测到粘贴内容包含表格,是否需要以表格形式添加?', '提示', {
// confirmButtonText: '添加表格',
// cancelButtonText: '纯文本添加',
// type: 'info'
// }).then(() => {
// _this.$emit('openAddTable', content);
// return false
// }).catch(() => {
// });
// _this.$confirm('检测到粘贴内容包含表格,是否需要以表格形式添加?', '提示', {
// confirmButtonText: '添加表格',
// cancelButtonText: '纯文本添加',
// type: 'info'
// }).then(() => {
// _this.$emit('openAddTable', content);
// return false
// }).catch(() => {
// });
}
} else {
console.log('Original content:', content); // 输出原始粘贴内容
const mathRegex = /\$\$([\s\S]+?)\$\$|\$([\s\S]+?)\$/g;
content = content.replace(mathRegex, function (match, blockFormula, inlineFormula) {
const formula = blockFormula || inlineFormula;
const mode = blockFormula ? 'block' : 'inline';
console.log(`Matched ${mode} formula:`, formula);
// 1. 修改正则:同时匹配 $$...$$ (块级) 和 $...$ (行内)
// 注意:先匹配双美元符,再匹配单美元符,防止冲突
const mathRegex = /\$\$([\s\S]+?)\$\$|\$([\s\S]+?)\$/g;
return `<wmath data-wrap="${mode}" data-latex="${formula.trim()}">${formula.trim()}</wmath>`;
});
content = content.replace(mathRegex, function (match, blockFormula, inlineFormula) {
// 判断是块级还是行内
const formula = blockFormula || inlineFormula;
const mode = blockFormula ? 'block' : 'inline';
console.log(`Matched ${mode} formula:`, formula);
// 2. 统一改造:标签内只放纯 formula不带 $ 符号
// 属性中保存 data-latex 和 data-wrap
return `<wmath data-wrap="${mode}" data-latex="${formula.trim()}">${formula.trim()}</wmath>`;
});
console.log('Processed content:', content); // 输出处理后的内容
}
// 更新 args.content 为处理后的内容
// 阻止默认的粘贴行为,确保自定义处理优先执行
if (args.event) {
args.event.preventDefault();
args.event.stopPropagation();
@@ -814,103 +894,79 @@ console.log('Processed content:', content); // 输出处理后的内容
}, 10);
},
clear_custom_action: (editor, vm) => {
vm.onClear();
},
init_instance_callback: (editor) => {
if (_this.value) {
editor.setContent('<p>' + _this.value + '</p>');
setTimeout(() => {
window.renderMathJax(_this.tinymceId); // 初始化时渲染 MathJax
}, 10);
}
_this.hasInit = true;
editor.on('NodeChange Change KeyUp SetContent', () => {
this.hasChange = true;
this.$emit('input', editor.getContent());
// console.log('at line 518:', '改变');
// setTimeout(() => {
// window.renderMathJax(); // 初始化时渲染 MathJax
// }, 50);
});
}
// init_instance_callback: (editor) => {
// }
});
if (!window._wmath_listener_registered) {
// 💾 新增公式插入点记录
let latexEditorBookmark = null;
let activeEditorId = null;
// 👂 message 监听器:处理编辑 + 新增两种情况
window.addEventListener('message', function (event) {
const data = event.data;
if (data && (data.type === 'update-wmath' || data.type === 'insert-wmath')) {
const { editorId, wmathId, latex } = data;
const newLatex = latex ? latex.trim() : '';
if (!editorId) return;
const data = event.data;
const targetEditor = tinymce.get(editorId);
if (!targetEditor) return;
if (data && (data.type === 'update-wmath' || data.type === 'insert-wmath')) {
const { editorId, wmathId, latex } = data;
const newLatex = latex ? latex.trim() : '';
if (!editorId) return;
// 尝试寻找现有标签
const targetWmath = wmathId ? targetEditor.dom.select(`wmath[data-id="${wmathId}"]`, targetEditor.getBody())[0] : null;
const targetEditor = tinymce.get(editorId);
if (!targetEditor) return;
if (targetWmath) {
// --- 原有的更新/删除逻辑 ---
if (!newLatex) {
targetEditor.dom.remove(targetWmath);
} else {
targetWmath.setAttribute('data-latex', newLatex);
if (data.wrapMode === 'inline') {
targetWmath.setAttribute('data-wrap', 'inline');
} else {
targetWmath.setAttribute('data-wrap', 'block');
}
targetWmath.innerHTML = newLatex;
}
} else if (newLatex) {
// --- ✨ 新增逻辑:如果找不到现有标签且有 latex 内容 ---
// 生成一个新的唯一 ID (如果你后端或插件没给的话)
const newId = wmathId || 'wmath_' + Date.now();
// 构建新的 wmath 标签字符串
// 这里你可以根据之前选的 wrapMode (块级/行内) 来决定样式
const htmlToInsert = `<wmath data-id="${newId}" data-latex="${newLatex}" data-wrap="${data.wrapMode === 'inline' ? 'inline' : 'block'}">${newLatex}</wmath>`;
// 在当前光标位置插入内容
targetEditor.insertContent(htmlToInsert);
}
const targetWmath = wmathId
? targetEditor.dom.select(`wmath[data-id="${wmathId}"]`, targetEditor.getBody())[0]
: null;
// 统一渲染
setTimeout(() => {
if (typeof renderMathJax === 'function') {
renderMathJax(editorId);
}
}, 10);
}
});
if (targetWmath) {
if (!newLatex) {
targetEditor.dom.remove(targetWmath);
} else {
targetWmath.setAttribute('data-latex', newLatex);
if (data.wrapMode === 'inline') {
targetWmath.setAttribute('data-wrap', 'inline');
} else {
targetWmath.setAttribute('data-wrap', 'block');
}
targetWmath.innerHTML = newLatex;
}
} else if (newLatex) {
const newId = wmathId || 'wmath_' + Date.now();
const htmlToInsert = `<wmath data-id="${newId}" data-latex="${newLatex}" data-wrap="${
data.wrapMode === 'inline' ? 'inline' : 'block'
}">${newLatex}</wmath>`;
targetEditor.insertContent(htmlToInsert);
}
// 统一渲染
setTimeout(() => {
if (typeof renderMathJax === 'function') {
renderMathJax(editorId);
}
}, 10);
}
});
// 🚩 标记为已注册,防止重复
window._wmath_listener_registered = true;
// 导出保存位置函数(你可以在按钮点击时调用它)
window._recordLatexInsertContext = function (editorInstance) {
latexEditorBookmark = editorInstance.selection.getBookmark(2);
activeEditorId = editorInstance.id;
};
}
},
hexToBlob(hex, mimeType) {
const len = hex.length / 2;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return new Blob([bytes], { type: mimeType });
},
const len = hex.length / 2;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return new Blob([bytes], { type: mimeType });
},
updateTableStyles(container) {
var html = this.$commonJS.updateTableStyles(container, this.typesettingType);
var editor = window.tinymce.activeEditor; // 将外部 DOM 内容更新到编辑器
@@ -920,11 +976,11 @@ console.log('Processed content:', content); // 输出处理后的内容
editor.focus(); // 聚焦到编辑器// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
},
//销毁富文本
destroyTinymce() {this.onClear();
destroyTinymce() {
this.onClear();
if (window.tinymce.get(this.tinymceId)) {
window.tinymce.get(this.tinymceId).destroy();
}
},
//设置内容
setContent(value) {
@@ -932,11 +988,13 @@ 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
? 'isTitleH1 pMainH1'
: item.is_h2
? 'isTitleH2'
: item.is_h3
? 'isTitleH3'
: ''
: item.is_h1
? 'Ptitle'
: ''
item.is_h1
? 'isTitleH1 pMainH1'
: item.is_h2
? 'isTitleH2'
: item.is_h3
? 'isTitleH3'
: ''
"
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">
@@ -366,6 +365,7 @@
:id="'editor' + item.am_id"
v-html="highlightText({ am_id: item.am_id, text: item.content }, item.checks ? item.checks : [], item.type)"
></div>
</div>
</div>
</template>
@@ -967,7 +967,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 +978,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 +1275,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 +1476,7 @@ export default {
this.$forceUpdate();
},
handleGlobalClick(e) {
if(this.isPreview)return;
// 如果点在气泡内,不关
if (e.target.closest('.bubble-container')) return;
@@ -1787,8 +1789,7 @@ 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,8 +1818,7 @@ 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 = {
...data,
@@ -1898,7 +1898,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 +2001,7 @@ export default {
},
changeSort(type) {
console.log('type at line 782:', type);
if (this.currentId) {
this.$emit('changeSort', type, this.currentId);
}
@@ -2130,6 +2130,7 @@ export default {
// 3. 核心调用逻辑
async handleOpenLink() {
// 1. 校验是否选中内容
const selectedText = this.currentSelection.label || '';
if (!selectedText) {
@@ -2234,12 +2235,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', {
this.$emit('onEditTitle', {
mainId: this.currentId,
value: value
});
});
}
},
clearHighlight1() {
@@ -2319,7 +2389,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 +2414,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 +2440,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 +2461,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 +2540,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 +2802,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');
this.currentTag = '';
this.currentTagData = null; // 必须重置,防止带入旧数据
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 +2814,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 +2835,11 @@ export default {
await this.getProofreadingList([id]); // 建议 await 异步操作
this.getPList(id);
}
}else{
if (clickedTag) {
this.handleClickJump();
}
}
setTimeout(() => {
this.isInternalAction = false;
@@ -2786,12 +2865,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 +2895,7 @@ export default {
return formattedDate;
},
async goToListComment(data, fn) {
console.log('data at line 993:', data);
await this.goToComment(data.am_id, fn);
},
divOnScroll() {
@@ -2943,7 +3022,10 @@ export default {
text = this.highlightText3(item.text, [], type, item.am_id);
}
}
return text;
// const finalHtml = text.replace(/<(?!(\/?(span|p|div|table|tr|td|th|b|i|strong|em|ul|ol|li|br|img|myh3|myfigure|mytable|blue|wmath)))/gi, '&lt;');
return text;
},
// 改造版DOM 安全高亮
// 仅保留 <wmath>…</wmath> 内部,其他用正则匹配高亮
@@ -3225,6 +3307,8 @@ export default {
html += `<img contenteditable="false" src="${this.imagePath || ''}" alt="" style="width:20px;height:20px;opacity:.6;">`;
}
return html;
},
getProofreadingList(arr) {
@@ -3359,11 +3443,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 +3488,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 +3526,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 +3548,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 +4401,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 +4435,11 @@ wmath {
display: inline-block !important;
width: auto !important;
}
.preview-container{
::v-deep myfigure,
::v-deep mytable {
text-shadow: none !important
}
}
</style>

View File

@@ -930,7 +930,7 @@ if(newData.ami_id) {
},
async created() {
this.isCollapse = localStorage.getItem('isCollapse') == 'true' ? true : false;
console.log('localStorage.getItem', typeof localStorage.getItem('isCollapse'));
this.isShowEditComment();
this.isFresh = false;
this.$nextTick(async () => {

View File

@@ -154,9 +154,11 @@
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>
</span>
@@ -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

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

View File

@@ -73,7 +73,7 @@ module.exports = {
// 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: 'http://mytest.tmrjournals.com/public/index.php/',//新测试环境
changeOrigin: true,
pathRewrite: {
'^/api': ''