417 lines
15 KiB
Vue
417 lines
15 KiB
Vue
<template>
|
|
<div class="detail-container">
|
|
<header class="action-bar">
|
|
<div class="left">
|
|
<el-button type="text" icon="el-icon-back" @click="goBack" class="back-btn">{{ $t('mailboxMouldDetail.back') }}</el-button>
|
|
<el-divider direction="vertical"></el-divider>
|
|
<span class="page-title">{{ isEditMode ? $t('mailboxMouldDetail.editTemplate') : $t('mailboxMouldDetail.createTemplate') }}</span>
|
|
|
|
</div>
|
|
<div class="right">
|
|
<el-button size="mini" @click="goBack">{{ $t('mailboxMouldDetail.cancel') }}</el-button>
|
|
<el-button
|
|
type="primary"
|
|
size="mini"
|
|
icon="el-icon-document-checked"
|
|
@click="handleSave"
|
|
:loading="saveLoading"
|
|
:disabled="saveLoading || journalLoading"
|
|
:loading-text="$t('mailboxMouldDetail.loading')"
|
|
>
|
|
{{ $t('mailboxMouldDetail.save') }}
|
|
</el-button>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="editor-layout" v-loading="journalLoading" :element-loading-text="$t('mailboxMouldDetail.loading')">
|
|
<aside class="config-aside scroll-panel" v-show="!journalLoading">
|
|
<el-form ref="detailForm" label-position="top" :model="form" :rules="rules" size="mini" class="compact-form">
|
|
<div class="section-title"><i class="el-icon-setting"></i> {{ $t('mailboxMouldDetail.basicInfo') }}</div>
|
|
|
|
<el-form-item prop="title" :label="$t('mailboxMouldDetail.templateTitle')">
|
|
<el-input v-model="form.title" :placeholder="$t('mailboxMouldDetail.templateTitlePlaceholder')"></el-input>
|
|
</el-form-item>
|
|
|
|
<el-form-item prop="journalId" :label="$t('mailboxMouldDetail.journal')">
|
|
<el-select v-model="form.journalId" filterable style="width: 100%">
|
|
<el-option v-for="j in journalList" :key="j.journal_id" :label="j.title" :value="String(j.journal_id)" />
|
|
</el-select>
|
|
</el-form-item>
|
|
|
|
<el-form-item prop="scene" :label="$t('mailboxMouldDetail.templateType')">
|
|
<el-select v-model="form.scene" style="width: 100%">
|
|
<el-option :label="$t('mailboxMouldDetail.sceneInviteSubmission')" value="invite_submission"></el-option>
|
|
<el-option :label="$t('mailboxMouldDetail.scenePromoteCitation')" value="promote_citation"></el-option>
|
|
<el-option :label="$t('mailboxMouldDetail.sceneGeneralThanks')" value="general_thanks"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
|
|
<el-row :gutter="8">
|
|
<el-col :span="14">
|
|
<el-form-item prop="lang" :label="$t('mailboxMouldDetail.languageConfig')">
|
|
<el-radio-group v-model="form.lang" class="full-width-radio">
|
|
<el-radio-button label="en">EN</el-radio-button>
|
|
<el-radio-button label="zh">ZH</el-radio-button>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="10">
|
|
<el-form-item prop="version" :label="$t('mailboxMouldDetail.version')">
|
|
<el-input v-model="form.version" :placeholder="$t('mailboxMouldDetail.versionPlaceholder')"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-divider></el-divider>
|
|
|
|
<div class="section-title"><i class="el-icon-cpu"></i> {{ $t('mailboxMouldDetail.variablesJson') }}</div>
|
|
<el-form-item class="no-m-b">
|
|
<el-input
|
|
type="textarea"
|
|
:rows="6"
|
|
v-model="form.variables"
|
|
class="dark-json-input"
|
|
:placeholder="$t('mailboxMouldDetail.variablesPlaceholder')"
|
|
></el-input>
|
|
</el-form-item>
|
|
|
|
<div class="status-box">
|
|
<span>{{ $t('mailboxMouldDetail.activeStatus') }}</span>
|
|
<el-switch v-model="form.is_active" :active-value="1" :inactive-value="0"></el-switch>
|
|
</div>
|
|
</el-form>
|
|
</aside>
|
|
|
|
<section class="main-editor" v-show="!journalLoading">
|
|
<el-card shadow="never" class="editor-card">
|
|
<div class="subject-input-wrapper">
|
|
<div class="subject-label">{{ $t('mailboxMouldDetail.emailSubject') }}:</div>
|
|
<el-input
|
|
v-model="form.subject"
|
|
size="small"
|
|
:placeholder="$t('mailboxMouldDetail.emailSubjectPlaceholder')"
|
|
class="subject-inner-input"
|
|
></el-input>
|
|
</div>
|
|
|
|
<div class="body-editor-container">
|
|
<div class="subject-label" style="margin-bottom: 10px;">{{ $t('mailboxMouldDetail.emailBody') }}:</div>
|
|
<TmrEmailEditor
|
|
v-model="form.body"
|
|
placeholder=""
|
|
/>
|
|
<!-- <CkeditorMail v-model="form.body" /> -->
|
|
</div>
|
|
</el-card>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import CkeditorMail from '@/components/page/components/email/CkeditorMail.vue';
|
|
import TmrEmailEditor from '@/components/page/components/email/TmrEmailEditor.vue';
|
|
const API = {
|
|
getAllJournal: 'api/Article/getJournal',
|
|
getTemplate: 'api/mail_template/getTemplate',
|
|
saveTemplate: 'api/mail_template/saveTemplate'
|
|
};
|
|
|
|
export default {
|
|
name: 'mailboxMouldDetail',
|
|
components: { CkeditorMail,TmrEmailEditor },
|
|
data() {
|
|
return {
|
|
journalLoading: true,
|
|
journalList: [],
|
|
saveLoading: false,
|
|
// 防抖:避免重复点击“保存”导致多次请求
|
|
saveDebounceTimer: null,
|
|
rules: {
|
|
journalId: [{ required: true, message: this.$t('mailboxMouldDetail.rulesJournal'), trigger: 'change' }],
|
|
scene: [{ required: true, message: this.$t('mailboxMouldDetail.rulesScene'), trigger: 'change' }],
|
|
lang: [{ required: true, message: this.$t('mailboxMouldDetail.rulesLanguage'), trigger: 'change' }],
|
|
title: [{ required: true, message: this.$t('mailboxMouldDetail.rulesTitle'), trigger: 'blur' }],
|
|
version: [{ required: true, message: this.$t('mailboxMouldDetail.rulesVersion'), trigger: 'blur' }]
|
|
},
|
|
form: {
|
|
journalId: '',
|
|
scene: 'invite_submission',
|
|
lang: 'en',
|
|
version: '1.0.0',
|
|
title: '',
|
|
subject: '',
|
|
body: '',
|
|
variables: '',
|
|
is_active: 1
|
|
}
|
|
};
|
|
},
|
|
computed: {
|
|
isEditMode() {
|
|
const q = this.$route && this.$route.query ? this.$route.query : {};
|
|
return !!(q.template_id || q.id);
|
|
}
|
|
},
|
|
created() {
|
|
this.loadJournals();
|
|
},
|
|
beforeDestroy() {
|
|
if (this.saveDebounceTimer) {
|
|
clearTimeout(this.saveDebounceTimer);
|
|
this.saveDebounceTimer = null;
|
|
}
|
|
},
|
|
methods: {
|
|
loadJournals() {
|
|
this.journalLoading = true;
|
|
const q = this.$route && this.$route.query ? this.$route.query : {};
|
|
const fromRouteJournalId = q.journal_id || q.journalId || '';
|
|
this.$api
|
|
.post(API.getAllJournal, { username: localStorage.getItem('U_name') })
|
|
.then(res => {
|
|
const list = res || [];
|
|
const mapped = (Array.isArray(list) ? list : []).map(j => ({
|
|
journal_id: j.journal_id || j.id,
|
|
title: j.title || j.name || ''
|
|
}));
|
|
this.journalList = mapped;
|
|
if (fromRouteJournalId) {
|
|
const has = mapped.some(j => String(j.journal_id) === String(fromRouteJournalId));
|
|
if (has) this.form.journalId = String(fromRouteJournalId);
|
|
}
|
|
if (!this.form.journalId && mapped.length > 0) this.form.journalId = String(mapped[0].journal_id);
|
|
})
|
|
.catch(() => {
|
|
this.journalList = [];
|
|
})
|
|
.then(() => {
|
|
const q = this.$route && this.$route.query ? this.$route.query : {};
|
|
const templateId = q.template_id || q.id || '';
|
|
if (templateId) {
|
|
return this.loadTemplate(String(templateId));
|
|
}
|
|
return null;
|
|
})
|
|
.finally(() => {
|
|
this.journalLoading = false;
|
|
});
|
|
},
|
|
loadTemplate(templateId) {
|
|
return this.$api.post(API.getTemplate, { template_id: String(templateId) }).then(res => {
|
|
if (!res || res.code !== 0) return;
|
|
const data = (res && res.data) || {};
|
|
const t = data.template || data.detail || data || {};
|
|
if (t.journal_id != null) this.form.journalId = String(t.journal_id);
|
|
if (t.scene != null) this.form.scene = String(t.scene);
|
|
if (t.language != null) this.form.lang = String(t.language).toLowerCase();
|
|
if (t.title != null) this.form.title = String(t.title);
|
|
if (t.subject != null) this.form.subject = String(t.subject);
|
|
if (t.variables_json != null) this.form.variables = String(t.variables_json);
|
|
if (t.version != null) this.form.version = String(t.version);
|
|
if (t.is_active != null) this.form.is_active = Number(t.is_active) === 1 ? 1 : 0;
|
|
const bodyHtml = t.body_html != null ? t.body_html : (t.body != null ? t.body : '');
|
|
this.form.body = String(bodyHtml || '');
|
|
});
|
|
},
|
|
goBack() {
|
|
const q = (this.$route && this.$route.query) || {};
|
|
if (q.from_auto_promotion === '1') {
|
|
this.$router.push({ path: '/autoPromotion' });
|
|
return;
|
|
}
|
|
this.$router.push({ path: '/mailboxMould' });
|
|
},
|
|
handleSave() {
|
|
// 若已在保存中,直接忽略重复点击
|
|
if (this.saveLoading) return;
|
|
|
|
// 防抖:连续点击只触发最后一次
|
|
if (this.saveDebounceTimer) {
|
|
clearTimeout(this.saveDebounceTimer);
|
|
}
|
|
|
|
this.saveDebounceTimer = setTimeout(() => {
|
|
this.saveDebounceTimer = null;
|
|
this._doSave();
|
|
}, 600);
|
|
}
|
|
,
|
|
async _doSave() {
|
|
const formRef = this.$refs.detailForm;
|
|
const validatePromise =
|
|
formRef && formRef.validate
|
|
? new Promise(resolve => formRef.validate(ok => resolve(ok)))
|
|
: Promise.resolve(true);
|
|
|
|
const ok = await validatePromise;
|
|
if (!ok) return;
|
|
|
|
if (!this.form.subject) {
|
|
this.$message.warning(this.$t('mailboxMouldDetail.rulesSubject'));
|
|
return;
|
|
}
|
|
if (!this.form.body) {
|
|
this.$message.warning(this.$t('mailboxMouldDetail.rulesBody'));
|
|
return;
|
|
}
|
|
|
|
this.saveLoading = true;
|
|
try {
|
|
const q = this.$route.query;
|
|
const templateId = q.template_id || q.id || '';
|
|
const bodyHtml = this.form.body || '';
|
|
const bodyText = bodyHtml.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
|
|
const params = {
|
|
journal_id: String(this.form.journalId || ''),
|
|
scene: String(this.form.scene || 'invite_submission'),
|
|
language: String(this.form.lang || 'en'),
|
|
title: String(this.form.title || ''),
|
|
subject: String(this.form.subject || ''),
|
|
body_html: bodyHtml,
|
|
body_text: bodyText || '',
|
|
variables_json: String(this.form.variables || ''),
|
|
version: String(this.form.version || '1.0.0'),
|
|
is_active: this.form.is_active === 1 ? '1' : '0'
|
|
};
|
|
|
|
if (templateId) params.template_id = String(templateId);
|
|
|
|
const res = await this.$api.post(API.saveTemplate, params);
|
|
if (res && res.code === 0) {
|
|
this.$message.success(this.$t('mailboxMouldDetail.saveSuccess'));
|
|
this.goBack();
|
|
} else {
|
|
this.$message.error((res && res.msg) || this.$t('mailboxMouldDetail.saveFail'));
|
|
}
|
|
} catch (e) {
|
|
this.$message.error(this.$t('mailboxMouldDetail.saveFail'));
|
|
} finally {
|
|
this.saveLoading = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 容器锁定 */
|
|
.detail-container { height: 100vh; display: flex; flex-direction: column; background: #f0f2f5; overflow: hidden; }
|
|
|
|
/* 顶部栏高度压缩 */
|
|
.action-bar {
|
|
height: 40px;
|
|
background: #fff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 15px;
|
|
border-bottom: 1px solid #dcdfe6;
|
|
flex-shrink: 0;
|
|
}
|
|
.action-bar .left { display: flex; align-items: center; min-width: 0; }
|
|
.page-title { font-size: 13px; font-weight: bold; color: #333; }
|
|
.page-subject {
|
|
margin-left: 10px;
|
|
font-size: 12px;
|
|
color: #666;
|
|
max-width: 520px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.editor-layout { flex: 1; display: flex; overflow: hidden; background: #f0f2f5; }
|
|
|
|
/* 左侧边栏 - 占比 2 (约 20%-25%) */
|
|
.config-aside {
|
|
width: 280px;
|
|
background: #fff;
|
|
border-right: 1px solid #dcdfe6;
|
|
padding: 15px;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.section-title { font-size: 12px; font-weight: bold; color: #409EFF; margin-bottom: 12px; display: flex; align-items: center; gap: 5px; }
|
|
.el-divider--horizontal { margin: 15px 0; }
|
|
|
|
/* 右侧编辑区 - 占比 8 */
|
|
.main-editor {
|
|
flex: 1;
|
|
padding: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
.editor-card {
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: none;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
|
}
|
|
.editor-card /deep/ .el-card__body {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 0 !important;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 邮件主题行优化 */
|
|
.subject-input-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px 15px;
|
|
background: #fafafa;
|
|
border-bottom: 1px solid #eee;
|
|
gap: 10px;
|
|
}
|
|
.subject-label { font-size: 12px; font-weight: bold; color: #666; white-space: nowrap; }
|
|
.subject-inner-input{
|
|
border: 1px solid #dcdfe6;
|
|
border-radius: 4px;
|
|
|
|
}
|
|
.subject-inner-input /deep/ .el-input__inner { border: transparent; background: transparent; font-weight: 500; font-size: 14px; }
|
|
.subject-inner-input /deep/ .el-input__inner:focus { background: #fff; border-color: #dcdfe6; }
|
|
|
|
/* 编辑器容器撑满 */
|
|
.body-editor-container { flex: 1; overflow: hidden; padding: 10px 15px; }
|
|
.body-editor-container /deep/ .ck-editor { height: 100%; display: flex; flex-direction: column; }
|
|
.body-editor-container /deep/ .ck-editor__main { flex: 1; overflow: auto; }
|
|
|
|
/* 状态切换栏 */
|
|
.status-box {
|
|
margin-top: auto;
|
|
padding-top: 15px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
font-size: 12px;
|
|
color: #666;
|
|
}
|
|
|
|
/* 黑色背景 JSON 输入框 */
|
|
.dark-json-input /deep/ .el-textarea__inner {
|
|
background: #1e1e1e;
|
|
color: #9cdcfe;
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
border: 1px solid #333;
|
|
}
|
|
|
|
/* 滚动条 */
|
|
.scroll-panel { overflow-y: auto; }
|
|
.scroll-panel::-webkit-scrollbar { width: 4px; }
|
|
.scroll-panel::-webkit-scrollbar-thumb { background: #ccc; border-radius: 2px; }
|
|
|
|
/* 表单紧凑微调 */
|
|
.detail-container /deep/ .el-form-item--mini.el-form-item { margin-bottom: 12px; }
|
|
.detail-container /deep/ .el-form--label-top .el-form-item__label { padding: 0 0 4px; font-size: 12px; color: #999; }
|
|
</style> |