From e57e06be951c086f617a1d1f9fc66634b4ef935a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com> Date: Fri, 5 Jun 2026 14:33:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=9C=E8=80=85=E8=83=8C=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/langs/en.js | 14 + src/components/common/langs/zh.js | 14 + src/components/page/articleDetailEditor.vue | 48 ++- src/components/page/articleListEditor_A.vue | 125 +++++-- .../ReReviewNotifyMailDialog.vue | 307 ++++++++++++++++++ .../autoPromotion/PromotionDetailDrawer.vue | 50 ++- .../email/TemplateSelectorDialog.vue | 45 ++- src/components/page/mailboxCollect.vue | 55 +++- src/components/page/mailboxSend.vue | 284 +++++++++++++--- src/utils/mailTemplatePreview.js | 33 ++ 10 files changed, 861 insertions(+), 114 deletions(-) create mode 100644 src/components/page/components/articleDetail/ReReviewNotifyMailDialog.vue diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js index 409b57f..49e2bb6 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -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: diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index 5a41c4a..0e56f2e 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -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 一致性核查', diff --git a/src/components/page/articleDetailEditor.vue b/src/components/page/articleDetailEditor.vue index 37e5ae9..38c5450 100644 --- a/src/components/page/articleDetailEditor.vue +++ b/src/components/page/articleDetailEditor.vue @@ -812,9 +812,14 @@ > - - - + {{ $t('articleDetailEditor.reReviewNotifyBtn') }} @@ -1126,6 +1131,16 @@ +
{{ HIxForm.realname }}
@@ -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; diff --git a/src/components/page/articleListEditor_A.vue b/src/components/page/articleListEditor_A.vue index 0e46db7..4f7e4d7 100644 --- a/src/components/page/articleListEditor_A.vue +++ b/src/components/page/articleListEditor_A.vue @@ -275,41 +275,40 @@ >
- - {{ v.realname || v.email || '-' }} + {{ v.realname || v.email || '-' }} + + + + + + + - - -
@@ -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; diff --git a/src/components/page/components/articleDetail/ReReviewNotifyMailDialog.vue b/src/components/page/components/articleDetail/ReReviewNotifyMailDialog.vue new file mode 100644 index 0000000..322f432 --- /dev/null +++ b/src/components/page/components/articleDetail/ReReviewNotifyMailDialog.vue @@ -0,0 +1,307 @@ + + + + + diff --git a/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue b/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue index 0a15e8f..aa51bd4 100644 --- a/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue +++ b/src/components/page/components/autoPromotion/PromotionDetailDrawer.vue @@ -44,15 +44,25 @@ + - {{ $t('autoPromotionLogs.logRefresh') }} + {{ $t('autoPromotionLogs.searchBtn') }}
@@ -69,7 +79,7 @@
-
+

{{ $t('autoPromotionLogs.emptyLogs') }}

@@ -147,7 +157,7 @@ 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; } diff --git a/src/components/page/components/email/TemplateSelectorDialog.vue b/src/components/page/components/email/TemplateSelectorDialog.vue index ce65894..3e58b9d 100644 --- a/src/components/page/components/email/TemplateSelectorDialog.vue +++ b/src/components/page/components/email/TemplateSelectorDialog.vue @@ -40,6 +40,14 @@
+
+

{{ $t('mailTemplate.noStyle') }}

+

{{ $t('mailTemplate.noStyleDesc') }}

+
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; diff --git a/src/components/page/mailboxCollect.vue b/src/components/page/mailboxCollect.vue index 46ff825..467ee7c 100644 --- a/src/components/page/mailboxCollect.vue +++ b/src/components/page/mailboxCollect.vue @@ -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')); }); }, /** diff --git a/src/components/page/mailboxSend.vue b/src/components/page/mailboxSend.vue index eeab9ca..ee4865b 100644 --- a/src/components/page/mailboxSend.vue +++ b/src/components/page/mailboxSend.vue @@ -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" />
@@ -139,6 +139,41 @@
+ + +
{{ sendProgressSummaryText }}
+
    +
  • + + + + + + +
    + {{ item.email }} + {{ item.msg }} +
    +
  • +
+ + {{ $t('mailboxSend.sendBatchConfirm') }} + +
+ @@ -168,11 +203,15 @@ + 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" + />
@@ -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; +} + diff --git a/src/utils/mailTemplatePreview.js b/src/utils/mailTemplatePreview.js index 149f272..d61ce90 100644 --- a/src/utils/mailTemplatePreview.js +++ b/src/utils/mailTemplatePreview.js @@ -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; + }); +}