Merge branch 'master' of https://git.nuttyreading.com/wangjinlei/tougao_web into Editorial-Board
This commit is contained in:
@@ -1159,6 +1159,10 @@ colTitle: 'Template title',
|
|||||||
},
|
},
|
||||||
tmrEmailEditor: {
|
tmrEmailEditor: {
|
||||||
preview: 'Preview',
|
preview: 'Preview',
|
||||||
|
previewWithVariables: 'Preview (showing variables)',
|
||||||
|
previewWithVariablesTitle: 'Preview (showing variables)',
|
||||||
|
previewWithVariablesHint: 'The expert data is an example, used for variablespelling check only.',
|
||||||
|
close: 'Close',
|
||||||
placeholder: 'Please enter email content'
|
placeholder: 'Please enter email content'
|
||||||
},
|
},
|
||||||
wordCite: {
|
wordCite: {
|
||||||
|
|||||||
@@ -1144,6 +1144,10 @@ const zh = {
|
|||||||
},
|
},
|
||||||
tmrEmailEditor: {
|
tmrEmailEditor: {
|
||||||
preview: '预览效果',
|
preview: '预览效果',
|
||||||
|
previewWithVariables: '预览(示例变量)',
|
||||||
|
previewWithVariablesTitle: '预览效果(已替换示例变量)',
|
||||||
|
previewWithVariablesHint: '专家数据仅为示例,仅用于变量拼写检查。',
|
||||||
|
close: '关闭',
|
||||||
placeholder: '请输入邮件内容'
|
placeholder: '请输入邮件内容'
|
||||||
},
|
},
|
||||||
wordCite: {
|
wordCite: {
|
||||||
|
|||||||
@@ -4,9 +4,14 @@
|
|||||||
<div class="title-slot">
|
<div class="title-slot">
|
||||||
<slot name="title"></slot>
|
<slot name="title"></slot>
|
||||||
</div>
|
</div>
|
||||||
<button @click="showModal = true" class="preview-trigger-btn">
|
<div class="header-actions">
|
||||||
<i class="icon-eye"></i> {{ $t('tmrEmailEditor.preview') || '预览' }}
|
<button type="button" @click="openPreview(false)" class="preview-trigger-btn">
|
||||||
</button>
|
<i class="icon-eye"></i> {{ $t('tmrEmailEditor.preview') }}
|
||||||
|
</button>
|
||||||
|
<button type="button" @click="openPreview(true)" class="preview-trigger-btn preview-with-vars-btn">
|
||||||
|
<i class="icon-eye"></i> {{ $t('tmrEmailEditor.previewWithVariables') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<textarea
|
<textarea
|
||||||
@@ -18,19 +23,21 @@
|
|||||||
></textarea>
|
></textarea>
|
||||||
|
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div v-if="showModal" class="tmr-modal-mask" @click.self="showModal = false">
|
<div v-if="showModal" class="tmr-modal-mask" @click.self="closePreviewModal">
|
||||||
<div class="tmr-modal-container">
|
<div class="tmr-modal-container">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<span>{{ $t('tmrEmailEditor.preview') || '邮件预览' }}</span>
|
<span>{{ modalPreviewTitle }}</span>
|
||||||
<button class="close-btn" @click="showModal = false">×</button>
|
<button type="button" class="close-btn" @click="closePreviewModal">×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<p v-if="previewWithMockVariables" class="mock-preview-hint">{{ $t('tmrEmailEditor.previewWithVariablesHint') }}</p>
|
||||||
|
|
||||||
<div class="common_tmr_email_box" v-html="htmlContentForPreview"></div>
|
<div class="common_tmr_email_box" v-html="htmlContentForPreview"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="confirm-btn" @click="showModal = false">关闭</button>
|
<button type="button" class="confirm-btn" @click="closePreviewModal">{{ $t('tmrEmailEditor.close') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -47,6 +54,14 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
journalList: {
|
||||||
|
type: Array,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
journalId: {
|
||||||
|
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
@@ -54,7 +69,28 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showModal: false
|
showModal: false,
|
||||||
|
/** 为 true 时预览会用 variableMockData 替换正文中的 {{变量名}} */
|
||||||
|
previewWithMockVariables: false,
|
||||||
|
/**
|
||||||
|
* 预览「带变量」时的假数据。键名须与模板里 {{键名}} 完全一致(含中文键)。
|
||||||
|
* 可对照本地变量清单维护,例如:约稿变量清单表格.docx
|
||||||
|
*/
|
||||||
|
variableMockData: {
|
||||||
|
// 以下为示例占位,请按实际模板增删改:
|
||||||
|
|
||||||
|
|
||||||
|
submission_url: "https://submission.tmrjournals.com/", // 投稿系统链接
|
||||||
|
eic_name: "Zhang San", // 主编姓名
|
||||||
|
editor_name: "Alice Wong" ,// 责任编辑姓名
|
||||||
|
expert_title: "Prof", // 专家职称 (如 Prof./Dr.)
|
||||||
|
expert_name: "John Doe", // 专家姓名
|
||||||
|
expert_field: "Biomedical Engineering", // 专家研究领域
|
||||||
|
// representative_work_title: "Advanced Applications of AI in Medical Imaging", // 专家代表作标题
|
||||||
|
// ai_content_analysis: "", // AI 约稿理由分析
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -66,12 +102,65 @@ export default {
|
|||||||
if (!this.value) return '';
|
if (!this.value) return '';
|
||||||
return this.extractContent(this.value);
|
return this.extractContent(this.value);
|
||||||
},
|
},
|
||||||
// 用于预览的 HTML,保持与输出一致
|
modalPreviewTitle() {
|
||||||
|
if (this.previewWithMockVariables) {
|
||||||
|
return this.$t('tmrEmailEditor.previewWithVariablesTitle');
|
||||||
|
}
|
||||||
|
return this.$t('tmrEmailEditor.preview');
|
||||||
|
},
|
||||||
|
// 用于预览的 HTML;带变量模式会先替换 {{...}}
|
||||||
htmlContentForPreview() {
|
htmlContentForPreview() {
|
||||||
return this.value || '';
|
let html = this.value || '';
|
||||||
|
if (this.previewWithMockVariables) {
|
||||||
|
html = this.applyVariableMocks(html);
|
||||||
|
}
|
||||||
|
return html;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openPreview(withMockVariables) {
|
||||||
|
this.previewWithMockVariables = !!withMockVariables;
|
||||||
|
this.showModal = true;
|
||||||
|
},
|
||||||
|
closePreviewModal() {
|
||||||
|
this.showModal = false;
|
||||||
|
this.previewWithMockVariables = false;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 将 HTML 中的 {{ key }} 替换为 variableMockData[key];未配置的键保持原样。
|
||||||
|
*/
|
||||||
|
applyVariableMocks(html) {
|
||||||
|
if (!html) return '';
|
||||||
|
const oneMonthLater = new Date();
|
||||||
|
oneMonthLater.setMonth(oneMonthLater.getMonth() + 1);
|
||||||
|
// 格式化为 YYYY-MM-DD
|
||||||
|
const deadlineStr = oneMonthLater.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
|
||||||
|
const journal_info=this.journalList.find(e=>e.journal_id==this.journalId)
|
||||||
|
|
||||||
|
const map = {
|
||||||
|
...this.variableMockData,
|
||||||
|
journal_abbr: journal_info.jabbr, // 期刊缩写
|
||||||
|
journal_name: journal_info.title,// 期刊全称
|
||||||
|
journal_url: journal_info.website, // 期刊官网链接
|
||||||
|
journal_email: journal_info.email, // 期刊官方邮箱
|
||||||
|
indexing_databases: "ESCI, Scopus, ROAD", // 收录数据库
|
||||||
|
special_support_deadline:deadlineStr
|
||||||
|
} || {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return html.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (full, rawKey) => {
|
||||||
|
const key = String(rawKey).trim();
|
||||||
|
if (!key) return full;
|
||||||
|
if (Object.prototype.hasOwnProperty.call(map, key) && map[key] != null && map[key] !== '') {
|
||||||
|
const val = map[key];
|
||||||
|
return typeof val === 'string' ? val : String(val);
|
||||||
|
}
|
||||||
|
return full;
|
||||||
|
});
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* 核心:剥离外层容器和换行符
|
* 核心:剥离外层容器和换行符
|
||||||
* 将 <div class="...">line1<br>line2</div> 还原为 line1\nline2
|
* 将 <div class="...">line1<br>line2</div> 还原为 line1\nline2
|
||||||
@@ -125,6 +214,14 @@ export default {
|
|||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.preview-trigger-btn {
|
.preview-trigger-btn {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #dcdfe6;
|
||||||
@@ -246,4 +343,15 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.confirm-btn:hover { background: #66b1ff; }
|
.confirm-btn:hover { background: #66b1ff; }
|
||||||
|
|
||||||
|
.preview-with-vars-btn {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mock-preview-hint {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: red;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -98,6 +98,8 @@
|
|||||||
<div class="subject-label" style="margin-bottom: 10px;">{{ $t('mailboxMouldDetail.emailBody') }}:</div>
|
<div class="subject-label" style="margin-bottom: 10px;">{{ $t('mailboxMouldDetail.emailBody') }}:</div>
|
||||||
<!-- <TmrEmailEditor
|
<!-- <TmrEmailEditor
|
||||||
v-model="form.body"
|
v-model="form.body"
|
||||||
|
:journalList="journalList"
|
||||||
|
:journalId="form.journalId"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
/> -->
|
/> -->
|
||||||
<CkeditorMail v-model="form.body" />
|
<CkeditorMail v-model="form.body" />
|
||||||
@@ -172,8 +174,9 @@ export default {
|
|||||||
.then(res => {
|
.then(res => {
|
||||||
const list = res || [];
|
const list = res || [];
|
||||||
const mapped = (Array.isArray(list) ? list : []).map(j => ({
|
const mapped = (Array.isArray(list) ? list : []).map(j => ({
|
||||||
|
...j,
|
||||||
journal_id: j.journal_id || j.id,
|
journal_id: j.journal_id || j.id,
|
||||||
title: j.title || j.name || ''
|
title: j.title || j.name || '',
|
||||||
}));
|
}));
|
||||||
this.journalList = mapped;
|
this.journalList = mapped;
|
||||||
if (fromRouteJournalId) {
|
if (fromRouteJournalId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user