作者背调

This commit is contained in:
2026-06-05 14:33:24 +08:00
parent 77703a855a
commit e57e06be95
10 changed files with 861 additions and 114 deletions

View File

@@ -947,6 +947,14 @@ const en = {
selectTemplateStyleFirst: 'Please select template and style first',
recipientLimit: 'You can add up to {count} recipient(s)',
recipientLimitPlaceholder: 'Limit reached (max {count})',
sendBatchTitle: 'Send result',
sendBatchSuccess: 'Sent successfully:',
sendBatchFail: 'Failed to send:',
sendBatchConfirm: 'OK',
sendProgressTitle: 'Sending mail',
sendProgressRunning: 'Sending… {done}/{total} completed',
sendProgressAllSuccess: 'All sent successfully ({count})',
sendProgressDone: 'Done: {ok} sent, {fail} failed',
backToInbox: 'Back to email list',
},
home: {
@@ -1287,6 +1295,8 @@ const en = {
addTemplateBtn: 'Go add templates',
templateTab: 'Template',
styleTab: 'Style',
noStyle: 'No style',
noStyleDesc: 'Apply template body only, without header/footer',
loading: 'Loading...',
refresh: 'Refresh'
},
@@ -1627,6 +1637,10 @@ const en = {
dialogIntroduction: 'Introduction :',
cancel: 'Cancel'
},
articleDetailEditor: {
reReviewNotifyBtn: 'Notify re-review',
reReviewNotifyTitle: 'Notify re-review'
},
refRelevance: {
startDetect: 'Batch Audit Reference Context',
startDetectTip:

View File

@@ -932,6 +932,14 @@ const zh = {
selectTemplateStyleFirst: '请先选择模板和风格',
recipientLimit: '最多只能添加 {count} 个收件人',
recipientLimitPlaceholder: '已达上限(最多 {count} 个)',
sendBatchTitle: '发送结果',
sendBatchSuccess: '发送成功:',
sendBatchFail: '发送失败:',
sendBatchConfirm: '确定',
sendProgressTitle: '正在发送邮件',
sendProgressRunning: '正在发送,已完成 {done}/{total}',
sendProgressAllSuccess: '全部发送成功,共 {count} 封',
sendProgressDone: '发送完成:成功 {ok} 封,失败 {fail} 封',
backToInbox: '返回收件箱',
},
home: {
@@ -1267,6 +1275,8 @@ const zh = {
addTemplateBtn: '去新增模板',
templateTab: '模版',
styleTab: '样式',
noStyle: '不使用样式',
noStyleDesc: '仅应用模板正文,不套页眉页脚',
loading: '加载中...',
refresh: '刷新'
},
@@ -1607,6 +1617,10 @@ const zh = {
dialogIntroduction: '简介:',
cancel: '取消'
},
articleDetailEditor: {
reReviewNotifyBtn: '通知复审',
reReviewNotifyTitle: '通知复审'
},
refRelevance: {
startDetect: '全量引文相关性核查',
startDetectTip: '一键启动对本篇稿件全部参考文献与正文语境的 AI 一致性核查',

View File

@@ -812,9 +812,14 @@
>
</span>
<span v-else>
<!-- 无数据:补全空内容(可自定义为“-”“无”等) -->
<span>-</span>
<el-button
v-if="index1 === 0 && showReReviewNotifyBtn(iken)"
type="text"
size="mini"
style="margin-left: 8px"
@click.stop="openReReviewNotifyMail(iken)"
>{{ $t('articleDetailEditor.reReviewNotifyBtn') }}</el-button>
</span>
</td>
</template>
@@ -1126,6 +1131,16 @@
</el-dialog>
<!-- 审稿详情 start-->
<reviewerDetail ref="reviewerDetail" v-if="reviewerVisible" :reviewerDetail="reviewerDetail" destroy-on-close></reviewerDetail>
<re-review-notify-mail-dialog
:visible.sync="reReviewNotifyVisible"
:reviewer="reReviewNotifyReviewer"
:article-id="editform.articleId"
:article-title="form.title"
:accept-sn="form.accept_sn"
:journal-id="form.journal"
:journal-name="form.journal_name"
:journal-url="form.journal_url"
/>
<!-- 审稿详情 end-->
<el-dialog title="Edit H Index" :visible.sync="HEditVisible" width="400px" :close-on-click-modal="false">
<div style="margin: 0 0 20px 10px; font-weight: bold">{{ HIxForm.realname }}</div>
@@ -1547,11 +1562,13 @@
import timetalk from './time_talk';
import reviewerDetail from '../../components/page/components/articleDetail/reviewerdetail.vue';
import FigureCopyright from '../../components/page/components/articleDetail/FigureCopyright.vue';
import ReReviewNotifyMailDialog from '../../components/page/components/articleDetail/ReReviewNotifyMailDialog.vue';
export default {
components: {
timetalk,
reviewerDetail,
FigureCopyright
FigureCopyright,
ReReviewNotifyMailDialog
},
data() {
return {
@@ -1561,6 +1578,8 @@ export default {
finalDecisionData: {},
FinalDecisionVisible: false,
reviewerVisible: false,
reReviewNotifyVisible: false,
reReviewNotifyReviewer: null,
expanded: false,
corresVisible: false,
isShowAuthorInfo: false,
@@ -1658,6 +1677,8 @@ export default {
articleId: this.$route.query.id,
journal: '',
journalname: '',
journal_name: '',
journal_url: '',
username: '',
title: '',
accept_sn: '',
@@ -2272,6 +2293,23 @@ export default {
this.$refs.reviewerDetail.init(item.art_rev_id, type, repeatItem);
});
},
showReReviewNotifyBtn(reviewer) {
if (!this.isReReviewNotifyEnabled()) return false;
if (!reviewer) return false;
const hasFirstReview =
Number(reviewer.state) !== 0 && [1, 2, 3, 4].includes(Number(reviewer.recommend));
if (!hasFirstReview) return false;
const second = Array.isArray(reviewer.repeat) ? reviewer.repeat[0] : null;
if (!second) return true;
return ![1, 2, 3].includes(Number(second.recommend));
},
isReReviewNotifyEnabled() {
return Object.prototype.hasOwnProperty.call(this.$route.query, 'zy');
},
openReReviewNotifyMail(reviewer) {
this.reReviewNotifyReviewer = reviewer ? { ...reviewer } : null;
this.reReviewNotifyVisible = true;
},
goReviewerDetail(id) {
console.log('id at line 1112:', id);
@@ -2694,7 +2732,9 @@ export default {
: '';
this.form.title = res.article.title;
this.form.journal = res.article.journal_id;
this.form.journalname = res.article.journalname;
this.form.journal_name = res.article.journal_name || res.article.journalname || '';
this.form.journalname = res.article.journalname || res.article.journal_name || '';
this.form.journal_url = res.article.journal_url || res.article.website || res.article.journal_website || '';
this.form.abstrart = res.article.abstrart;
this.form.accept_sn = res.article.accept_sn;
this.form.scoring = res.article.scoring;

View File

@@ -275,41 +275,40 @@
>
<div style="display: flex; align-items: center; justify-content: space-between">
<div style="display: flex; align-items: center; width: calc(100% - 120px)">
<span class="reportIcon" style="cursor: pointer" @click="linkEmail(item, v.art_aut_id)"
<span class="reportIcon"
><i class="el-icon-s-custom"></i
></span>
<span
@click="linkEmail(item, v.art_aut_id)"
style="
max-width: calc(100% - 60px);
white-space: nowrap;
display: inline-block;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
"
>{{ v.realname || v.email || '-' }}
<span class="author-name">{{ v.realname || v.email || '-' }}</span>
<span class="author-action-icons">
<span
class="author-action-btn author-action-btn--mail"
title="Send email"
@click.stop="linkEmail(item, v.art_aut_id)"
>
<svg class="author-action-svg" viewBox="0 0 16 16" aria-hidden="true">
<path
fill="currentColor"
d="M2 3.5A1.5 1.5 0 0 1 3.5 2h9A1.5 1.5 0 0 1 14 3.5v9A1.5 1.5 0 0 1 12.5 14h-9A1.5 1.5 0 0 1 2 12.5v-9zm1.2-.5 4.8 3.6L12.8 3H3.2zM3 4.3v7.7h10V4.3l-4.5 3.4a.6.6 0 0 1-.7 0L3 4.3z"
/>
</svg>
</span>
<span
class="author-action-btn author-action-btn--search"
title="Search author"
@click.stop="openAuthorSearch(v)"
>
<svg class="author-action-svg" viewBox="0 0 16 16" aria-hidden="true">
<path
fill="currentColor"
d="M7 1.25a5.75 5.75 0 1 1 0 11.5 5.75 5.75 0 0 1 0-11.5zm0 1.3a4.45 4.45 0 1 0 0 8.9 4.45 4.45 0 0 0 0-8.9z"
/>
<path
fill="currentColor"
d="M11.05 11.05a.65.65 0 0 1 .92 0l2.98 2.98a.65.65 0 1 1-.92.92l-2.98-2.98a.65.65 0 0 1 0-.92z"
/>
</svg>
</span>
</span>
<svg
@click="linkEmail(item, v.art_aut_id)"
t="1763011629734"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="11794"
width="24"
height="16"
style="margin-top: 1.5px; margin-left: 4px; cursor: pointer"
>
<path
d="M780.8 0c134.3168 0 243.2 108.8832 243.2 243.2v537.6c0 134.3168-108.8832 243.2-243.2 243.2H243.2c-134.3168 0-243.2-108.8832-243.2-243.2V243.2C0 108.8832 108.8832 0 243.2 0h537.6z m5.3568 604.4864c-7.9936-6.496-13.4912-8.4928-16.4864-5.9904-3.0016 2.496-4.4992 8.2432-4.4992 17.2352v32.9792c0 8.9984-2.1248 15.2448-6.368 18.7392-4.2496 3.5008-11.3728 5.248-21.3632 5.248h-34.432a455.7504 455.7504 0 0 1-17.664-0.3776 357.76 357.76 0 0 0-15.36-0.3712h-14.9952c-11.4944 0-19.488 1.4976-23.9872 4.4992-4.4928 2.9952-6.7456 11.488-6.7456 25.4848v38.2208c0 8 3.3728 14.496 10.1184 19.488 6.7456 4.9984 13.6192 7.5008 20.6144 7.5008h86.944c7.0016 0 12.6208 2.7456 16.8704 8.2432 4.2432 5.4976 6.368 12.4928 6.368 20.992v33.728c0 13.4848 2.1248 19.9808 6.3744 19.4816 4.2432-0.4992 11.616-4.992 22.1056-13.4912a919.7312 919.7312 0 0 0 28.1088-22.4832c10.24-8.4992 20.736-16.992 31.488-25.4848a1926.9952 1926.9952 0 0 0 31.104-25.1136c9.9904-8.2432 18.7328-15.36 26.2272-21.3568 5.504-4 8.128-10.496 7.872-19.488-0.2496-8.9984-3.6224-15.9936-10.112-20.992a1228.544 1228.544 0 0 1-28.864-22.8608 1902.9184 1902.9184 0 0 0-32.9792-26.2336 2137.6256 2137.6256 0 0 1-32.6016-25.856 968.064 968.064 0 0 0-27.7376-21.7408zM180.5248 389.376c-7.488-5.4976-13.7344-6.8672-18.7328-4.1216-4.992 2.752-7.4944 9.3696-7.4944 19.8656v277.3248c0 10.496 1.248 20.6144 3.744 30.3616a62.72 62.72 0 0 0 13.4976 25.856c6.496 7.4944 15.488 13.6192 26.9824 18.368 11.488 4.7424 26.2336 7.1168 44.224 7.1168h56.16c22.464 0.0192 48.2112 0.1408 77.2544 0.3776 30.976 0.2496 64.2112 0.3712 99.6864 0.3712h103.4368a42.112 42.112 0 0 1-0.7488-8.2432v-61.4656c0-7.9936 0.6272-16.3648 1.8752-25.1072 1.248-8.7424 3.7504-16.6144 7.4944-23.6096a48.5568 48.5568 0 0 1 15.3664-17.2416c6.496-4.4928 14.9888-6.7456 25.4848-6.7456h87.6992c0-1.4976 0.2496-3.4944 0.7488-5.9968 0-2.496 0.128-5.4976 0.3712-8.992 0.256-3.5008 0.3776-8 0.3776-13.4912 0-8 1.376-16.992 4.1216-26.9824 2.752-9.9968 6.7456-18.7392 11.9936-26.24 5.248-7.488 11.9936-12.3648 20.2368-14.6112 8.2432-2.2464 17.8624 0.3712 28.8576 7.872 10.9952 6.9952 23.104 15.488 36.352 25.4848a1266.0096 1266.0096 0 0 1 40.8512 32.2304V396.864c0-7.0016-2.624-11.1232-7.872-12.3712-5.248-1.248-12.6144 1.1264-22.112 7.1232-2.9952 1.9968-8 5.248-14.9888 9.7408a5917.1648 5917.1648 0 0 1-22.8608 14.6176c-8.2432 5.248-16.992 10.8672-26.24 16.864a12420.544 12420.544 0 0 1-25.4784 16.4928c-7.744 4.992-14.24 9.2416-19.488 12.736-5.248 3.5008-8.3712 5.504-9.3696 5.9968l-94.4448 58.464c-3.4944 2.0032-8.8704 5.248-16.1152 9.7472a2571.0336 2571.0336 0 0 1-23.232 14.24c-8.2496 4.992-15.872 9.6192-22.8608 13.8688a3873.9264 3873.9264 0 0 1-14.24 8.6208c-2.496 1.4976-6.2464 3.1232-11.2448 4.864-4.9984 1.7536-10.624 2.88-16.864 3.3792-6.2464 0.4992-12.7424 0.2496-19.488-0.7488a46.4064 46.4064 0 0 1-19.1168-7.4944l-20.2368-13.4912A3314.24 3314.24 0 0 0 432 544.896a2994.4 2994.4 0 0 1-19.8592-13.12 260.5568 260.5568 0 0 0-14.2464-8.992c-5.4912-3.0016-11.8656-3.6224-19.1104-1.8752-7.2448 1.7472-13.12 4.6208-17.6128 8.6208-2.496 1.4976-7.1232 5.8688-13.8688 13.12a2662.8928 2662.8928 0 0 0-21.3632 23.232 654.7392 654.7392 0 0 1-21.3568 22.4832c-6.7456 6.752-10.8736 10.624-12.3712 11.6224-7.4944 4.4928-12.992 5.4976-16.4864 2.9952-3.5008-2.496-4-7.2448-1.504-14.24 1.504-4 4.3776-9.7408 8.6208-17.2416 4.2496-7.488 8.6208-15.36 13.12-23.6096a770.4192 770.4192 0 0 0 12.736-24.3584c4-8 7.0016-14.4896 8.9984-19.488 2.496-4.992 3.2512-10.8672 2.2464-17.6128-0.9984-6.7456-4.992-12.3712-11.9872-16.864-2.0032-0.9984-6.1248-3.5008-12.3712-7.4944a3333.7728 3333.7728 0 0 0-22.4832-14.2464 3710.0032 3710.0032 0 0 1-27.7376-17.6128 1545.984 1545.984 0 0 0-28.1024-17.6128 915.1872 915.1872 0 0 1-23.2384-14.6176 494.88 494.88 0 0 0-13.4912-8.6208z m17.9968-139.4112c-15.9936 0-27.232 3.744-33.728 11.2448-6.5024 7.488-9.7472 17.7344-9.7472 30.7264v6.7456c0 4.4992 0.7488 8 2.2464 10.496 1.504 2.496 4.9984 5.7472 10.496 9.7408l306.56 183.6416c7.4944 4.4928 14.24 8.1216 20.2368 10.8672 5.9968 2.752 10.496 4.1216 13.4912 4.1216 2.496-0.4992 7.3728-2.496 14.6176-5.9968a299.5712 299.5712 0 0 1 18.368-8.2432l306.56-184.384c6.9952-5.504 10.9888-9.12 11.9936-10.8736 0.9984-1.7472 1.4976-4.8704 1.4976-9.3696l-0.7488-8.2432c0-12.4928-3.2512-22.2336-9.7472-29.2288-6.496-7.0016-17.984-10.496-34.4768-10.496z"
fill="#409eff"
p-id="11795"
data-spm-anchor-id="a313x.search_index.0.i17.7fe43a81QbZr1X"
class="selected"
></path>
</svg>
</div>
<div style="max-width: 260px; width: auto" v-if="v.user_id">
@@ -2758,6 +2757,16 @@ export default {
query: data
});
},
openAuthorSearch(author) {
const artAutId = author && author.art_aut_id != null && author.art_aut_id !== '' ? author.art_aut_id : '';
if (!artAutId) {
return;
}
window.open(
'https://submission.tmrjournals.com/api/author/index?artAutId=' + encodeURIComponent(artAutId),
'_blank'
);
},
//超出传送文件个数限制
alertlimit() {
this.$message.error('The maximum number of uploaded files has been exceeded');
@@ -3382,6 +3391,58 @@ td {
align-items: center;
justify-content: center;
}
.author-name {
max-width: calc(100% - 100px);
white-space: nowrap;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
color: #303133;
}
.author-action-icons {
display: inline-flex;
align-items: center;
gap: 4px;
margin-left: 8px;
flex-shrink: 0;
}
.author-action-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 5px;
cursor: pointer;
line-height: 1;
color: #fff;
flex-shrink: 0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.14);
transition: background 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.author-action-svg {
width: 13px;
height: 13px;
display: block;
}
.author-action-btn--mail {
background: linear-gradient(180deg, #66b1ff 0%, #409eff 100%);
box-shadow: 0 1px 4px rgba(64, 158, 255, 0.32);
}
.author-action-btn--mail:hover {
background: linear-gradient(180deg, #79bbff 0%, #53a8ff 100%);
box-shadow: 0 3px 8px rgba(64, 158, 255, 0.38);
transform: translateY(-1px);
}
.author-action-btn--search {
background: linear-gradient(180deg, #0088bf 0%, #006699 100%);
box-shadow: 0 1px 4px rgba(0, 102, 153, 0.32);
}
.author-action-btn--search:hover {
background: linear-gradient(180deg, #0099cc 0%, #007bb8 100%);
box-shadow: 0 3px 8px rgba(0, 102, 153, 0.38);
transform: translateY(-1px);
}
.author-information {
cursor: pointer;

View File

@@ -0,0 +1,307 @@
<template>
<el-dialog
:title="$t('articleDetailEditor.reReviewNotifyTitle')"
:visible.sync="dialogVisible"
width="920px"
top="5vh"
append-to-body
:close-on-click-modal="false"
@open="handleOpen"
@closed="handleClosed"
>
<div v-loading="loading" class="re-review-mail-dialog">
<div class="mail_shuru">
<span class="mail_tit">{{ $t('mailboxSend.to') }}</span>
<el-tag v-if="toEmail" type="info" size="small">{{ toEmail }}</el-tag>
<span v-else class="mail-empty-tip">-</span>
</div>
<div class="mail_shuru">
<span class="mail_tit">{{ $t('mailboxSend.subject') }}</span>
<el-input v-model="mailForm.subject" class="mail_inp" />
</div>
<div class="mail-editor-wrap">
<ckeditor-mail
v-if="editorMounted"
ref="mailEditor"
:key="editorId"
:id="editorId"
v-model="mailForm.content"
:use-safe-init-content="true"
/>
</div>
<div class="mail-footer-bar">
<div class="sender-info">
<span class="sender-label">{{ $t('mailboxSend.sender') }}</span>
<span class="sender-content">
<template v-if="senderName && senderEmail">{{ senderName }} &lt;{{ senderEmail }}&gt;</template>
<template v-else>-</template>
</span>
</div>
<div class="action-buttons">
<el-button @click="dialogVisible = false">{{ $t('mailboxSend.cancel') || 'Cancel' }}</el-button>
<el-button type="primary" icon="el-icon-s-promotion" :loading="sendLoading" @click="handleSend">
{{ $t('mailboxSend.send') }}
</el-button>
</div>
</div>
</div>
</el-dialog>
</template>
<script>
import CkeditorMail from '@/components/page/components/email/CkeditorMail.vue';
import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview';
const RE_REVIEW_TEMPLATE_ID = '82';
/** 复审通知固定发件邮箱 j_email_id */
const RE_REVIEW_SENDER_J_EMAIL_ID = '23';
export default {
name: 'ReReviewNotifyMailDialog',
components: {
CkeditorMail
},
props: {
visible: {
type: Boolean,
default: false
},
reviewer: {
type: Object,
default: null
},
articleId: {
type: [String, Number],
default: ''
},
articleTitle: {
type: String,
default: ''
},
/** 稿件 SN对应接口字段 accept_sn */
acceptSn: {
type: String,
default: ''
},
journalId: {
type: [String, Number],
default: ''
},
/** 期刊名称,对应 getArticleDetail 返回字段 journal_name */
journalName: {
type: String,
default: ''
},
/** 期刊官网,对应 getArticleDetail 返回字段 */
journalUrl: {
type: String,
default: ''
}
},
data() {
return {
loading: false,
sendLoading: false,
editorMounted: false,
editorId: 're-review-mail-' + Date.now(),
mailForm: {
subject: '',
content: ''
},
toEmail: '',
senderName: '',
senderEmail: '',
jEmailId: ''
};
},
computed: {
dialogVisible: {
get() {
return this.visible;
},
set(val) {
this.$emit('update:visible', val);
}
}
},
methods: {
handleOpen() {
this.editorMounted = false;
this.editorId = 're-review-mail-' + Date.now();
this.resetForm();
this.loadMailData();
},
handleClosed() {
this.editorMounted = false;
this.resetForm();
},
resetForm() {
this.mailForm = { subject: '', content: '' };
this.toEmail = '';
this.senderName = '';
this.senderEmail = '';
this.jEmailId = '';
},
async loadMailData() {
this.loading = true;
this.editorMounted = false;
try {
await Promise.all([this.loadReviewerEmail(), this.loadSenderAccount()]);
await this.loadTemplate();
await this.$nextTick();
this.editorMounted = true;
} catch (e) {
console.error('loadMailData failed', e);
} finally {
this.loading = false;
}
},
async loadReviewerEmail() {
const reviewer = this.reviewer || {};
if (reviewer.email) {
this.toEmail = reviewer.email;
return;
}
if (!reviewer.art_rev_id || !this.articleId) return;
const res = await this.$api.post('api/Workbench/getArticleReviewDetail', {
article_id: this.articleId,
art_rev_id: reviewer.art_rev_id,
account: localStorage.getItem('U_name')
});
if (res && res.status === 1 && res.data && res.data.article_reviewer) {
this.toEmail = res.data.article_reviewer.email || '';
}
},
async loadSenderAccount() {
this.jEmailId = RE_REVIEW_SENDER_J_EMAIL_ID;
const emailRes = await this.$api.post('api/email_client/getOneEmail', {
j_email_id: this.jEmailId
});
if (emailRes && emailRes.code === 0 && emailRes.data && emailRes.data.email) {
const email = emailRes.data.email;
this.senderName = email.smtp_from_name || email.smtp_user || '';
this.senderEmail = email.smtp_user || '';
}
},
async loadTemplate() {
const res = await this.$api.post('api/mail_template/getTemplate', {
template_id: RE_REVIEW_TEMPLATE_ID
});
if (!res || res.code !== 0) {
this.$message.error((res && res.msg) || 'Failed to load mail template');
return;
}
const data = (res && res.data) || {};
const t = data.template || data.detail || data || {};
const vars = {
title: this.articleTitle,
accept_sn: this.acceptSn,
journal_name: this.journalName,
journal_url: this.journalUrl
};
const subjectRaw = t.subject != null ? String(t.subject) : '';
const bodyRaw = t.body_html != null ? t.body_html : t.body != null ? t.body : '';
this.mailForm.subject = applyReviewNotifyTemplateVariables(subjectRaw, vars);
this.mailForm.content = applyReviewNotifyTemplateVariables(String(bodyRaw || ''), vars);
},
async handleSend() {
if (!this.toEmail) {
this.$message.warning(this.$t('mailboxSend.validateTo'));
return;
}
if (!this.mailForm.subject) {
this.$message.warning(this.$t('mailboxSend.validateSubject'));
return;
}
if (!this.jEmailId) {
this.$message.warning(this.$t('mailboxSend.needAccount'));
return;
}
if (!this.journalId) {
this.$message.warning(this.$t('mailboxSend.needAccount'));
return;
}
this.sendLoading = true;
try {
let content = this.mailForm.content || '';
const editorRef = this.$refs.mailEditor;
if (editorRef && typeof editorRef.getInlinedContent === 'function') {
content = editorRef.getInlinedContent() || content;
}
const res = await this.$api.post('api/email_client/sendOne', {
to_email: this.toEmail,
subject: this.mailForm.subject,
content,
j_email_id: this.jEmailId,
journal_id: String(this.journalId)
});
if (res && res.code === 0) {
this.$message.success(this.$t('mailboxSend.sendSuccess'));
this.dialogVisible = false;
this.$emit('sent');
} else {
this.$message.error((res && res.msg) || this.$t('mailboxSend.sendFail'));
}
} catch (e) {
this.$message.error(this.$t('mailboxSend.sendFail'));
} finally {
this.sendLoading = false;
}
}
}
};
</script>
<style scoped>
.re-review-mail-dialog {
min-height: 420px;
}
.mail_shuru {
display: flex;
align-items: center;
min-height: 40px;
line-height: 40px;
margin-bottom: 8px;
}
.mail_tit {
width: 72px;
flex-shrink: 0;
color: #606266;
font-size: 14px;
}
.mail_inp {
flex: 1;
}
.mail_inp ::v-deep .el-input__inner {
border: none;
border-bottom: 1px solid #ebeef5;
border-radius: 0;
}
.mail-editor-wrap {
margin-top: 12px;
}
.mail-footer-bar {
margin-top: 16px;
padding: 10px 15px;
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
}
.sender-info {
font-size: 14px;
color: #666;
}
.sender-label {
font-weight: bold;
margin-right: 10px;
}
.sender-content {
color: #006699;
}
.mail-empty-tip {
color: #909399;
}
</style>

View File

@@ -44,15 +44,25 @@
<el-option :label="$t('autoPromotionLogs.taskLogState3')" value="3"></el-option>
<el-option :label="$t('autoPromotionLogs.taskLogState4')" value="4"></el-option>
</el-select>
<el-input
v-model="logKeyword"
size="mini"
clearable
:placeholder="$t('autoPromotionLogs.searchPlaceholder')"
prefix-icon="el-icon-search"
class="log-keyword-input"
@keyup.enter.native="handleLogKeywordSearch"
@clear="handleLogKeywordSearch"
/>
<el-button
type="primary"
size="mini"
icon="el-icon-refresh"
icon="el-icon-search"
:loading="loading"
class="log-refresh-btn"
@click="handleRefreshLogs"
class="log-search-btn"
@click="handleLogKeywordSearch"
>
{{ $t('autoPromotionLogs.logRefresh') }}
{{ $t('autoPromotionLogs.searchBtn') }}
</el-button>
</div>
</header>
@@ -69,7 +79,7 @@
<div class="list-wrapper">
<div
v-for="(item, rowIndex) in fullData"
v-for="(item, rowIndex) in displayLogData"
:key="item.id"
class="log-row"
:class="{ 'row-error': item.isErrorRow }"
@@ -137,7 +147,7 @@
</div>
</div>
<div v-if="!loading && fullData.length === 0" class="empty-ui">
<div v-if="!loading && displayLogData.length === 0" class="empty-ui">
<i class="el-icon-document"></i>
<p>{{ $t('autoPromotionLogs.emptyLogs') }}</p>
</div>
@@ -147,7 +157,7 @@
<el-pagination
:current-page.sync="currentPage"
:page-size="pageSize"
:page-sizes="[20, 50, 100]"
:page-sizes="[20, 50, 100, 500]"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handlePageSizeChange"
@@ -306,10 +316,11 @@ export default {
return {
loading: false,
logState: '',
logKeyword: '',
currentPage: 1,
pageSize: 20,
totalCount: 0,
fullData: [],
displayLogData: [],
logDetailVisible: false,
logDetailLoading: false,
logDetailMode: 'preview',
@@ -376,6 +387,7 @@ export default {
if (oldVal === '' || oldVal == null) return;
this.currentPage = 1;
this.logState = '';
this.logKeyword = '';
this.loadData();
}
},
@@ -383,13 +395,15 @@ export default {
handleOpen() {
this.currentPage = 1;
this.logState = '';
this.logKeyword = '';
this.loadData();
},
handleLogStateChange() {
this.currentPage = 1;
this.loadData();
},
handleRefreshLogs() {
handleLogKeywordSearch() {
this.currentPage = 1;
this.loadData();
},
handlePageSizeChange(size) {
@@ -399,7 +413,7 @@ export default {
},
async loadData() {
if (!this.taskId) {
this.fullData = [];
this.displayLogData = [];
this.totalCount = 0;
return;
}
@@ -413,10 +427,14 @@ export default {
if (this.logState !== '' && this.logState != null) {
params.state = String(this.logState);
}
const keyword = String(this.logKeyword || '').trim();
if (keyword) {
params.keyword = keyword;
}
const res = await this.$api.post(API_GET_TASK_LOGS, params);
if (res && res.code != null && Number(res.code) !== 0) {
this.$message.error(res.msg || this.$t('autoPromotionLogs.logLoadFailed'));
this.fullData = [];
this.displayLogData = [];
this.totalCount = 0;
return;
}
@@ -431,9 +449,9 @@ export default {
this.totalCount = Number(
(payload && (payload.total != null ? payload.total : payload.count)) || rawList.length || 0
);
this.fullData = rawList.map((row, idx) => this.mapLogItem(row, idx));
this.displayLogData = rawList.map((row, idx) => this.mapLogItem(row, idx));
} catch (e) {
this.fullData = [];
this.displayLogData = [];
this.totalCount = 0;
this.$message.error((e && e.message) || this.$t('autoPromotionLogs.logLoadFailed'));
} finally {
@@ -771,7 +789,11 @@ export default {
align-items: center;
}
.log-refresh-btn {
.log-keyword-input {
width: 220px;
}
.log-search-btn {
padding: 7px 10px;
}

View File

@@ -40,6 +40,14 @@
<el-tab-pane :label="$t('mailTemplate.styleTab')" name="style">
<div class="card-grid">
<div
v-if="styleOptional"
:class="['card-item', 'card-item-none', { active: !selectedHeaderId }]"
@click="selectedHeaderId = null"
>
<p class="card-title">{{ $t('mailTemplate.noStyle') }}</p>
<p class="card-desc">{{ $t('mailTemplate.noStyleDesc') }}</p>
</div>
<div
v-for="(item, index) in headerStyles"
:key="item.id"
@@ -111,6 +119,8 @@ export default {
// 可选:打开时回显的风格/模板
initialStyleId: { type: [String, Number], default: '' },
initialTemplateId: { type: [String, Number], default: '' },
// 为 true 时仅选模板即可应用Style 可选(可不选)
styleOptional: { type: Boolean, default: false },
// 可选:来源页面标识,用于回跳时决定 mailboxMouldDetail 返回到哪里
// 例如:'autoPromotion':返回自动化推广;默认返回邮件模版列表
returnSource: { type: String, default: '' }
@@ -163,24 +173,36 @@ export default {
},
computed: {
applyDisabled() {
return this.initLoading || this.contentLoading || !this.selectedHeaderId || !this.selectedContentId;
if (this.initLoading || this.contentLoading) return true;
if (!this.selectedContentId) return true;
if (!this.styleOptional && !this.selectedHeaderId) return true;
return false;
},
hasValidPreview() {
const header = this.headerStyles.find((h) => h.id === this.selectedHeaderId);
const content = this.contentTemplates.find((c) => c.id === this.selectedContentId);
if (!content) return false;
if (this.styleOptional && !this.selectedHeaderId) return true;
const header = this.headerStyles.find((h) => h.id === this.selectedHeaderId);
return !!(header && content);
},
// 【关键】拼接 HTML
combinedHtml() {
if (this.initLoading) return '';
if (!this.hasValidPreview) return '';
const header = this.headerStyles.find((h) => h.id === this.selectedHeaderId);
const content = this.contentTemplates.find((c) => c.id === this.selectedContentId);
if (!content) return '';
if (this.styleOptional && !this.selectedHeaderId) {
return content.bodyHtml || '';
}
const header = this.headerStyles.find((h) => h.id === this.selectedHeaderId);
return `${header.htmlHeader}${content.bodyHtml}${header.htmlFooter}`;
},
currentSelectionText() {
const h = this.headerStyles.find((h) => h.id === this.selectedHeaderId);
const c = this.contentTemplates.find((c) => c.id === this.selectedContentId);
if (this.styleOptional && !this.selectedHeaderId) {
return c ? c.name : '';
}
return `${c ? c.name : ''} + ${h ? h.name : ''}`;
},
currentJournalLabel() {
@@ -306,8 +328,8 @@ export default {
htmlFooter: item.footer_html || ''
}));
// 默认选中第一条
if (this.headerStyles.length && !this.selectedHeaderId) {
// 默认选中第一条Style 可选时不自动选中)
if (this.headerStyles.length && !this.selectedHeaderId && !this.styleOptional) {
this.selectedHeaderId = this.headerStyles[0].id;
}
} catch (e) {
@@ -382,13 +404,14 @@ export default {
},
submit() {
if (this.applyDisabled) return;
// 将拼接好的 HTML 及选择信息抛给父组件(兼容:父组件也可只用 html
const header = this.headerStyles.find((h) => h.id === this.selectedHeaderId);
const header = this.selectedHeaderId
? this.headerStyles.find((h) => h.id === this.selectedHeaderId)
: null;
const content = this.contentTemplates.find((c) => c.id === this.selectedContentId);
this.$emit('confirm', {
html: this.combinedHtml,
journal_id: this.selectedJournalId,
style_id: this.selectedHeaderId,
style_id: this.selectedHeaderId || '',
template_id: this.selectedContentId,
style: header || null,
template: content || null
@@ -494,6 +517,12 @@ export default {
background: #f5f7ff;
outline: 1px solid #6366f1;
}
.card-item-none {
min-height: 56px;
display: flex;
flex-direction: column;
justify-content: center;
}
.card-img {
width: 100%;
height: auto;

View File

@@ -229,6 +229,16 @@ import {
created() {
this.loadJournals();
this.initAccountSelection();
this._skipNextActivatedSync = true;
},
activated() {
if (this._skipNextActivatedSync) {
this._skipNextActivatedSync = false;
return;
}
if (this.selectedAccount) {
this.autoSyncInbox();
}
},
beforeDestroy() {
this.stopInboxSse(true);
@@ -311,7 +321,7 @@ import {
: null;
if (res && Number(res.code) === 0 && email) {
this.selectedAccount = email;
this.fetchData();
this.autoSyncInbox();
this.startInboxSse();
return;
}
@@ -381,8 +391,8 @@ import {
localStorage.setItem('mailboxCollect_journal_id', String(row.journal_id));
}
// 不再写入路由 query直接拉取数据
this.fetchData();
// 切换账号后自动收信并刷新列表
this.autoSyncInbox();
this.startInboxSse();
},
handleAccountDialogBeforeClose(done) {
@@ -651,29 +661,52 @@ fetchLatestSingleMail(jEmailId, journalId) {
.catch(() => {});
});
},
// 同步收件箱api/email_client/syncInbox 参数 j_email_id、journal_id均为 String完成后重新拉取 displayList
autoSyncInbox() {
return this.syncInboxAndRefresh({ showMessage: false, fetchOnFail: true });
},
handleSyncInbox() {
if (!this.selectedAccount) return;
this.syncInboxAndRefresh({ showMessage: true, fetchOnFail: false });
},
syncInboxAndRefresh(options = {}) {
const showMessage = options.showMessage !== false;
const fetchOnFail = options.fetchOnFail === true;
if (!this.selectedAccount) return Promise.resolve(false);
if (this.syncLoading) return Promise.resolve(false);
this.syncLoading = true;
const params = {
j_email_id: String(this.selectedAccount.j_email_id),
journal_id: String(this.selectedAccount.journal_id)
};
this.$api
return this.$api
.post(API.syncInbox, params)
.then((res) => {
this.syncLoading = false;
if (res && res.code === 0) {
this.newMailsBuffer=[]
this.$message.success(this.$t('mailboxCollect.syncSuccess'));
this.newMailsBuffer = [];
if (showMessage) {
this.$message.success(this.$t('mailboxCollect.syncSuccess'));
}
this.fetchData();
} else {
return true;
}
if (showMessage) {
this.$message.error((res && res.msg) || this.$t('mailboxCollect.syncFail'));
}
if (fetchOnFail) {
this.fetchData();
}
return false;
})
.catch(() => {
if (showMessage) {
this.$message.error(this.$t('mailboxCollect.syncFail'));
}
if (fetchOnFail) {
this.fetchData();
}
return false;
})
.finally(() => {
this.syncLoading = false;
this.$message.error(this.$t('mailboxCollect.syncFail'));
});
},
/**

View File

@@ -109,7 +109,7 @@
:source-content.sync="sourceContent"
:source-rows="16"
:source-placeholder="$t('mailboxSend.sourcePlaceholder')"
:show-select-template-button="false"
:show-select-template-button="true"
@onSelectTemplate="showTemplateDialog = true"
/>
</div>
@@ -139,6 +139,41 @@
</form>
</div>
<!-- 批量发送进度 -->
<el-dialog
:title="$t('mailboxSend.sendProgressTitle')"
:visible.sync="sendProgressVisible"
width="520px"
append-to-body
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="sendProgressDone"
>
<div class="send-progress-summary">{{ sendProgressSummaryText }}</div>
<ul class="send-progress-list">
<li
v-for="(item, index) in sendProgressList"
:key="item.email + '-' + index"
class="send-progress-item"
:class="'send-progress-item--' + item.status"
>
<span class="send-progress-icon">
<i v-if="item.status === 'pending'" class="el-icon-more-outline"></i>
<i v-else-if="item.status === 'sending'" class="el-icon-loading"></i>
<i v-else-if="item.status === 'success'" class="el-icon-circle-check"></i>
<i v-else-if="item.status === 'fail'" class="el-icon-circle-close"></i>
</span>
<div class="send-progress-body">
<span class="send-progress-email">{{ item.email }}</span>
<span v-if="item.msg" class="send-progress-msg">{{ item.msg }}</span>
</div>
</li>
</ul>
<span slot="footer" v-if="sendProgressDone">
<el-button type="primary" @click="handleSendProgressClose">{{ $t('mailboxSend.sendBatchConfirm') }}</el-button>
</span>
</el-dialog>
<!-- 选择通讯录 -->
<el-dialog :title="$t('mailboxSend.selectUser')" :visible.sync="Librarybox" width="900px" :close-on-click-modal="false">
<el-button type="primary" style="margin: 0 0 15px 0;" icon="el-icon-plus" @click="mailLiyAll()" v-if="LibrarySelection!=''">
@@ -168,11 +203,15 @@
</el-dialog>
<template-selector-dialog
v-if="showTemplateDialog"
:visible.sync="showTemplateDialog"
@confirm="handleTemplateApply"
@close-all-dialogs="closeAllDialogs"
/>
v-if="showTemplateDialog"
:visible.sync="showTemplateDialog"
:journal-id="mailJournalId"
:initial-style-id="selectedStyleId"
:initial-template-id="selectedTemplateId"
:style-optional="true"
@confirm="handleTemplateApply"
@close-all-dialogs="closeAllDialogs"
/>
</div>
</template>
@@ -221,8 +260,7 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
sourceContent: '',
toInput: '',
// 默认只允许 1 个收件人,后续可按需动态修改
maxToRecipients: 1,
maxToRecipients: 0,
toSelecting: false,
nextToUid: 1,
ccList: [],
@@ -245,7 +283,11 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
cb: null
},
recipientSuggestScrollHandler: null,
showSendEmail:true
showSendEmail:true,
sendProgressVisible: false,
sendProgressDone: false,
sendProgressList: [],
sendProgressSuccessList: []
};
},
components: {
@@ -256,6 +298,26 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
footerBarLeft() {
const collapsed = this.collapseValue === true || this.collapseValue === 'true';
return (collapsed ? 64: 260) + 'px';
},
mailJournalId() {
const stored = localStorage.getItem('mailboxCollect_journal_id');
const fromRoute = this.$route.query.journal_id;
return stored || fromRoute || '';
},
sendProgressSummaryText() {
const list = this.sendProgressList || [];
const total = list.length;
if (!total) return '';
if (!this.sendProgressDone) {
const done = list.filter((item) => item.status === 'success' || item.status === 'fail').length;
return this.$t('mailboxSend.sendProgressRunning', { done, total });
}
const ok = list.filter((item) => item.status === 'success').length;
const fail = list.filter((item) => item.status === 'fail').length;
if (!fail) {
return this.$t('mailboxSend.sendProgressAllSuccess', { count: ok });
}
return this.$t('mailboxSend.sendProgressDone', { ok, fail });
}
},
watch: {
@@ -424,8 +486,11 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
this.queryMail.content = html;
this.sourceContent = html;
// 优先回填 TinyMCE 实例
if (window.tinymce && window.tinymce.activeEditor && window.tinymce.activeEditor.setContent) {
// 优先回填编辑器实例
const editorRef = this.$refs.myTextEditor;
if (editorRef && editorRef.editor && typeof editorRef.editor.setContent === 'function') {
editorRef.editor.setContent(html);
} else if (window.tinymce && window.tinymce.activeEditor && window.tinymce.activeEditor.setContent) {
window.tinymce.activeEditor.setContent(html);
}
this.selectedTemplateId = templateId ? String(templateId) : '';
@@ -654,7 +719,97 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
});
},
// 发送邮件api/email_client/sendTemplateStyleText
getSendContent() {
if (this.isSourceMode) {
return this.sourceContent || this.queryMail.content || '';
}
const editorRef = this.$refs.myTextEditor;
if (editorRef && typeof editorRef.getInlinedContent === 'function') {
return editorRef.getInlinedContent() || this.queryMail.content || '';
}
return this.queryMail.content || '';
},
initSendProgress(toList) {
this.sendProgressList = (toList || []).map((email) => ({
email,
status: 'pending',
msg: ''
}));
this.sendProgressDone = false;
this.sendProgressSuccessList = [];
this.sendProgressVisible = true;
},
setSendProgressItemStatus(index, status, msg) {
const item = this.sendProgressList[index];
if (!item) return;
this.$set(this.sendProgressList, index, {
...item,
status,
msg: msg || ''
});
},
closeSendProgressDialog() {
this.sendProgressVisible = false;
this.sendProgressDone = false;
this.sendProgressList = [];
this.sendProgressSuccessList = [];
},
handleSendProgressClose() {
if (this.sendProgressSuccessList.length) {
const successSet = new Set(this.sendProgressSuccessList.map((item) => item.email));
this.queryMail.sendnamelist = (this.queryMail.sendnamelist || []).filter(
(item) => !successSet.has(item.name)
);
this.queryMail.sendname = (this.queryMail.sendnamelist || []).map((i) => i.name);
}
this.closeSendProgressDialog();
},
resetMailFormAfterSend() {
this.queryMail.sendnamelist = [];
this.queryMail.sendtitle = '';
this.queryMail.sendcc = '';
this.ccList = [];
this.queryMail.content = '';
this.sourceContent = '';
this.selectedTemplateId = '';
this.selectedStyleId = '';
this.fileL_atta = [];
},
async sendMailToRecipients(toList, journalId, jEmailId, content) {
this.initSendProgress(toList);
const successList = [];
const failList = [];
for (let i = 0; i < toList.length; i++) {
const email = toList[i];
this.setSendProgressItemStatus(i, 'sending');
await this.$nextTick();
try {
const res = await this.$api.post('api/email_client/sendOne', {
to_email: email,
subject: this.queryMail.sendtitle || '',
content,
j_email_id: String(jEmailId),
journal_id: String(journalId)
});
if (res && res.code === 0) {
successList.push({ email });
this.setSendProgressItemStatus(i, 'success');
} else {
const msg = (res && res.msg) || this.$t('mailboxSend.sendFail');
failList.push({ email, msg });
this.setSendProgressItemStatus(i, 'fail', msg);
}
} catch (e) {
const msg = this.$t('mailboxSend.sendFail');
failList.push({ email, msg });
this.setSendProgressItemStatus(i, 'fail', msg);
}
}
this.sendProgressDone = true;
this.sendProgressSuccessList = successList;
return { successList, failList };
},
// 发送邮件:多个收件人时逐封调用 sendOne
handleSend() {
// 防抖:快速连点只触发最后一次
if (this.sendLoading) return;
@@ -664,7 +819,6 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
if (this.sendLoading) return;
const toList = (this.queryMail.sendnamelist || []).map((item) => item.name).filter(Boolean);
const expert_id_list = (this.queryMail.sendnamelist || []).map((item) => item.expert_id).filter(Boolean);
if (!toList.length) {
this.$message.warning(this.$t('mailboxSend.validateTo'));
return;
@@ -674,7 +828,6 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
return;
}
// journal_id 优先从本地缓存获取,兼容旧的路由参数
const storedJournalId = localStorage.getItem('mailboxCollect_journal_id');
const journalId = storedJournalId || this.$route.query.journal_id;
const storedEmailId = localStorage.getItem('mailboxCollect_j_email_id');
@@ -688,42 +841,22 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
this.$message.warning(this.$t('mailboxSend.needAccount'));
return;
}
// if (!this.selectedTemplateId || !this.selectedStyleId) {
// this.$message.warning(this.$t('mailboxSend.selectTemplateStyleFirst'));
// return;
// }
this.sendLoading = true;
const params = {
to_email: toList.join(','),
subject: this.queryMail.sendtitle || '',
content: this.queryMail.content || '',
j_email_id: String(jEmailId),
journal_id: String(journalId),
};
const self = this;
this.$api
.post('api/email_client/sendOne', params)
.then((res) => {
if (res && res.code === 0) {
const content = this.getSendContent();
this.sendMailToRecipients(toList, journalId, jEmailId, content)
.then(({ successList, failList }) => {
if (!failList.length) {
self.$message.success(self.$t('mailboxSend.sendSuccess'));
self.queryMail.sendnamelist = [];
self.queryMail.sendtitle = '';
self.queryMail.sendcc = '';
self.ccList = [];
self.queryMail.content = '';
self.sourceContent = '';
self.selectedTemplateId = '';
self.selectedStyleId = '';
self.fileL_atta = [];
self.goBackInbox();
} else {
self.$message.error((res && res.msg) || self.$t('mailboxSend.sendFail'));
setTimeout(() => {
self.closeSendProgressDialog();
self.resetMailFormAfterSend();
self.goBackInbox();
}, 900);
return;
}
})
.catch(() => {
self.$message.error(self.$t('mailboxSend.sendFail'));
self.sendProgressSuccessList = successList;
})
.finally(() => {
self.sendLoading = false;
@@ -1105,4 +1238,65 @@ import TemplateSelectorDialog from '@/components/page/components/email/TemplateS
font-size: 13px;
}
.send-progress-summary {
margin-bottom: 14px;
padding: 10px 12px;
background: #f4f8ff;
border-radius: 4px;
color: #606266;
font-size: 14px;
line-height: 1.5;
}
.send-progress-list {
list-style: none;
margin: 0;
padding: 0;
max-height: 320px;
overflow-y: auto;
}
.send-progress-item {
display: flex;
align-items: flex-start;
padding: 10px 0;
border-bottom: 1px solid #ebeef5;
}
.send-progress-item:last-child {
border-bottom: none;
}
.send-progress-icon {
width: 22px;
flex-shrink: 0;
margin-right: 10px;
line-height: 22px;
text-align: center;
font-size: 18px;
color: #c0c4cc;
}
.send-progress-item--sending .send-progress-icon {
color: #409eff;
}
.send-progress-item--success .send-progress-icon {
color: #67c23a;
}
.send-progress-item--fail .send-progress-icon {
color: #f56c6c;
}
.send-progress-body {
flex: 1;
min-width: 0;
}
.send-progress-email {
display: block;
color: #303133;
word-break: break-all;
line-height: 22px;
}
.send-progress-msg {
display: block;
margin-top: 4px;
color: #f56c6c;
font-size: 12px;
line-height: 1.4;
}
</style>

View File

@@ -74,3 +74,36 @@ export function applyMailTemplateVariableMocks(html, options) {
return full;
});
}
/** 复审通知邮件模板变量标题、SN、期刊名称、期刊官网 */
export function applyReviewNotifyTemplateVariables(html, options) {
const opts = options || {};
if (!html) return '';
const title = opts.title != null ? String(opts.title) : '';
const acceptSn =
opts.accept_sn != null && opts.accept_sn !== ''
? String(opts.accept_sn)
: opts.sn != null
? String(opts.sn)
: '';
const journalName = opts.journal_name != null ? String(opts.journal_name) : '';
const journalUrl = opts.journal_url != null ? String(opts.journal_url) : '';
const map = {
title,
article_title: title,
manuscript_title: title,
accept_sn: acceptSn,
article_sn: acceptSn,
sn: acceptSn,
journal_name: journalName,
journal_url: journalUrl
};
return String(html).replace(/\{\{\s*([^}]+?)\s*\}\}/g, (full, rawKey) => {
const key = String(rawKey).trim();
if (!key) return full;
if (Object.prototype.hasOwnProperty.call(map, key)) {
return map[key];
}
return full;
});
}