Compare commits

2 Commits

Author SHA1 Message Date
8fc0325f35 提交 2025-11-19 15:55:48 +08:00
9fd6b73850 文章详情 Reviewer detail 打开弹窗 2025-11-19 15:05:07 +08:00
9 changed files with 297 additions and 217 deletions

View File

@@ -5,7 +5,7 @@ import qs from 'qs'
// axios全局配置
axios.defaults.timeout = 1000 * 6 * 2; // 超时时间
axios.defaults.timeout = 1000 * 6 * 10 * 3; // 超时时间
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; // 配置请求头

View File

@@ -29,7 +29,7 @@ a {
position: absolute;
left: 260px;
right: 0;
top: 40px;
top: 60px;
bottom: 0;
padding-bottom: 30px;
-webkit-transition: left .3s ease-in-out;

View File

@@ -9,6 +9,10 @@ const baseUrl = '/';
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
// const baseUrl = '/api'
// const mediaUrl = 'http://tougaotest.tmrjournals.com/public/';
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
// const baseUrl = '/api'
//本地(正式环境 )
// const mediaUrl = 'https://submission.tmrjournals.com/public/';

View File

@@ -656,7 +656,13 @@
<td class="review_table_index">
Reviewer {{ reviewerIndex + 1 }}
<el-button
style="margin-left: 10px"
type="text"
@click="crateRevision(iken)"
v-if="iken.can_repeat == 1"
>Re-review</el-button
>
</td>
<td style="position: relative; cursor: pointer">
@@ -672,14 +678,15 @@
>( {{ iken.rated }} )</span
>
<span v-if="iken.state != 0"
style="color: #006699;float: right;"
style="color: #888;float: right;margin-right: 20px;"
@click="handleClick(iken,'detail')"
>Detail</span
>
</td>
<!-- 1st review原逻辑不变 -->
<td @click="handleClick(iken,'question')" style="cursor: pointer">
<span style="display: inline-block; margin-left: 4px; margin-right: 8px">
<td style="cursor: pointer">
<span >
<span style="display: inline-block; margin-left: 4px; margin-right: 8px">
<font
v-if="iken.recommend == 1 || iken.recommend == 2"
style="
@@ -717,13 +724,23 @@
<span v-if="iken.recommend == 1">Minor</span>
<span v-else-if="iken.recommend == 2">Major</span>
<span v-else-if="iken.recommend == 3">reject and resubmission</span>
<span v-else-if="iken.recommend == 4">Reject</span>
<span v-else-if="iken.recommend == 4">Reject</span>
</span>
<span v-if="iken.state != 0"
style="color: #888;float: right;margin-right: 20px;"
@click="handleClick(iken,'question')"
>Detail</span
>
</td>
<!-- 关键按最大重复次数遍历而非仅遍历当前iken.repeat -->
<template v-for="(_1, index1) in maxRepeatReviewCount()">
<td>
<td >
<!-- 补全逻辑判断当前评审者的repeat中是否有第index1条数据 -->
<span
<span
v-if="Array.isArray(iken.repeat) && iken.repeat[index1]"
style="cursor: pointer"
@@ -768,6 +785,12 @@
<span v-else-if="iken.repeat[index1].recommend == 2">Reject</span>
<span v-else-if="iken.repeat[index1].recommend == 3">Revision</span>
<span v-else>No reply</span>
<span v-if="[1,2,3].includes(iken.repeat[index1].recommend)"
style="color: #888;float: right;margin-right: 20px;"
@click="handleClick(iken,'re-review',iken.repeat[index1])"
>Detail</span
>
</span>
<span v-else>
<!-- 无数据:补全空内容(可自定义为“-”“无”等) -->
@@ -843,8 +866,8 @@
<span style="">{{ iken.realname }}</span>
<span
style="color: #006699; float: right;"
<span v-if="[1,2,3].includes(iken.state)"
style="color: #888; float: right;"
@click="handleClickFinal(iken)"
>Detail</span
>
@@ -1726,7 +1749,31 @@ export default {
}
},
methods: {
crateRevision(item) {
// 二次询问
this.$confirm('Do you want to send a review invitation?', 'Tip', {
type: 'warning'
})
.then(() => {
this.$api
.post('api/Reviewer/startRepeatReviewer', {
art_rev_id: item.art_rev_id
})
.then((res) => {
//console.log(res)
if (res.code == 0) {
this.$message.success('A review invitation was successfully sent!');
this.getFinalList()
} else {
this.$message.error(res.msg);
}
})
.catch((err) => {
console.log(err);
});
})
.catch(() => {});
},
handleClickFinal(data) {
this.finalDecisionData={...data}
@@ -1900,12 +1947,12 @@ this.FinalDecisionVisible=true
// console.log('maxItem at line 2142:', maxItem.repeat.length)
return maxItem && maxItem.repeat ? maxItem.repeat.length : 0;
},
handleClick(item,type) {
handleClick(item,type,repeatItem) {
this.reviewerDetail=item
this.reviewerVisible=true
this.$nextTick(()=>{
this.$refs.reviewerDetail.init(item.art_rev_id,type)
this.$refs.reviewerDetail.init(item.art_rev_id,type,repeatItem)
})
@@ -1932,14 +1979,7 @@ this.FinalDecisionVisible=true
this.finalList = [...res.data.final_review];
this.reviewList = res.data.review;
if(this.finalList.length > 0) {
console.log('this.finalList.length at line 1829:', this.finalList.map(item => item.id))
// this.$api
// .post('api/Workbench/updateArticleState', {
// article_id: this.editform.articleId,
// act_id: this.finalList.map(item => item.id),
// type: '3',
// account: localStorage.getItem('U_name')
// })
}
}
}
@@ -2858,7 +2898,7 @@ this.FinalDecisionVisible=true
}
.art_author_ {
padding: 20px 0;
padding: 15px 20px !important;
}
.art_author_ > h2 {

View File

@@ -879,6 +879,16 @@
<p v-if="currentArticleData" class="reviewer-decision-title">
{{ currentArticleData.title }}
</p>
<!-- <div v-if="currentArticleData&&currentArticleData.state!=0" style="overflow: hidden;">
<span style="float: right;">
<b @click.stop="articleReviewer()" class="btnCliArt" style="" >
<i :class="[1,2,4,8].includes(currentArticleData.state) ? 'el-icon-edit' : 'el-icon-view'" style="margin-right: 5px;"></i>Inviting Reviewer {{ [1,2,4,8].includes(currentArticleData.state) ? '' : 'History' }}
</b>
</span>
</div> -->
<div class="art_author_" style="padding: 0;" v-if="reviewList.length > 0">
<div class="fixCard reviewer_decision" style="position: relative">
@@ -921,13 +931,13 @@
>( {{ iken.rated }} )</span
>
<span v-if="iken.state != 0"
style="color: #006699;float: right;"
style="color: #006699;float: right;margin-top: 2px"
@click="handleClick(iken)"
>Detail</span
>
</td>
<!-- 1st review原逻辑不变 -->
<td @click="handleClick(iken)" style="cursor: pointer">
<td style="cursor: pointer">
<span style="display: inline-block; margin-left: 4px; margin-right: 8px">
<font
v-if="iken.recommend == 1 || iken.recommend == 2"
@@ -1032,10 +1042,17 @@
</div>
</div>
</div>
<span style="font-size: 20px; margin-top: 20px; margin-right: 10px; text-align: right; float: right; font-weight: 400">
<div v-if="currentArticleData&&currentArticleData.state!=0" style="overflow: hidden;">
<span style="float: right;">
<span style="font-size: 14px; margin-top: 10px; margin-right: 10px; text-align: right; font-weight: 400">
Average score : <b style="font-size: 18px; color: #db890e">{{ avegeCount(reviewList) }}</b>
</span>
</span>
</div>
</div>
<span slot="footer" class="dialog-footer">
@@ -2033,13 +2050,16 @@ export default {
this.$forceUpdate();
},
//文章送审
articleReviewer(row) {
this.$router.push({
path: 'articleReviewer',
articleReviewer() {
const routeData = this.$router.resolve({
path: 'articleReviewer', // 原路由路径
query: {
id: row.article_id
id: this.currentArticleData.article_id // 原查询参数
}
});
// 新开窗口跳转(第二个参数 '_blank' 表示新窗口)
window.open(routeData.href, '_blank');
},
articleEditorialBoard(row) {
this.$router.push({

View File

@@ -116,8 +116,8 @@
</div> -->
<!-- 对话列表 -->
<div class="kuang_communtion">
<h2>Communication</h2>
<div class="kuang_communtion" >
<h2 >Communication</h2>
<div v-for="item in talkMsgs" class="kuang_communtion_conmt">
<div v-if="item.user_id != artMes.user_id" class="talk_aued">
<p>Editor :</p>

View File

@@ -26,6 +26,7 @@
<el-form-item label="Disclose name or anonymous" label-width="200px">
<span v-if="reviewList.length > 0&&reviewList[0].is_anonymous == 0">Disclose name</span>
<span v-if="reviewList.length > 0&&reviewList[0].is_anonymous == 1">Remain anonymous</span>
<span v-if="reviewList.length== 0">-</span>
</el-form-item>
<!-- <el-form-item label="Status">
<span>{{ mystate(detailDate.state) }}</span>
@@ -1246,6 +1247,8 @@ console.log('at line 1094:', this.reviewList)
this.questionform.comment = res.data.comments;
this.questionform.is_anonymous = res.data.is_anonymous;
this.questionform.type= res.data.type;this.questionform.score = res.data.score;
}else{
}
});
},

View File

@@ -1,14 +1,28 @@
<template>
<el-dialog :title="type=='detail'?'Manuscript reviewer detail':'Feedback questionnaire'" :visible.sync="reviewerVisible" width="1200px" :close-on-click-modal="false">
<el-dialog title="" :visible.sync="reviewerVisible" width="1200px" :close-on-click-modal="false">
<template slot="title" >
<div class="dialog-title">
<p v-if="type=='detail'">Manuscript reviewer detail</p>
<p v-else>Feedback questionnaire
<span style="margin-top: -3px;"> <span v-if="time" class="time">Time{{ formatDate(time) }}</span>
<span v-if="stime" class="time">Response time {{ formatDate(stime) }}</span></span>
</p>
</div>
</template>
<div>
<div class="container" v-if="type=='detail'">
<div class="" v-if="type=='detail'">
<el-row :gutter="10">
<el-col :span="20">
<el-col :span="24">
<div class="form-box" style="width: 100%">
<el-form ref="articleform" :model="detailDate" label-width="130px">
<el-form-item label="Article">
<span>{{ detailDate.article }}</span>
<el-form ref="articleform" :model="detailDate" label-width="140px">
<el-form-item label="Title">
<span style="font-weight: bold;color: #333;">{{ detailDate.article }}</span>
</el-form-item>
<el-form-item label="Reviewer">
<span>{{ detailDate.reviewer }}</span>
@@ -20,8 +34,8 @@
<span>{{ formatDate(detailDate.ctime) }}</span>
</el-form-item>
<el-form-item label="Disclose name or anonymous" label-width="200px">
<span v-if="reviewList.length > 0 && reviewList[0].is_anonymous == 0">Disclose name</span>
<span v-if="reviewList.length > 0 && reviewList[0].is_anonymous == 1">Remain anonymous</span>
<span v-if="detailDate.is_anonymous == 0">Disclose name</span>
<span v-if="detailDate.is_anonymous == 1">Remain anonymous</span>
</el-form-item>
<el-form-item v-if="canRepeat == 1">
@@ -30,60 +44,7 @@
</el-form>
</div>
</el-col>
<el-col :span="8" v-if="recordList && recordList.length > 0">
<div class="clearfix fsheader">
<h4>Peer-review Archive</h4>
</div>
<div class="time">
<el-timeline>
<el-timeline-item
reverse="true"
:timestamp="item.ctime | formatDatehms"
placement="top"
v-for="(item, index) in recordList"
:key="index"
>
<el-card>
<h4></h4>
<div>
<div v-if="index == recordList.length - 1">
<!-- 初审 -->
<el-tag>Under review</el-tag>
<p style="margin-top: 10px">
Comments:
<el-button
style="margin-left: 10px"
type="text"
@click="showUnderReview(item)"
icon="el-icon-view"
>Details</el-button
>
</p>
</div>
<div v-else>
<!-- 复审 -->
<el-tag type="success">Second review</el-tag>
<p style="margin-top: 10px">
Comments:
<el-button
style="margin-left: 10px"
type="text"
@click="showSecondReview(item)"
icon="el-icon-view"
>Details</el-button
>
</p>
<p v-if="item.stime > 0" style="" class="stime">
Response time: {{ item.stime | formatDatehms }}
</p>
<p v-else style="" class="stime">Response time: Re-reviewing...</p>
</div>
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</el-col>
</el-row>
</div>
<common-review-article v-if="type=='question'"
@@ -96,8 +57,7 @@
:journal_id="journal_id"
></common-review-article>
<!-- <el-dialog title="Second review questionnaire" :visible.sync="FdialogFormVisible" width="900px" @close="closeSecDia" :close-on-click-modal="false"> -->
<el-form :model="ReReviewQuestion" ref="question" label-width="300px" label-position="top">
<el-form :model="ReReviewQuestion" ref="question" label-width="300px" label-position="top" v-if="type=='re-review'">
<el-divider content-position="center">REFEREE'S RECOMMENDATIONS</el-divider>
<el-form-item label="REFEREE'S RECOMMENDATIONS" prop="recommend">
<el-radio-group v-model="ReReviewQuestion.recommend" style="line-height: 30px">
@@ -158,7 +118,10 @@ export default {
questionform: { },
canRepeat: null,
undeQuestion: {},
ReReviewQuestion: {}
ReReviewQuestion: {},
repeatItem: {},
time:"",//审稿时间
stime:"",//复审回复时间
};
},
created: function () {
@@ -167,8 +130,12 @@ export default {
computed: {},
methods: {
init(art_rev_id,type) {
init(art_rev_id,type,repeatItem) {
this.time="";
this.stime="";
this.type=type;
this.repeatItem=repeatItem
if (art_rev_id) {
this.dateId = art_rev_id;
this.detailDate = {
@@ -187,9 +154,13 @@ export default {
}
if(type=='question'){
this. questionform={}
this.initquesion();
}
if(type=='re-review'){
this. ReReviewQuestion={}
this.initReReviewQuestion();
}
this.$api
.post('api/Workbench/updateArticleState', {
article_id: this.articleId,
@@ -349,24 +320,60 @@ export default {
background: 'rgba(0, 0, 0, 0.7)'
});
this.$api
.post('api/Reviewer/getartrevdate', {
revid: this.dateId,
human: 'editor'
.post('api/Workbench/getArticleReviewDetail', {
article_id:this.$route.query.id,
art_rev_id:this.dateId ,
account: localStorage.getItem('U_name')
})
.then((res) => {
this.detailDate.artrevid = res.art_rev_id;
this.detailDate.article = res.article_title;
this.detailDate.reviewer = res.account;
this.detailDate.reviewer_email = res.user_email;
this.detailDate.ctime = res.ctime;
this.detailDate.state = res.state;
this.txt_mess = res;
this.canRepeat = res.can_repeat;
this.journal_id = res.journal_id;
this.articleId = res.article_id;
if(res.status == 1){
this.detailDate.artrevid = res.data.article_reviewer.art_rev_id;
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.ctime = res.data.article_reviewer.ctime;
this.detailDate.is_anonymous = res.data.article_reviewer.is_anonymous;
this.articleId = res.data.article.article_id;
loading.close();
this.reviewerVisible = true;
}else{
loading.close();
this.$message.error(res.msg);
return false;
}
})
.catch((err) => {
loading.close();
console.log(err);
});
},
initReReviewQuestion() {
console.log('this.repeatItem at line 335:', this.repeatItem)
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
this.$api.post('api/Workbench/getArticleReviewDetail', {
article_id:this.$route.query.id,
art_rev_id:this.dateId,
art_rev_rep_id:this.repeatItem.art_rev_rep_id,
account: localStorage.getItem('U_name')
})
.then((res) => {
if(res.status==1){
this.time=res.data.article_reviewer_repeat.ctime;
this.stime=res.data.article_reviewer_repeat.stime;
this.ReReviewQuestion = res.data.article_reviewer_repeat;
loading.close();
this.reviewerVisible = true;
}
})
.catch((err) => {
loading.close();
@@ -388,6 +395,7 @@ export default {
})
.then((res) => {
if (res.code == 0) {
this.time=res.data.ctime;
this.showUnderReview(res.data)
this.reviewerVisible = true;
loading.close();
@@ -548,4 +556,24 @@ td {
.overflow-x-auto {
overflow-x: auto;
}
.dialog-title {
width: 100%;
line-height: 24px;
font-size: 18px;
color: #303133;
}
.dialog-title p{
width:calc(100% - 30px);
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.time{
color: #888;line-height: 20px;
font-size: 14px;
margin-left: 20px;
display: inline-block
}
</style>

View File

@@ -1,111 +1,96 @@
<!--对话模块-->
<template>
<div class="kuang_communtion" v-loading="loading">
<h2>
Communication
</h2>
<div :style="{'max-height': height+'px'}" style="overflow: auto;">
<div v-for="(item, index) in talkMsgs" class="kuang_communtion_conmt">
<div v-if="item.user_id == msgform.user_id" class="talk_aued">
<p>
Author :
</p>
<el-card>Dear Editor,
<p style="white-space: pre-wrap;">{{item.ad_content}}</p>
</el-card>
<b>{{formatDate(item.ad_ctime)}}</b>
</div>
<div v-if="item.user_id != msgform.user_id" class="talk_aued talk_edit">
<p>
Editor :
</p>
<el-card>
<p style="white-space: pre-wrap;">{{item.ad_content}}</p>
</el-card>
<b>{{formatDate(item.ad_ctime)}}</b>
</div>
</div>
</div>
<div class="kuang_communtion" v-loading="loading">
<h2 :style="{ 'margin-bottom': talkMsgs && talkMsgs.length > 0 ? '15px' : '0' }">Communication</h2>
<div :style="{ 'max-height': height + 'px' }" style="overflow: auto">
<div v-for="(item, index) in talkMsgs" class="kuang_communtion_conmt">
<div v-if="item.user_id == msgform.user_id" class="talk_aued">
<p>Author :</p>
<el-card
>Dear Editor,
<p style="white-space: pre-wrap">{{ item.ad_content }}</p>
</el-card>
<b>{{ formatDate(item.ad_ctime) }}</b>
</div>
<div v-if="item.user_id != msgform.user_id" class="talk_aued talk_edit">
<p>Editor :</p>
<el-card>
<p style="white-space: pre-wrap">{{ item.ad_content }}</p>
</el-card>
<b>{{ formatDate(item.ad_ctime) }}</b>
</div>
</div>
</div>
<div class="kuang_communtion_input">
<p v-if="talkMsgs"></p>
<el-input type="textarea" rows="3" v-model="msgform.ad_content" placeholder="Editor messages" resize="none">
</el-input>
<div class="kuang_communtion_input_text">
Dear Editor, through this window, you can have informal communication with the author. Please be aware
of the
prompt reply, standard use of English, and no offensive, insulting, discriminatory language.
<el-button type="primary" @click="saveMsg">Send</el-button>
</div>
</div>
</div>
<div class="kuang_communtion_input">
<p v-if="talkMsgs"></p>
<el-input type="textarea" rows="3" v-model="msgform.ad_content" placeholder="Editor messages" resize="none"> </el-input>
<div class="kuang_communtion_input_text">
Dear Editor, through this window, you can have informal communication with the author. Please be aware of the prompt reply,
standard use of English, and no offensive, insulting, discriminatory language.
<el-button type="primary" @click="saveMsg">Send</el-button>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
export default {
props: {
talkMsgs: {
type: Array,
required: true
},
msgform: {
type: Object,
required: true
},
height: {
type: Number,
},
// loading: {
// type: Boolean,
// required: true
// }
},
components: {},
data() {
return {
username: localStorage.getItem('U_name'),
loading:false
}
},
computed: {},
methods: {
formatDate(timestamp) {
var date = new Date(timestamp * 1000); //时间戳为10位需*1000时间戳为13位的话不需乘1000
var Y = date.getFullYear() + '-';
var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
var D = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
var h = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
var m = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
var s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
return Y + M + D + ' ' + h + ':' + m + ':' + s;
},
// 留言弹出框
saveMsg() {
if (this.msgform.ad_content == '') {
this.$message.error('Please input messages!');
return false;
}
this.loading = true;
this.$api
.post('api/Article/pushArticleDialog', this.msgform)
.then((res) => {
this.loading = false;
// this.$message.success('Sent successfully');
this.$emit('talksave',true) // 传递成功信号
// setTimeout(()=>{
// this.$router.go(0);
// },1000)
});
},
}
}
import Vue from 'vue';
export default {
props: {
talkMsgs: {
type: Array,
required: true
},
msgform: {
type: Object,
required: true
},
height: {
type: Number
}
// loading: {
// type: Boolean,
// required: true
// }
},
components: {},
data() {
return {
username: localStorage.getItem('U_name'),
loading: false
};
},
computed: {},
methods: {
formatDate(timestamp) {
var date = new Date(timestamp * 1000); //时间戳为10位需*1000时间戳为13位的话不需乘1000
var Y = date.getFullYear() + '-';
var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
var D = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
var h = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
var m = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
var s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
return Y + M + D + ' ' + h + ':' + m + ':' + s;
},
// 留言弹出框
saveMsg() {
if (this.msgform.ad_content == '') {
this.$message.error('Please input messages!');
return false;
}
this.loading = true;
this.$api.post('api/Article/pushArticleDialog', this.msgform).then((res) => {
this.loading = false;
// this.$message.success('Sent successfully');
this.$emit('talksave', true); // 传递成功信号
// setTimeout(()=>{
// this.$router.go(0);
// },1000)
});
}
}
};
</script>
<style scoped>
</style>
<style scoped></style>