加了变量预览

This commit is contained in:
2026-04-13 14:18:51 +08:00
parent 075fae74ab
commit 0086899ec4
4 changed files with 131 additions and 12 deletions

View File

@@ -1156,6 +1156,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'
} }

View File

@@ -1141,6 +1141,10 @@ const zh = {
}, },
tmrEmailEditor: { tmrEmailEditor: {
preview: '预览效果', preview: '预览效果',
previewWithVariables: '预览(示例变量)',
previewWithVariablesTitle: '预览效果(已替换示例变量)',
previewWithVariablesHint: '专家数据仅为示例,仅用于变量拼写检查。',
close: '关闭',
placeholder: '请输入邮件内容' placeholder: '请输入邮件内容'
} }

View File

@@ -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">&times;</button> <button type="button" class="close-btn" @click="closePreviewModal">&times;</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>

View File

@@ -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) {